#!/usr/local/cpanel/3rdparty/bin/perl # cpanel - ea_sync_user_phpini_settings Copyright 2017 cPanel, Inc. # All rights Reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited package ea_sync_user_phpini_settings; use strict; use Cpanel::Config::Httpd (); use Getopt::Param::Tiny (); use Cpanel::PwCache (); use Cpanel::AccessIds (); use Cwd (); use Path::Iter (); use Cpanel::ProgLang (); use Cpanel::ArrayFunc::Uniq (); use Cpanel::Version (); #### barf ## # paths before ULC in INC can cause the user-run-code to error out with things like: # Can't locate Cpanel/Exception/CORE.pm: /root/perl5/lib/perl5//x86_64-linux-64int/Cpanel/Exception/CORE.pm: Permission denied at /usr/local/cpanel/Cpanel/Exception.pm line 43. # Can't locate Cpanel/Carp.pm: /root/perl5/lib/perl5//x86_64-linux-64int/Cpanel/Carp.pm: Permission denied at (eval .*) line 1. # etc, on and on down the rabbit hole depending on how you try to hack around it while ( $INC[0] ne '/usr/local/cpanel' ) { shift @INC } #### /barf ## our ( $php_version_info, $php, $ini_hr ); exit( run(@ARGV) ) unless caller; sub run { my (@args) = @_; die "This script can only be run by root\n" if $> != 0; die "This script only operates when you are under EasyApache 4\n" if !Cpanel::Config::Httpd::is_ea4(); # Handle v58 grossfully (not a typo) eval 'require Cpanel::PHP::Config;'; if ( $@ || !defined &Cpanel::Version::get_short_release_number || Cpanel::Version::get_short_release_number() < 62 ) { print "Nothing to do (only applies to v64 and newer)\n"; exit(0); } my $param = Getopt::Param::Tiny->new( { array_ref => \@args, help_coderef => \&_help, known_only => [ 'user', 'all-users' ], no_args_help => 1, validate => \&_validate, } ); my $starting_dir = Cwd::cwd(); $php_version_info = Cpanel::PHP::Config::get_php_version_info(); die "There are no PHP packages installed via ea4\n" if !@{ $php_version_info->{versions} }; $php = Cpanel::ProgLang->new( type => 'php' ); $ini_hr = {}; my @users; if ( $param->param('all-users') ) { require Cpanel::Config::LoadUserDomains; require Cpanel::PwCache; Cpanel::PwCache::Build::init_passwdless_pwcache(); my %user_map = Cpanel::Config::LoadUserDomains::loaduserdomains( undef, 0, 1 ); @users = sort keys %user_map; } else { @users = $param->param('user'); } for my $user (@users) { print "Operating on “$user” …\n"; _process_user($user); print " … done!\n"; } chdir($starting_dir) or die "Could not chdir back to $starting_dir: $!\n"; return 0; # exit clean } ############### #### helpers ## ############### sub _process_user { my ($user) = @_; my $user_ar = [ Cpanel::PwCache::getpwnam($user) ]; my $user_homedir = $user_ar->[7]; my $user_php = Cpanel::PHP::Config::get_php_config_for_users( [$user] ); my $user_dir_to_package_map = {}; for my $dom ( sort keys %{$user_php} ) { next if exists $user_dir_to_package_map->{ $user_php->{$dom}{documentroot} }; $user_dir_to_package_map->{ $user_php->{$dom}{documentroot} } = $user_php->{$dom}{phpversion}; } my $count = Cpanel::AccessIds::do_as_user( $user, sub { return _proc_dir( $user_homedir, $user_dir_to_package_map ) } ); if ( !$count->{errors} && !$count->{processed} ) { print "\tNo php.ini files found.\n"; } else { $count->{processed} ||= 0; $count->{errors} ||= 0; print "\tSuccessfully processed php.ini files: $count->{processed}\n"; print "\tphp.ini files that had errors during processing: $count->{errors}\n"; } } sub _proc_dir { my ( $user_homedir, $user_dir_to_package_map ) = @_; my $count; chdir($user_homedir) or die "failed to chdir($user_homedir): $!\n"; my @errors; my $fetch = Path::Iter::get_iterator( '.', { stop_when_opendir_fails => 1, errors => \@errors } ); while ( my $next_path = $fetch->() ) { if ( $next_path =~ m{(?:^|/)php\.ini$} ) { print "\tProcessing $user_homedir/$next_path …\n"; eval { my $path_parent = $next_path; $path_parent =~ s{/?php\.ini$}{}; my $package = $user_dir_to_package_map->{ $path_parent ? "$user_homedir/$path_parent" : $user_homedir } || $php_version_info->{default}; $ini_hr->{$package} ||= $php->get_ini( 'package' => $package ); $ini_hr->{$package}->set_directives( path => $next_path, directives => { _ea_sync_user_phpini_settings => 1 }, userfiles => 1 ); }; if ($@) { warn "ERROR: $@\n" if $@; $count->{errors}++; } else { $count->{processed}++; } print "\t … done!\n"; } } if (@errors) { warn "\tThe following errors occured while traversing “$user_homedir” (some php.ini files may not have been processed):\n"; for my $err (@errors) { warn "\t\t$err->{function}(" . join( ", ", @{ $err->{args} } ) . ") – $err->{error}\n"; } } return $count; } sub _validate { my ($param) = @_; my $ok = 1; # innocnent until proven guilty ;) my @users = $param->param('user'); my $all = $param->param('all-users'); if ( $all && @users ) { warn "You cannot specifiy both --all-users and --user options.\n"; $ok = 0; } if ( grep m/^(?:--user|)$/, @users ) { warn "--user requires a value (--user=)\n"; $ok = 0; } if ( @users > Cpanel::ArrayFunc::Uniq::uniq(@users) ) { warn "Each must be unique!\n"; $ok = 0; } for my $user ( Cpanel::ArrayFunc::Uniq::uniq(@users) ) { next if $user =~ m/^(?:--user|)$/; # already looked for this once if ( !Cpanel::PwCache::getpwnam($user) ) { warn "“$user” is not a user on this system\n"; $ok = 0; } } return 1 if $ok; return; } sub _help { my ($param) = @_; print <<"END_HELP"; Usage: $0 --user= [--user= [--user= …]] | --all-users For given users sync any php.ini files in their home directory to corresponding .user.ini and, on some versions, .htaccess. Options: --help this screen --user= specify the user to operate on, may be given more than once --all-users process all users END_HELP exit( $param->param('help') ? 0 : 1 ); } 1;