Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325
Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325
Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325
Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325
Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325
Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325
Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325
#!/usr/local/cpanel/3rdparty/bin/perl
# cpanel - scripts/ea-tomcat85 Copyright 2018 cPanel, Inc.
# All rights Reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
use strict;
use warnings;
package scripts::ea_tomcat85;
use Cpanel::Config::LoadUserDomains ();
use Cpanel::AccessIds ();
use Cpanel::JSON ();
use File::Path::Tiny (); # Path::Tiny’s version is overly complicated
use Path::Tiny;
use Capture::Tiny 'capture';
use XML::LibXML ();
use Cwd ();
use App::CmdDispatch;
use Path::Iter ();
use Cpanel::SafeDir::Read ();
my $cmds = {
add => {
code => \&add,
clue => 'add ',
abstract => 'add tomcat 8.5 support',
help => "add tomcat 8.5 instance to the given user.",
},
rem => {
code => \&rem,
clue => 'rem ',
abstract => 'remove tomcat 8.5 support',
help => "Remove tomcat 8.5 instance from the given user.\n\tSince this is destructive you must also verify you want to continue. Passing --verify= will make it work. If you don’t pass it an explanatory message is given with instructions.",
},
list => {
code => sub {
_bail( $_[0], "“list” does not take any arguments" ) if @_ > 1;
for my $user ( _get_tomcat85_users() ) {
print "$user\n";
}
},
clue => 'list',
abstract => 'list users with tomcat 8.5 instance',
help => 'List users, one per line, that have a tomcat 8.5 instance.',
},
all => {
code => sub {
my ( $app, $operation ) = @_;
if ( !defined $operation || ( $operation ne "stop" && $operation ne "restart" ) ) {
_bail( $_[0], "“all” requires an additional argument, either “stop” or “restart”" );
}
for my $user ( _get_tomcat85_users() ) {
print "Starting $user …\n";
my $homedir = _get_homedir($user);
Cpanel::AccessIds::do_as_user_with_exception(
$user,
sub {
local $ENV{HOME} = $homedir;
my $orig_umask = umask(0027);
_ubic_tc($operation);
umask($orig_umask);
}
);
print " … done ($user).\n";
}
},
clue => 'all ',
abstract => 'Stop/restart all tomcat 8.5 instances',
help => 'Stop or restart all users’s tomcat 8.5 instances.',
},
};
my $hint_blurb = "This tool supports the following commands (i.e. $0 {command} …):";
my $opts = {
'help:pre_hint' => $hint_blurb,
'help:pre_help' => "Various tomcat 8.5 related admin utilities\n\n$hint_blurb",
default_commands => "help",
alias => { remove => "rem" },
};
run(@ARGV) if !caller;
sub run {
my (@argv) = @_;
die "This script should only be called as root\n" if $> != 0;
local $ENV{TERM} = $ENV{TERM} || "xterm-256color"; # non-CLI modulino avoid needless: Cannot find termcap: TERM not set at …/Term/ReadLine.pm line 373.
App::CmdDispatch->new( $cmds, $opts )->run(@argv);
}
################
#### commands ##
################
sub add {
my ( $app, $user, $homedir ) = _process_args(@_);
_bail( $app, "The user already has a tomcat 8.5 instance." ) if -d "$homedir/ea-tomcat85" && !_tomcat85_dir_is_empty_or_apps_only("$homedir/ea-tomcat85");
print "Adding $user’s tomcat 8.5 instance …\n";
my ( $http_port, $ajp_port ) = _get_ports($user);
my $curdir = Cwd::cwd();
Cpanel::AccessIds::do_as_user_with_exception(
$user,
sub {
local $ENV{HOME} = $homedir;
my $orig_umask = umask(0027);
mkdir "$homedir/ea-tomcat85"; # it may exist so don’t check it’s RV
die "Could not create directory “$homedir/ea-tomcat85”: $!\n" if !-d "$homedir/ea-tomcat85";
chdir "$homedir/ea-tomcat85" or die "Could not change into “$homedir/ea-tomcat85”: $!\n";
for my $dir (qw(conf bin logs run temp webapps/ROOT work/Catalina/localhost/ROOT)) {
File::Path::Tiny::mk($dir) or die "Could not create directory “$dir”: $!\n";
}
my %symlinks = (
'/opt/cpanel/ea-tomcat85/README.USER-SERVICE-MANAGEMENT' => 'README.USER-SERVICE-MANAGEMENT',
'/opt/cpanel/ea-tomcat85/README.FASTERSTARTUP' => 'README.FASTERSTARTUP',
'/opt/cpanel/ea-tomcat85/README.SECURITY' => 'README.SECURITY',
'/opt/cpanel/ea-tomcat85/README.APACHE-PROXY' => 'README.APACHE-PROXY',
'/opt/cpanel/ea-tomcat85/README.USER-INSTANCE' => 'README.USER-INSTANCE',
);
for my $target ( keys %symlinks ) {
symlink( $target, $symlinks{$target} ) or warn "Could not symlink “$symlinks{$target}” to “$target”: $!\n";
}
my $data_start = tell DATA;
path("bin/setenv.sh")->spew();
seek( DATA, $data_start, 0 );
system("cp -r /opt/cpanel/ea-tomcat85/user-conf/* conf/");
system("chmod 640 conf/*");
my $dom = XML::LibXML->load_xml( location => "conf/server.xml", load_ext_dtd => 0, ext_ent_handler => sub { } );
$dom->findnodes('//Server[@shutdown="SHUTDOWN"]')->shift()->setAttribute( port => -1 ); # disable the shutdown port
my $ajp_connector_exists = 0;
for my $conn ( $dom->findnodes("//Server/Service/Connector") ) {
# hide version exposure
# Could set `server` in addition to xpoweredBy but:
# 1. it is a generic value for tomcat 4.1-8.5 so all an attacker can gelan is that it is tomcat
# 2. if we set it to something else it will be a known value and they will be able to
# determine not only that its tomcat but also the version
$conn->setAttribute( xpoweredBy => "false" );
# set the ports to the user’s ports
if ( $conn->getAttribute('protocol') eq 'HTTP/1.1' ) {
$conn->setAttribute( port => $http_port );
$conn->removeAttribute('redirectPort');
}
elsif ( $conn->getAttribute('protocol') eq 'AJP/1.3' ) {
$conn->setAttribute( port => $ajp_port );
$conn->removeAttribute('redirectPort');
$conn->setAttribute( secretRequired => "false" );
$ajp_connector_exists = 1;
}
}
if ( !$ajp_connector_exists ) {
# Create
my $ajp = $dom->createElement('Connector');
$ajp->setAttribute( port => $ajp_port );
$ajp->setAttribute( protocol => 'AJP/1.3' );
$ajp->setAttribute( xpoweredBy => "false" );
$ajp->setAttribute( secretRequired => "false" );
$dom->findnodes('//Server/Service[@name="Catalina"]')->shift()->addChild($ajp);
}
my $valve = $dom->createElement("Valve");
$valve->setAttribute( className => "org.apache.catalina.valves.ErrorReportValve" );
$valve->setAttribute( showReport => "false" );
$valve->setAttribute( showServerInfo => "false" );
for my $host ( $dom->findnodes("//Host") ) {
# hide version exposure: Create a ErrorReportValve w/ showServerInfo and showReport attributes to false
$host->addChild($valve);
# Host – autoDeploy, deployOnStartup, unpackWARs, and deployXML false
# - FWiW, ASF does exploded deploys instead of ^^^
$host->setAttribute( autoDeploy => "false" );
$host->setAttribute( deployOnStartup => "false" );
$host->setAttribute( deployXML => "false" );
$host->setAttribute( unpackWARs => "false" );
}
_write_dom( "conf/server.xml" => $dom );
_sysdie( "add service management" => ( qw(/usr/local/cpanel/scripts/cpuser_service_manager add ea-tomcat85), "--init-script=/opt/cpanel/ea-tomcat85/bin/user-init.sh" ) );
_ubic_tc("start");
umask($orig_umask);
}
);
chdir $curdir or warn "Could not chdir back to “$curdir”: $!\n";
print " … done!\n";
return;
}
sub rem {
my ( $app, $user, $homedir ) = _process_args(@_);
_bail( $app, "The user does not have a tomcat 8.5 instance." ) if !-d "$homedir/ea-tomcat85" || _tomcat85_dir_is_empty_or_apps_only("$homedir/ea-tomcat85");
print "Removing $user’s tomcat 8.5 instance …\n";
if ( !grep m/^--verify=\Q$user\E$/, @_ ) {
die "This operation can not be undone, please pass the --verify= to indicate you’ve backed up anything in $homedir/ea-tomcat85/ that you want to keep!\n";
}
my $curdir = Cwd::cwd();
Cpanel::AccessIds::do_as_user_with_exception(
$user,
sub {
local $ENV{HOME} = $homedir;
chdir $homedir or die "Could not change into “$homedir”: $!\n";
my $orig_umask = umask(0027);
_ubic_tc("stop");
_sysdie( "remove service management" => qw(/usr/local/cpanel/scripts/cpuser_service_manager rem ea-tomcat85) );
# Can’t just File::Path::Tiny::rm("$homedir/ea-tomcat85")
# because we want to keep these if they are not empty
rmdir("$homedir/ea-tomcat85/webapps/ROOT");
rmdir("$homedir/ea-tomcat85/webapps");
my $fetch = Path::Iter::get_iterator("$homedir/ea-tomcat85");
while ( my $path = $fetch->() ) {
next
if $path eq "$homedir/ea-tomcat85"
|| $path =~ m{^\Q$homedir\E/ea-tomcat85/webapps(:?/|)};
-l $path || -f _ ? unlink($path) : File::Path::Tiny::rm($path);
}
rmdir("$homedir/ea-tomcat85"); # if its not empty (webapps) then this fails, that is what we want
umask($orig_umask);
}
);
chdir $curdir or warn "Could not chdir back to “$curdir”: $!\n";
print " … done!\n";
return;
}
###############
#### helpers ##
###############
sub _ubic_tc {
my ($cmd) = @_;
# would be cool if Cpanel::FindBin (or whatever) did this for us: CPANEL-22345
my $real_perl = readlink("/usr/local/cpanel/3rdparty/bin/perl");
my $cp_bin_dir = $real_perl;
$cp_bin_dir =~ s{/perl$}{};
local $ENV{PATH} = "$cp_bin_dir:$ENV{PATH}"; # not only does this allow it to find our ubic-admin, it allows its env-shebang to pick up our perl
# This is necessary to reflect reality under start/restart since Cpanel::AccessIds does not reset ENV
# - HOME is already set because other callers need it also
local $ENV{USER} = ( getpwuid($>) )[0];
system( "ubic", $cmd, "ea-tomcat85" );
return $? ? 0 : 1;
}
sub _sysdie {
my ( $do_this, @cmd ) = @_;
my $cmd_str = join( " ", @cmd );
system(@cmd) && die "Failed to $do_this (exit $?)\nThis will need done manually:\n\t`$cmd_str`\n";
return;
}
sub _bail {
my ( $app, $msg ) = @_;
chomp($msg);
die "$msg\n" if $ENV{"scripts::ea_tomcat85::bail_die"};
warn "$msg\n";
$app->help();
exit(1); # there is no return()ing from this lol
}
sub _process_args {
my ( $app, $user ) = @_;
_bail( $app, "User argument is missing." ) if !$user;
_bail( $app, "User argument is invalid." ) if !exists { Cpanel::Config::LoadUserDomains::loaduserdomains( undef, 0, 1 ) }->{$user};
return ( $app, $user, _get_homedir($user) );
}
sub _get_homedir {
my ($user) = @_;
return ( getpwnam($user) )[7];
}
sub _get_ports {
my ($user) = @_;
my ( $http_port, $ajp_port );
my $port_count = 2;
_load_modulino("/usr/local/cpanel/scripts/cpuser_port_authority");
# see if they have them already so that we don’t keep allocating them
if ( -f $scripts::cpuser_port_authority::port_authority_conf ) {
my @ports;
my $hr = eval { Cpanel::JSON::LoadFile($scripts::cpuser_port_authority::port_authority_conf) } || {};
for my $port ( keys %{$hr} ) {
next if $hr->{$port}{owner} ne $user || !exists $hr->{$port}{service} || $hr->{$port}{service} ne 'ea-tomcat85';
push @ports, $port;
}
if ( @ports >= $port_count ) {
( $http_port, $ajp_port ) = sort @ports;
}
}
if ( !$http_port || !$ajp_port ) {
my @cmd = ( qw(/usr/local/cpanel/scripts/cpuser_port_authority give), $user, $port_count, '--service=ea-tomcat85' );
my ( $stdout, $stderr, $exit ) = capture { system(@cmd) }; # we could scripts::cpuser_port_authority::run("give", … but its nice to output the command, and the exit value is nice to detect success/failure
if ($exit) {
my $cmd = join( " ", @cmd );
$stderr =~ s{\[\?1034h}{};
die "$stderr\nFailed to allocate ports!\n`$cmd` exited $exit\n";
}
else {
my @lines = split( /\n/, $stdout );
chomp(@lines);
( $http_port, $ajp_port ) = sort grep { m/^[1-9][0-9]+$/ } @lines;
}
}
if ( !$http_port || !$ajp_port ) {
die "Failed to get ports ($port_count)!\n";
}
return ( $http_port, $ajp_port );
}
sub _load_modulino {
my ($modulino) = @_;
eval { require "$modulino" };
die "This feature is not available on this version of cPanel. You need v76 or newer\n" if $@;
return 1;
}
sub _write_dom {
my ( $path, $dom ) = @_;
# get nicely indented XML (from http://grantm.github.io/perl-libxml-by-example/dom.html#modifying-the-dom)
for my $node ( $dom->findnodes('//text()') ) {
$node->parentNode->removeChild($node) unless $node =~ /\S/;
}
return path($path)->spew( $dom->toString(1) );
}
sub _get_cmd { $cmds }
sub _get_tomcat85_users {
my @tomcat85_users;
my %cpusers = Cpanel::Config::LoadUserDomains::loaduserdomains( undef, 0, 1 );
for my $user ( sort keys %cpusers ) {
my $homedir = _get_homedir($user);
push @tomcat85_users, $user if -d "$homedir/ea-tomcat85" && !_tomcat85_dir_is_empty_or_apps_only("$homedir/ea-tomcat85");
}
return @tomcat85_users;
}
sub _tomcat85_dir_is_empty_or_apps_only {
my ($tomcat85_dir) = @_;
my @contents = Cpanel::SafeDir::Read::read_dir($tomcat85_dir);
return 1 if !@contents || ( -d "$tomcat85_dir/webapps" && @contents == 1 );
return;
}
1;
__DATA__
# Your customizations can go here, for example, CATALINA_OPTS
# example from https://go.cpanel.net/TomcatFastStart
# Trade some security for startup speed by using non-blocking entropy:
# CATALINA_OPTS="$CATALINA_OPTS -Djava.security.egd=file:/dev/./urandom"
# DO NOT EDIT THIS LINE OR ANYTHING BELOW THIS LINE
. /opt/cpanel/ea-tomcat85/bin/user-setenv.sh