D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
scripts
/
Filename :
modify_featurelist
back
Copy
#!/usr/local/cpanel/3rdparty/bin/perl # Copyright 2025 WebPros International, LLC # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited. package scripts::modify_featurelist; use cPstrict; =encoding utf-8 =head1 NAME modify_featurelist =head1 USAGE modify_featurelist --config <path to config> | --flag <run-once-flag> --feature <feature-name> [--enable | --disable] [[ --list <feature-list-name> | --all ] [ --rule <path to rule file> ] | --help] =head1 DESCRIPTION This script modifies feature lists to enable or disable a certain feature. The script will only run once per server using the provided flag as a lock to decide if the code should run. When you pass the C<--config> it should be to a JSON with Comments (jsonc) file that contains the following keys: =over =item flag - string - the run once flag =item condition - the component rules to evalute. This is optional. Note: Conditions can only be provided in the config file. When a condition is provided the feature list changes will only be made if the condition evalutes truthfully on compared to the local server state for things like cpanel version, company id, etc. Any available rules can be provided in this section. =item all - boolean - run on all feature lists except the disabled list. You use include either the `all` attribute or the `list` attribute. =item list - array - the feature lists to modify. You use include either the `all` attribute or the `list` attribute. =item feature - string - cpanel feature name =item enable - boolean - true to enable by default, false to disable by default =back B<NOTE:> Any items provided directly by command line parameters will override the values in the config file. When C<--all> is provided, the feature will be added to all feature lists. This will not add it to the `disabled` feature list. When one or more C<--list> arguments are provided, the feature will be added to the specified feature lists. You must provide either C<--config> or the folowing: C<--flag>, C<--feature>, and either C<--enable> or C<--disable>. All other arguments are optional. =head1 METHODS =cut use Cpanel::ConfigFiles (); use Cpanel::Features::Load (); use Cpanel::Features::Write (); use Cpanel::FileUtils::TouchFile (); use Cpanel::Version (); use Cpanel::Version::Compare (); use Cpanel::Imports; use parent qw( Cpanel::HelpfulScript ); use constant _OPTIONS => ( 'config=s', 'feature=s', 'list=s@', 'all', 'enable', 'disable', 'flag=s', 'verbose!' ); our $VERSION_DIR = "/var/cpanel/version"; use constant PLUGIN_DIR => '/var/cpanel/plugins/'; __PACKAGE__->new(@ARGV)->run() if !caller; sub _verify_directories () { if ( !-e $Cpanel::ConfigFiles::FEATURES_DIR ) { mkdir( $Cpanel::ConfigFiles::FEATURES_DIR, 0755 ) or do { logger()->warn("Unable to create feature directory '$Cpanel::ConfigFiles::FEATURES_DIR': $!"); return; }; } if ( !-e $VERSION_DIR ) { mkdir( $VERSION_DIR, 0755 ) or do { logger()->warn("Unable to create run lock directory '$VERSION_DIR': $!"); return; }; } return 1; } sub get_featurelists() { opendir( my $dh, $Cpanel::ConfigFiles::FEATURES_DIR ) || do { logger()->warn("Cannot open directory: $Cpanel::ConfigFiles::FEATURES_DIR."); return; }; my @feature_lists = grep { !/^(\.\.?|disabled)$/n } readdir($dh); closedir($dh) or die "Failed to retrieve the featurelist: $!"; return @feature_lists; } =head2 modify_feature_for_all_feature_lists($feature, $value, $verbose) Adds a feature and value to all feature lists. =head3 ARGUMENTS =over =item * $feature_list - The feature list to modify. =item * $feature - The feature to add. =item * $value - 1 to enable the feature, 0 to disable the feature. =back =cut sub modify_feature_for_all_feature_lists ( $feature, $value, $verbose ) { my @feature_lists = get_featurelists(); my $mail_only_flag = PLUGIN_DIR . "$feature/mail_only"; foreach my $feature_list (@feature_lists) { if ( $feature_list eq 'Mail Only' && $value && !-e $mail_only_flag ) { say "Disabling '$feature' for '$feature_list' featurelist." if $verbose; modify_featurelist( $feature_list, $feature, 0, $verbose ); next; } modify_featurelist( $feature_list, $feature, $value, $verbose ); } return 1; } =head2 modify_featurelist($feature_list, $feature, $value, $verbose) Initialize the feature in the feature list with the provided default value. =head3 ARGUMENTS =over =item * $feature_list - The feature list to modify. =item * $feature - The feature to add. =item * $value - 1 to enable the feature, 0 to disable the feature. =back =cut sub modify_featurelist ( $feature_list, $feature, $value, $verbose ) { say "Attempting to modify: $feature_list" if $verbose; my $features = eval { Cpanel::Features::Load::load_featurelist($feature_list) }; if ( my $error = $@ ) { logger()->warn("Unable to add default feature entry $feature=$value: $@"); } $features->{$feature} = $value; Cpanel::Features::Write::write_featurelist( $feature_list, $features ); say "Feature $feature added to feature list $feature_list with default of $value" if $verbose; return 1; } =head2 do_once() Creates a touch file to track whether a task has been done. The task is executed and as long as the touch file exists, it will not do it again. =cut sub do_once (%opts) { return unless $opts{version} && $opts{code} && ref $opts{code} eq 'CODE'; my $lock = _lock_name(%opts); return if -e $lock; _mark_did_once(%opts); my $ret = eval { $opts{code}->(); }; warn($@) if $@; return $ret; } sub _mark_did_once (%opts) { my $lock = _lock_name(%opts); if ( !Cpanel::FileUtils::TouchFile::touchfile($lock) ) { warn("Failed to touch cpanel $opts{version} version file"); } return 1; } sub _lock_name (%opts) { return $VERSION_DIR . '/cpanel' . $opts{version}; } sub load_config_file ($file) { require Common::JSONC; return Common::JSONC::load_jsonc($file); } =head2 I<OBJ>->run() Runs this script. =cut sub run ($self) { my $config = {}; my $config_path = $self->getopt('config'); if ($config_path) { $config = load_config_file($config_path); } $config->{verbose} = $self->getopt('verbose') // !!0; $config->{flag} //= $self->getopt('flag') || die $self->full_help(); $config->{feature} //= $self->getopt('feature') || die $self->full_help(); # only allow one of these arguments if ( $self->getopt('enable') && $self->getopt('disable') ) { say "ERROR: Only one of --enable or --disable flags may be passed."; die $self->full_help(); } # fixed json deserialize issue $config->{enable} = !!1 if $config->{enable} && $config->{enable} eq 'true'; $config->{enable} = !!0 if $config->{enable} && $config->{enable} eq 'false'; # overwrite if set by command line $config->{enable} = !!1 if $self->getopt('enable'); $config->{enable} = !!0 if $self->getopt('disable'); die $self->full_help() if !defined $config->{enable}; # fixed json deserialize issue $config->{all} = !!1 if $config->{all} && $config->{all} eq 'true'; $config->{all} = !!0 if $config->{all} && $config->{all} eq 'false'; # overwrite if set by command line $config->{all} = !!1 if $self->getopt('all'); $config->{list} = $self->getopt('list') if $self->getopt('list') && $self->getopt('list')->@*; die $self->full_help() if !$config->{all} && ( !$config->{list} || ref $config->{list} ne 'ARRAY' || !$config->{list}->@* ); my $make_changes = !!0; if ( $config->{condition} ) { require Cpanel::Plugins::Components::Rules; my $rules_obj = Cpanel::Plugins::Components::Rules->new( 'config' => $config->{condition} ); $make_changes = $rules_obj->is_allowed(); } else { $make_changes = !!1; say "No condition provided, proceeding with feature list modification" if $config->{verbose}; } if ( !$make_changes ) { logger()->info("Skipping feature list modification as per the conditional rule passed"); say "Skipping feature list modification as per the conditional rule that didn't match." if $config->{verbose}; return 0; } _verify_directories(); do_once( 'version' => $config->{flag}, 'eol' => 'never', 'code' => sub { my $verbose = $config->{verbose}; say "Running feature list modification for $config->{feature} with enable set to $config->{enable}"; if ( $config->{all} ) { modify_feature_for_all_feature_lists( $config->{feature}, $config->{enable}, $verbose ); } else { foreach my $feature_list ( $config->{list}->@* ) { modify_featurelist( $feature_list, $config->{feature}, $config->{enable}, $verbose ); } } }, ); } 1;