113498266Sopenharmony_ci#*************************************************************************** 213498266Sopenharmony_ci# _ _ ____ _ 313498266Sopenharmony_ci# Project ___| | | | _ \| | 413498266Sopenharmony_ci# / __| | | | |_) | | 513498266Sopenharmony_ci# | (__| |_| | _ <| |___ 613498266Sopenharmony_ci# \___|\___/|_| \_\_____| 713498266Sopenharmony_ci# 813498266Sopenharmony_ci# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci# 1013498266Sopenharmony_ci# This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci# you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci# are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci# 1413498266Sopenharmony_ci# You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci# copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci# furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci# 1813498266Sopenharmony_ci# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci# KIND, either express or implied. 2013498266Sopenharmony_ci# 2113498266Sopenharmony_ci# SPDX-License-Identifier: curl 2213498266Sopenharmony_ci# 2313498266Sopenharmony_ci########################################################################### 2413498266Sopenharmony_ci 2513498266Sopenharmony_cipackage processhelp; 2613498266Sopenharmony_ci 2713498266Sopenharmony_ciuse strict; 2813498266Sopenharmony_ciuse warnings; 2913498266Sopenharmony_ci 3013498266Sopenharmony_ciBEGIN { 3113498266Sopenharmony_ci use base qw(Exporter); 3213498266Sopenharmony_ci 3313498266Sopenharmony_ci our @EXPORT = qw( 3413498266Sopenharmony_ci portable_sleep 3513498266Sopenharmony_ci pidfromfile 3613498266Sopenharmony_ci pidexists 3713498266Sopenharmony_ci pidwait 3813498266Sopenharmony_ci processexists 3913498266Sopenharmony_ci killpid 4013498266Sopenharmony_ci killsockfilters 4113498266Sopenharmony_ci killallsockfilters 4213498266Sopenharmony_ci set_advisor_read_lock 4313498266Sopenharmony_ci clear_advisor_read_lock 4413498266Sopenharmony_ci ); 4513498266Sopenharmony_ci 4613498266Sopenharmony_ci # portable sleeping needs Time::HiRes 4713498266Sopenharmony_ci eval { 4813498266Sopenharmony_ci no warnings "all"; 4913498266Sopenharmony_ci require Time::HiRes; 5013498266Sopenharmony_ci }; 5113498266Sopenharmony_ci # portable sleeping falls back to native Sleep on Win32 5213498266Sopenharmony_ci eval { 5313498266Sopenharmony_ci no warnings "all"; 5413498266Sopenharmony_ci require Win32; 5513498266Sopenharmony_ci } 5613498266Sopenharmony_ci} 5713498266Sopenharmony_ci 5813498266Sopenharmony_ciuse serverhelp qw( 5913498266Sopenharmony_ci servername_id 6013498266Sopenharmony_ci mainsockf_pidfilename 6113498266Sopenharmony_ci datasockf_pidfilename 6213498266Sopenharmony_ci ); 6313498266Sopenharmony_ci 6413498266Sopenharmony_ciuse pathhelp qw( 6513498266Sopenharmony_ci os_is_win 6613498266Sopenharmony_ci ); 6713498266Sopenharmony_ci 6813498266Sopenharmony_ci####################################################################### 6913498266Sopenharmony_ci# portable_sleep uses Time::HiRes::sleep if available and falls back 7013498266Sopenharmony_ci# to the classic approach of using select(undef, undef, undef, ...). 7113498266Sopenharmony_ci# even though that one is not portable due to being implemented using 7213498266Sopenharmony_ci# select on Windows: https://perldoc.perl.org/perlport.html#select 7313498266Sopenharmony_ci# Therefore it uses Win32::Sleep on Windows systems instead. 7413498266Sopenharmony_ci# 7513498266Sopenharmony_cisub portable_sleep { 7613498266Sopenharmony_ci my ($seconds) = @_; 7713498266Sopenharmony_ci 7813498266Sopenharmony_ci if($Time::HiRes::VERSION) { 7913498266Sopenharmony_ci Time::HiRes::sleep($seconds); 8013498266Sopenharmony_ci } 8113498266Sopenharmony_ci elsif (os_is_win()) { 8213498266Sopenharmony_ci Win32::Sleep($seconds*1000); 8313498266Sopenharmony_ci } 8413498266Sopenharmony_ci else { 8513498266Sopenharmony_ci select(undef, undef, undef, $seconds); 8613498266Sopenharmony_ci } 8713498266Sopenharmony_ci} 8813498266Sopenharmony_ci 8913498266Sopenharmony_ci####################################################################### 9013498266Sopenharmony_ci# pidfromfile returns the pid stored in the given pidfile. The value 9113498266Sopenharmony_ci# of the returned pid will never be a negative value. It will be zero 9213498266Sopenharmony_ci# on any file related error or if a pid can not be extracted from the 9313498266Sopenharmony_ci# given file. 9413498266Sopenharmony_ci# 9513498266Sopenharmony_cisub pidfromfile { 9613498266Sopenharmony_ci my $pidfile = $_[0]; 9713498266Sopenharmony_ci my $pid = 0; 9813498266Sopenharmony_ci 9913498266Sopenharmony_ci if(-f $pidfile && -s $pidfile && open(my $pidfh, "<", "$pidfile")) { 10013498266Sopenharmony_ci $pid = 0 + <$pidfh>; 10113498266Sopenharmony_ci close($pidfh); 10213498266Sopenharmony_ci $pid = 0 if($pid < 0); 10313498266Sopenharmony_ci } 10413498266Sopenharmony_ci return $pid; 10513498266Sopenharmony_ci} 10613498266Sopenharmony_ci 10713498266Sopenharmony_ci####################################################################### 10813498266Sopenharmony_ci# pidexists checks if a process with a given pid exists and is alive. 10913498266Sopenharmony_ci# This will return the positive pid if the process exists and is alive. 11013498266Sopenharmony_ci# This will return the negative pid if the process exists differently. 11113498266Sopenharmony_ci# This will return 0 if the process could not be found. 11213498266Sopenharmony_ci# 11313498266Sopenharmony_cisub pidexists { 11413498266Sopenharmony_ci my $pid = $_[0]; 11513498266Sopenharmony_ci 11613498266Sopenharmony_ci if($pid > 0) { 11713498266Sopenharmony_ci # verify if currently existing Windows process 11813498266Sopenharmony_ci if ($pid > 65536 && os_is_win()) { 11913498266Sopenharmony_ci $pid -= 65536; 12013498266Sopenharmony_ci if($^O ne 'MSWin32') { 12113498266Sopenharmony_ci my $filter = "PID eq $pid"; 12213498266Sopenharmony_ci my $result = `tasklist -fi \"$filter\" 2>nul`; 12313498266Sopenharmony_ci if(index($result, "$pid") != -1) { 12413498266Sopenharmony_ci return -$pid; 12513498266Sopenharmony_ci } 12613498266Sopenharmony_ci return 0; 12713498266Sopenharmony_ci } 12813498266Sopenharmony_ci } 12913498266Sopenharmony_ci 13013498266Sopenharmony_ci # verify if currently existing and alive 13113498266Sopenharmony_ci if(kill(0, $pid)) { 13213498266Sopenharmony_ci return $pid; 13313498266Sopenharmony_ci } 13413498266Sopenharmony_ci } 13513498266Sopenharmony_ci 13613498266Sopenharmony_ci return 0; 13713498266Sopenharmony_ci} 13813498266Sopenharmony_ci 13913498266Sopenharmony_ci####################################################################### 14013498266Sopenharmony_ci# pidterm asks the process with a given pid to terminate gracefully. 14113498266Sopenharmony_ci# 14213498266Sopenharmony_cisub pidterm { 14313498266Sopenharmony_ci my $pid = $_[0]; 14413498266Sopenharmony_ci 14513498266Sopenharmony_ci if($pid > 0) { 14613498266Sopenharmony_ci # request the process to quit 14713498266Sopenharmony_ci if ($pid > 65536 && os_is_win()) { 14813498266Sopenharmony_ci $pid -= 65536; 14913498266Sopenharmony_ci if($^O ne 'MSWin32') { 15013498266Sopenharmony_ci my $filter = "PID eq $pid"; 15113498266Sopenharmony_ci my $result = `tasklist -fi \"$filter\" 2>nul`; 15213498266Sopenharmony_ci if(index($result, "$pid") != -1) { 15313498266Sopenharmony_ci system("taskkill -fi \"$filter\" >nul 2>&1"); 15413498266Sopenharmony_ci } 15513498266Sopenharmony_ci return; 15613498266Sopenharmony_ci } 15713498266Sopenharmony_ci } 15813498266Sopenharmony_ci 15913498266Sopenharmony_ci # signal the process to terminate 16013498266Sopenharmony_ci kill("TERM", $pid); 16113498266Sopenharmony_ci } 16213498266Sopenharmony_ci} 16313498266Sopenharmony_ci 16413498266Sopenharmony_ci####################################################################### 16513498266Sopenharmony_ci# pidkill kills the process with a given pid mercilessly and forcefully. 16613498266Sopenharmony_ci# 16713498266Sopenharmony_cisub pidkill { 16813498266Sopenharmony_ci my $pid = $_[0]; 16913498266Sopenharmony_ci 17013498266Sopenharmony_ci if($pid > 0) { 17113498266Sopenharmony_ci # request the process to quit 17213498266Sopenharmony_ci if ($pid > 65536 && os_is_win()) { 17313498266Sopenharmony_ci $pid -= 65536; 17413498266Sopenharmony_ci if($^O ne 'MSWin32') { 17513498266Sopenharmony_ci my $filter = "PID eq $pid"; 17613498266Sopenharmony_ci my $result = `tasklist -fi \"$filter\" 2>nul`; 17713498266Sopenharmony_ci if(index($result, "$pid") != -1) { 17813498266Sopenharmony_ci system("taskkill -f -fi \"$filter\" >nul 2>&1"); 17913498266Sopenharmony_ci # Windows XP Home compatibility 18013498266Sopenharmony_ci system("tskill $pid >nul 2>&1"); 18113498266Sopenharmony_ci } 18213498266Sopenharmony_ci return; 18313498266Sopenharmony_ci } 18413498266Sopenharmony_ci } 18513498266Sopenharmony_ci 18613498266Sopenharmony_ci # signal the process to terminate 18713498266Sopenharmony_ci kill("KILL", $pid); 18813498266Sopenharmony_ci } 18913498266Sopenharmony_ci} 19013498266Sopenharmony_ci 19113498266Sopenharmony_ci####################################################################### 19213498266Sopenharmony_ci# pidwait waits for the process with a given pid to be terminated. 19313498266Sopenharmony_ci# 19413498266Sopenharmony_cisub pidwait { 19513498266Sopenharmony_ci my $pid = $_[0]; 19613498266Sopenharmony_ci my $flags = $_[1]; 19713498266Sopenharmony_ci 19813498266Sopenharmony_ci # check if the process exists 19913498266Sopenharmony_ci if ($pid > 65536 && os_is_win()) { 20013498266Sopenharmony_ci if($flags == &WNOHANG) { 20113498266Sopenharmony_ci return pidexists($pid)?0:$pid; 20213498266Sopenharmony_ci } 20313498266Sopenharmony_ci while(pidexists($pid)) { 20413498266Sopenharmony_ci portable_sleep(0.01); 20513498266Sopenharmony_ci } 20613498266Sopenharmony_ci return $pid; 20713498266Sopenharmony_ci } 20813498266Sopenharmony_ci 20913498266Sopenharmony_ci # wait on the process to terminate 21013498266Sopenharmony_ci return waitpid($pid, $flags); 21113498266Sopenharmony_ci} 21213498266Sopenharmony_ci 21313498266Sopenharmony_ci####################################################################### 21413498266Sopenharmony_ci# processexists checks if a process with the pid stored in the given 21513498266Sopenharmony_ci# pidfile exists and is alive. This will return 0 on any file related 21613498266Sopenharmony_ci# error or if a pid can not be extracted from the given file. When a 21713498266Sopenharmony_ci# process with the same pid as the one extracted from the given file 21813498266Sopenharmony_ci# is currently alive this returns that positive pid. Otherwise, when 21913498266Sopenharmony_ci# the process is not alive, will return the negative value of the pid. 22013498266Sopenharmony_ci# 22113498266Sopenharmony_cisub processexists { 22213498266Sopenharmony_ci use POSIX ":sys_wait_h"; 22313498266Sopenharmony_ci my $pidfile = $_[0]; 22413498266Sopenharmony_ci 22513498266Sopenharmony_ci # fetch pid from pidfile 22613498266Sopenharmony_ci my $pid = pidfromfile($pidfile); 22713498266Sopenharmony_ci 22813498266Sopenharmony_ci if($pid > 0) { 22913498266Sopenharmony_ci # verify if currently alive 23013498266Sopenharmony_ci if(pidexists($pid)) { 23113498266Sopenharmony_ci return $pid; 23213498266Sopenharmony_ci } 23313498266Sopenharmony_ci else { 23413498266Sopenharmony_ci # get rid of the certainly invalid pidfile 23513498266Sopenharmony_ci unlink($pidfile) if($pid == pidfromfile($pidfile)); 23613498266Sopenharmony_ci # reap its dead children, if not done yet 23713498266Sopenharmony_ci pidwait($pid, &WNOHANG); 23813498266Sopenharmony_ci # negative return value means dead process 23913498266Sopenharmony_ci return -$pid; 24013498266Sopenharmony_ci } 24113498266Sopenharmony_ci } 24213498266Sopenharmony_ci return 0; 24313498266Sopenharmony_ci} 24413498266Sopenharmony_ci 24513498266Sopenharmony_ci####################################################################### 24613498266Sopenharmony_ci# killpid attempts to gracefully stop processes in the given pid list 24713498266Sopenharmony_ci# with a SIGTERM signal and SIGKILLs those which haven't died on time. 24813498266Sopenharmony_ci# 24913498266Sopenharmony_cisub killpid { 25013498266Sopenharmony_ci my ($verbose, $pidlist) = @_; 25113498266Sopenharmony_ci use POSIX ":sys_wait_h"; 25213498266Sopenharmony_ci my @requested; 25313498266Sopenharmony_ci my @signalled; 25413498266Sopenharmony_ci my @reapchild; 25513498266Sopenharmony_ci 25613498266Sopenharmony_ci # The 'pidlist' argument is a string of whitespace separated pids. 25713498266Sopenharmony_ci return if(not defined($pidlist)); 25813498266Sopenharmony_ci 25913498266Sopenharmony_ci # Make 'requested' hold the non-duplicate pids from 'pidlist'. 26013498266Sopenharmony_ci @requested = split(' ', $pidlist); 26113498266Sopenharmony_ci return if(not @requested); 26213498266Sopenharmony_ci if(scalar(@requested) > 2) { 26313498266Sopenharmony_ci @requested = sort({$a <=> $b} @requested); 26413498266Sopenharmony_ci } 26513498266Sopenharmony_ci for(my $i = scalar(@requested) - 2; $i >= 0; $i--) { 26613498266Sopenharmony_ci if($requested[$i] == $requested[$i+1]) { 26713498266Sopenharmony_ci splice @requested, $i+1, 1; 26813498266Sopenharmony_ci } 26913498266Sopenharmony_ci } 27013498266Sopenharmony_ci 27113498266Sopenharmony_ci # Send a SIGTERM to processes which are alive to gracefully stop them. 27213498266Sopenharmony_ci foreach my $tmp (@requested) { 27313498266Sopenharmony_ci chomp $tmp; 27413498266Sopenharmony_ci if($tmp =~ /^(\d+)$/) { 27513498266Sopenharmony_ci my $pid = $1; 27613498266Sopenharmony_ci if($pid > 0) { 27713498266Sopenharmony_ci if(pidexists($pid)) { 27813498266Sopenharmony_ci print("RUN: Process with pid $pid signalled to die\n") 27913498266Sopenharmony_ci if($verbose); 28013498266Sopenharmony_ci pidterm($pid); 28113498266Sopenharmony_ci push @signalled, $pid; 28213498266Sopenharmony_ci } 28313498266Sopenharmony_ci else { 28413498266Sopenharmony_ci print("RUN: Process with pid $pid already dead\n") 28513498266Sopenharmony_ci if($verbose); 28613498266Sopenharmony_ci # if possible reap its dead children 28713498266Sopenharmony_ci pidwait($pid, &WNOHANG); 28813498266Sopenharmony_ci push @reapchild, $pid; 28913498266Sopenharmony_ci } 29013498266Sopenharmony_ci } 29113498266Sopenharmony_ci } 29213498266Sopenharmony_ci } 29313498266Sopenharmony_ci 29413498266Sopenharmony_ci # Allow all signalled processes five seconds to gracefully die. 29513498266Sopenharmony_ci if(@signalled) { 29613498266Sopenharmony_ci my $twentieths = 5 * 20; 29713498266Sopenharmony_ci while($twentieths--) { 29813498266Sopenharmony_ci for(my $i = scalar(@signalled) - 1; $i >= 0; $i--) { 29913498266Sopenharmony_ci my $pid = $signalled[$i]; 30013498266Sopenharmony_ci if(!pidexists($pid)) { 30113498266Sopenharmony_ci print("RUN: Process with pid $pid gracefully died\n") 30213498266Sopenharmony_ci if($verbose); 30313498266Sopenharmony_ci splice @signalled, $i, 1; 30413498266Sopenharmony_ci # if possible reap its dead children 30513498266Sopenharmony_ci pidwait($pid, &WNOHANG); 30613498266Sopenharmony_ci push @reapchild, $pid; 30713498266Sopenharmony_ci } 30813498266Sopenharmony_ci } 30913498266Sopenharmony_ci last if(not scalar(@signalled)); 31013498266Sopenharmony_ci portable_sleep(0.05); 31113498266Sopenharmony_ci } 31213498266Sopenharmony_ci } 31313498266Sopenharmony_ci 31413498266Sopenharmony_ci # Mercilessly SIGKILL processes still alive. 31513498266Sopenharmony_ci if(@signalled) { 31613498266Sopenharmony_ci foreach my $pid (@signalled) { 31713498266Sopenharmony_ci if($pid > 0) { 31813498266Sopenharmony_ci print("RUN: Process with pid $pid forced to die with SIGKILL\n") 31913498266Sopenharmony_ci if($verbose); 32013498266Sopenharmony_ci pidkill($pid); 32113498266Sopenharmony_ci # if possible reap its dead children 32213498266Sopenharmony_ci pidwait($pid, &WNOHANG); 32313498266Sopenharmony_ci push @reapchild, $pid; 32413498266Sopenharmony_ci } 32513498266Sopenharmony_ci } 32613498266Sopenharmony_ci } 32713498266Sopenharmony_ci 32813498266Sopenharmony_ci # Reap processes dead children for sure. 32913498266Sopenharmony_ci if(@reapchild) { 33013498266Sopenharmony_ci foreach my $pid (@reapchild) { 33113498266Sopenharmony_ci if($pid > 0) { 33213498266Sopenharmony_ci pidwait($pid, 0); 33313498266Sopenharmony_ci } 33413498266Sopenharmony_ci } 33513498266Sopenharmony_ci } 33613498266Sopenharmony_ci} 33713498266Sopenharmony_ci 33813498266Sopenharmony_ci####################################################################### 33913498266Sopenharmony_ci# killsockfilters kills sockfilter processes for a given server. 34013498266Sopenharmony_ci# 34113498266Sopenharmony_cisub killsockfilters { 34213498266Sopenharmony_ci my ($piddir, $proto, $ipvnum, $idnum, $verbose, $which) = @_; 34313498266Sopenharmony_ci my $server; 34413498266Sopenharmony_ci my $pidfile; 34513498266Sopenharmony_ci my $pid; 34613498266Sopenharmony_ci 34713498266Sopenharmony_ci return if($proto !~ /^(ftp|imap|pop3|smtp)$/); 34813498266Sopenharmony_ci 34913498266Sopenharmony_ci die "unsupported sockfilter: $which" 35013498266Sopenharmony_ci if($which && ($which !~ /^(main|data)$/)); 35113498266Sopenharmony_ci 35213498266Sopenharmony_ci $server = servername_id($proto, $ipvnum, $idnum) if($verbose); 35313498266Sopenharmony_ci 35413498266Sopenharmony_ci if(!$which || ($which eq 'main')) { 35513498266Sopenharmony_ci $pidfile = mainsockf_pidfilename($piddir, $proto, $ipvnum, $idnum); 35613498266Sopenharmony_ci $pid = processexists($pidfile); 35713498266Sopenharmony_ci if($pid > 0) { 35813498266Sopenharmony_ci printf("* kill pid for %s-%s => %d\n", $server, 35913498266Sopenharmony_ci ($proto eq 'ftp')?'ctrl':'filt', $pid) if($verbose); 36013498266Sopenharmony_ci pidkill($pid); 36113498266Sopenharmony_ci pidwait($pid, 0); 36213498266Sopenharmony_ci } 36313498266Sopenharmony_ci unlink($pidfile) if(-f $pidfile); 36413498266Sopenharmony_ci } 36513498266Sopenharmony_ci 36613498266Sopenharmony_ci return if($proto ne 'ftp'); 36713498266Sopenharmony_ci 36813498266Sopenharmony_ci if(!$which || ($which eq 'data')) { 36913498266Sopenharmony_ci $pidfile = datasockf_pidfilename($piddir, $proto, $ipvnum, $idnum); 37013498266Sopenharmony_ci $pid = processexists($pidfile); 37113498266Sopenharmony_ci if($pid > 0) { 37213498266Sopenharmony_ci printf("* kill pid for %s-data => %d\n", $server, 37313498266Sopenharmony_ci $pid) if($verbose); 37413498266Sopenharmony_ci pidkill($pid); 37513498266Sopenharmony_ci pidwait($pid, 0); 37613498266Sopenharmony_ci } 37713498266Sopenharmony_ci unlink($pidfile) if(-f $pidfile); 37813498266Sopenharmony_ci } 37913498266Sopenharmony_ci} 38013498266Sopenharmony_ci 38113498266Sopenharmony_ci####################################################################### 38213498266Sopenharmony_ci# killallsockfilters kills sockfilter processes for all servers. 38313498266Sopenharmony_ci# 38413498266Sopenharmony_cisub killallsockfilters { 38513498266Sopenharmony_ci my ($piddir, $verbose) = @_; 38613498266Sopenharmony_ci 38713498266Sopenharmony_ci for my $proto (('ftp', 'imap', 'pop3', 'smtp')) { 38813498266Sopenharmony_ci for my $ipvnum (('4', '6')) { 38913498266Sopenharmony_ci for my $idnum (('1', '2')) { 39013498266Sopenharmony_ci killsockfilters($piddir, $proto, $ipvnum, $idnum, $verbose); 39113498266Sopenharmony_ci } 39213498266Sopenharmony_ci } 39313498266Sopenharmony_ci } 39413498266Sopenharmony_ci} 39513498266Sopenharmony_ci 39613498266Sopenharmony_ci 39713498266Sopenharmony_cisub set_advisor_read_lock { 39813498266Sopenharmony_ci my ($filename) = @_; 39913498266Sopenharmony_ci 40013498266Sopenharmony_ci my $fileh; 40113498266Sopenharmony_ci if(open($fileh, ">", "$filename") && close($fileh)) { 40213498266Sopenharmony_ci return; 40313498266Sopenharmony_ci } 40413498266Sopenharmony_ci printf "Error creating lock file $filename error: $!\n"; 40513498266Sopenharmony_ci} 40613498266Sopenharmony_ci 40713498266Sopenharmony_ci 40813498266Sopenharmony_cisub clear_advisor_read_lock { 40913498266Sopenharmony_ci my ($filename) = @_; 41013498266Sopenharmony_ci 41113498266Sopenharmony_ci if(-f $filename) { 41213498266Sopenharmony_ci unlink($filename); 41313498266Sopenharmony_ci } 41413498266Sopenharmony_ci} 41513498266Sopenharmony_ci 41613498266Sopenharmony_ci 41713498266Sopenharmony_ci1; 418