#!/usr/local/bin/perl =head1 modify-php-ini.pl Changes PHP variables for some or all domains. This command can be used to change the value of a PHP configuration variable (set in the php.ini file, FPM .conf file and Apache configuration) for one or many virtual servers at once. The servers to update can be selected with the C<--domain> or C<--user> flags, or you can choose to modify them all with the C<--all-domains> option. If your system supports multiple PHP versions, you can limit the changes to the config for a specific version with the C<--php-version> flag followed by a number, like 7.4 or 8.2. The variables to change are set with the C<--ini-name> flag, which can be given multiple times to change more than one variable. The new values are set either with the C<--ini-value> flag (followed by a number or string), or the C<--no-ini-value> flag to completely remove a setting. When updating PHP-FPM configuration, the C<--fpm-admin-value> flag can be used to force a setting to be written as an admin-level directive (C), and the C<--fpm-value> flag can be used to force a regular user-level directive (C). If neither flag is given, the existing directive type is preserved where possible. =cut package virtual_server; if (!$module_name) { $main::no_acl_check++; $ENV{'WEBMIN_CONFIG'} ||= "/etc/webmin"; $ENV{'WEBMIN_VAR'} ||= "/var/webmin"; if ($0 =~ /^(.*)\/[^\/]+$/) { chdir($pwd = $1); } else { chop($pwd = `pwd`); } $0 = "$pwd/modify-php-ini.pl"; require './virtual-server-lib.pl'; $< == 0 || die "modify-php-ini.pl must be run as root"; } &licence_status(); &foreign_require("phpini"); @OLDARGV = @ARGV; &set_all_text_print(); # Parse command-line args while(@ARGV > 0) { my $a = shift(@ARGV); if ($a eq "--domain") { push(@domains, shift(@ARGV)); } elsif ($a eq "--user") { push(@users, shift(@ARGV)); } elsif ($a eq "--all-domains") { $all_doms = 1; } elsif ($a eq "--ini-name") { push(@ini_names, shift(@ARGV)); } elsif ($a eq "--ini-value") { push(@ini_values, shift(@ARGV)); } elsif ($a eq "--no-ini-value") { push(@ini_values, undef); } elsif ($a eq "--fpm-admin-value") { $type = 1; } elsif ($a eq "--fpm-value") { $type = 0; } elsif ($a eq "--php-version") { $php_ver = shift(@ARGV); } elsif ($a eq "--multiline") { $multiline = 1; } elsif ($a eq "--help") { &usage(); } else { &usage("Unknown parameter $a"); } } # Validate parameters @domains || @users || $all_doms || usage("No domains to modify specified"); @ini_names || &usage("The --ini-name parameter must be given at least once"); @ini_names == @ini_values || &usage("The number of names and values must be the same"); !&master_admin() && $type && &usage("--fpm-admin-value is only available to the master administrator"); if (!&master_admin()) { foreach my $name (@ini_names) { $name =~ /^[a-z0-9_\.\-]+$/i || &usage("Invalid PHP configuration setting $name"); } foreach my $value (grep { defined($_) } @ini_values) { $value !~ /[\r\n]/ || &usage("PHP configuration values cannot contain newlines"); } # Never preserve or create an administrator-only FPM value $type = 0; } # Get the domains if (@domains || @users) { @doms = &get_domains_by_names_users(\@domains, \@users, \&usage); } else { @doms = &list_domains(); } @doms = &get_remote_api_domains(\@doms, $all_doms || @users); # Do each domain foreach my $d (@doms) { # Check if we can manage this domain &$first_print("Updating $d->{'dom'} .."); if (!&domain_has_website($d) || $d->{'alias'}) { &$second_print(".. no website enabled"); next; } # Update php.ini files $mode = &get_domain_php_mode($d); if ($mode ne "mod_php") { # Get the ini files @inis = &list_domain_php_inis($d); if ($php_ver) { @inis = grep { $_->[0] == $php_ver } @inis; if (!@inis) { &$second_print(".. no PHP configuration for ". "version $php_ver found"); next; } } # Update settings in each one my ($inifile, $ininame, $inivalue); eval { local $main::error_must_die = 1; foreach $ini (@inis) { $inifile = $ini->[1]; &lock_file($ini->[1]); $conf = &phpini::get_config($ini->[1]); for(my $i=0; $i<@ini_names; $i++) { $ininame = $ini_names[$i]; $inivalue = $ini_values[$i]; &phpini::save_directive( $conf, $ini_names[$i], $ini_values[$i]); } &flush_file_lines($ini->[1], undef, 1); &unlock_file($ini->[1]); } }; if ($@) { &$second_print(".. failed to update \"$inifile\" with ". "setting \"$ininame\" to \"$inivalue\": $@"); next; } } # Update Apache PHP directives $mod_php = 0; if ($d->{'web'} && &get_apache_mod_php_version()) { &obtain_lock_web($d); my @ports; push(@ports, $d->{'web_port'}) if ($d->{'web'}); push(@ports, $d->{'web_sslport'}) if ($d->{'ssl'}); foreach my $port (@ports) { my ($virt, $vconf, $conf) = &get_apache_virtual( $d->{'dom'}, $port); next if (!$virt); # Find currently set PHP variables my @phpv = &apache::find_directive( "php_value", $vconf); for(my $i=0; $i<@ini_names; $i++) { my $found = 0; for(my $j=0; $j<@phpv; $j++) { if ($phpv[$j] =~ /^(\S+)\s/ && $1 eq $ini_names[$i]) { if (defined($ini_values[$i])) { $phpv[$j] = $ini_names[$i]." ".$ini_values[$i]; } else { splice(@phpv, $j, 1); } $found = 1; last; } } if (!$found) { push(@phpv, $ini_names[$i]." ". $ini_values[$i]); } } &apache::save_directive("php_value", \@phpv, $vconf, $conf); &flush_file_lines($virt->{'file'}); &release_lock_web($d); ®ister_post_action(\&restart_apache); $mod_php = 1; } } # Update PHP-FPM pool file settings if ($mode eq "fpm") { for(my $i=0; $i<@ini_names; $i++) { &save_php_fpm_ini_value($d, $ini_names[$i], $ini_values[$i], undef, $type); } } &$second_print(".. updated ",scalar(@ini_names)," variables in ", scalar(@inis)," files", ($mod_php ? " and Apache configuration" : ""). ($mode eq "fpm" ? " and FPM configuration" : "")); } &run_post_actions(); &virtualmin_api_log(\@OLDARGV); sub usage { print "$_[0]\n\n" if ($_[0]); print "Changes PHP variables for some or all domains.\n"; print "\n"; print "virtualmin modify-php-ini --domain name | --user name | --all-domains\n"; print " [--php-version number]\n"; print " <--ini-name name>+ <--ini-value value>+\n"; exit(1); }