standard output impact SIGKILL? - php

I have a script to limit the execution time length of commands.
limit.php
<?php
declare(ticks = 1);
if ($argc<2) die("Wrong parameter\n");
$cmd = $argv[1];
$tl = isset($argv[2]) ? intval($argv[2]) : 3;
$pid = pcntl_fork();
if (-1 == $pid) {
die('FORK_FAILED');
} elseif ($pid == 0) {
exec($cmd);
posix_kill(posix_getppid(), SIGALRM);
} else {
pcntl_signal(SIGALRM, create_function('$signo',"die('EXECUTE_ENDED');"));
sleep($tl);
posix_kill($pid, SIGKILL);
die("TIMEOUT_KILLED : $pid");
}
Then I test this script with some commands.
TEST A
php limit.php "php -r 'while(1){sleep(1);echo PHP_OS;}'" 3
After 3s, we can find the processes were killed as we expected.
TEST B
Remove the output code and run again.
php limit.php "php -r 'while(1){sleep(1);}'" 3
Result looks not good, the process created by function "exec" was not killed like TEST A.
[alix#s4 tmp]$ ps aux | grep whil[e]
alix 4433 0.0 0.1 139644 6860 pts/0 S 10:32 0:00 php -r while(1){sleep(1);}
System info
[alix#s4 tmp]$ uname -a
Linux s4 2.6.18-308.1.1.el5 #1 SMP Wed Mar 7 04:16:51 EST 2012 x86_64 x86_64 x86_64 GNU/Linux
[alix#s4 tmp]$ php -v
PHP 5.3.9 (cli) (built: Feb 15 2012 11:54:46)
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2012 Zend Technologies
Why the processes killed in TEST A but not in TEST B? Does the output impact the SIGKILL?
Any suggestion?

There is a PIPE between php -r 'while(1){sleep(1);echo PHP_OS;} (process C) and it's parent (process B), posix_kill($pid, SIGKILL) sends KILL signal to process B, then process B is terminated, but process C doesn't know anything about the signal and continues to run and outputs something to the broken pipe, when process C receives the SIGPIPE signal but has no idea how to handle it so it exits.
You can verify it with strace (run php limit.php "strace php -r 'while(1){sleep(1); echo PHP_OS;};'" 1), and you will see something like this:
14:43:49.254809 write(1, "Linux", 5) = -1 EPIPE (Broken pipe)
14:43:49.254952 --- SIGPIPE (Broken pipe) # 0 (0) ---
14:43:49.255110 close(2) = 0
14:43:49.255212 close(1) = 0
14:43:49.255307 close(0) = 0
14:43:49.255402 munmap(0x7fb0762f2000, 4096) = 0
14:43:49.257781 munmap(0x7fb076342000, 1052672) = 0
14:43:49.258100 munmap(0x7fb076443000, 266240) = 0
14:43:49.258268 munmap(0x7fb0762f3000, 323584) = 0
14:43:49.258555 exit_group(0) = ?
As to php -r 'while(1){sleep(1);}, because there is no broken pipe occurs after it's parent dies, so it continues to run as expected.
Generally speaking, you should kill the whole process group but not only the process itself if you want to kill it's children too, with PHP you can add process B to its own process group, and kill the whole group then, here is the diff with your code:
--- limit.php 2012-08-11 20:50:22.000000000 +0800
+++ limit-new.php 2012-08-11 20:50:39.000000000 +0800
## -9,11 +9,13 ##
if (-1 == $pid) {
die('FORK_FAILED');
} elseif ($pid == 0) {
+ $_pid = posix_getpid();
+ posix_setpgid($_pid, $_pid);
exec($cmd);
posix_kill(posix_getppid(), SIGALRM);
} else {
pcntl_signal(SIGALRM, create_function('$signo',"die('EXECUTE_ENDED');"));
sleep($tl);
- posix_kill($pid, SIGKILL);
+ posix_kill(-$pid, SIGKILL);
die("TIMEOUT_KILLED : $pid");
}

You send the kill signall to your forked process, but that does not propagate to it's children or grandchildren. As such they are orphaned and continue running until something stops them from doing so. (In this case, any attempts to write to stdout should cause an error that then forces them to exit. Redirection of output would also probably result in indefinitely-running orphans.)
You want to send a kill signal to the process and all it's children. Unfortunately I lack the knowledge to tell you a good way to do that. I'm not very familiar with the process control functionality of PHP. Could parse the output of ps.
One simple way I found that works though is to send a kill signal to the whole process group with the kill command. It's messy, and it adds an extra "Killed" message to output on my machine, but it seems to work.
<?php
declare(ticks = 1);
if ($argc<2) die("Wrong parameter\n");
$cmd = $argv[1];
$tl = isset($argv[2]) ? intval($argv[2]) : 3;
$pid = pcntl_fork();
if (-1 == $pid) {
die('FORK_FAILED');
} elseif ($pid == 0) {
exec($cmd);
posix_kill(posix_getppid(), SIGALRM);
} else {
pcntl_signal(SIGALRM, create_function('$signo',"die('EXECUTE_ENDED');"));
sleep($tl);
$gpid = posix_getpgid($pid);
echo("TIMEOUT_KILLED : $pid");
exec("kill -KILL -{$gpid}"); //This will also cause the script to kill itself.
}
For more information see: Best way to kill all child processes

Related

Get all user processes via script run by cron job

I have a corn job that executes a PHP script every minute and execute processchecker.php. The script processchecker.php will then check from user process, which one contains the filename backgroundprocess.php.
This works perfectly if iam triggering these files manually by visiting their respective URLs.
Problem comes in when i automate the process as a cron job which for some reason does not return the processes that i am looking for. Cron jobs seem to be running with no user account and i am suspecting i need a method of listing all processes especially those started by the cron job itself.
processcheck.php
<?php
exec("ps aux", $output, $result);
$found=0;
foreach ($output AS $line) if(strpos($line, "backgroundprocess.php")){ $found=$found+1;};
if($found==0){
//service not running start it all over again
if (!$pid = shell_exec("nohup php backgroundprocess.php > /dev/null 2>&1 & echo $!")) return false;
}else{
//service is Already running
}
?>
From what i am seeing exec("ps aux", $output, $result); is not fetching processes started by the cron job itself......and therefore my background process will always be started over and over.
Please note, all this is on a remote vps server and i am using cpanel.
EDIT
Result is 0
Output is
Array
(
[0] => USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
[1] => rycjptbb 1 0.0 0.0 6324 600 ? SN 14:51 0:00 jailshell (rycjptbb) [init] ell -c nohup php public_html/processchecker.php > /dev/null & echo $!
[2] => rycjptbb 3 1.0 0.0 248940 12308 ? SN 14:51 0:00 php public_html/processchecker.php
[3] => rycjptbb 4 0.0 0.0 110236 1112 ? RN 14:51 0:00 ps aux
)
From what I understand, you just want to check if there is not other same process currently running.
For example, if process1.php runs every 1 minute, and the runtime can take lets say 3 minutes, you dont want another process to run in the second minute.
If that is your case then you can check if there is another process with the same name exists with this:
function is_other_process_exists()
{
$my_name = 'xxx'; //your process name, e.g. process1.php
$ps = `ps gax`;
$psLines = explode("\n", $ps);
array_pop($psLines);
$myLines = array();
foreach ( $psLines as $psLine )
{
if (strstr($psLine, $my_name))
{
$myLines[] = $psLine;
}
}
if ( count($myLines) > 1 ) {
$myPid = posix_getpid();
echo "process is already running with process id {$myPid}";
exit;
}
}

How to limit 1 instance of a PHP script, what's wrong with my solution

<?PHP
define('TEMPPATH', '/tmp/');
$fp = fopen(TEMPPATH.'abc.php.lock', 'a+');
if(!flock($fp, LOCK_EX | LOCK_NB))
exit;
I use the above codes to limit the script from running multiple instances, and it's OK when I test it, but today when I logged in to my server (CentOS 6) and I saw two instances running, how could that happen?
[root#server user]# ps aux | grep abc
root 21061 0.0 0.1 103284 2016 pts/0 S+ 15:45 0:00 grep abc
user 22560 0.0 1.2 154608 12788 ? Ss Nov10 1:35 /usr/bin/php /path/abc.php
user 25106 0.0 1.3 154896 13336 ? Ss Nov06 2:51 /usr/bin/php /path/abc.php
The script was started from crontab jobs, crontab tries to run it every minute, is there any better ways to do this ?
Very elaborate instance control :).
Take a look at mine:
function par_processes($name = null,$ownPids = array(),$dbg = false){
exec("ps x | grep $name", $ProcessState);
$modifier = 0;
while (list($idp,$procs) = each($ProcessState)){
if (preg_match("/grep/i",$procs)) {
unset($ProcessState[$idp]);
}else{
if (!empty($ownPids)){
if (in_array(strstr(trim($procs)," ",true),$ownPids)){
unset($ProcessState[$idp]);
}
}
}
}
if (!$dbg){
return (count($ProcessState) - $modifier);
}else{
return array(0=>(count($ProcessState) - $modifier),1=>$ProcessState,2=>$ownPids);
}
}
And in the php daemon before I go any further I use this:
# INSTANCE CHECK
if (par_processes("MYSOFTWARENAME",array(getmypid()))){
error_log("SOME ERROR"); die();
}
Quite frankly it can be done quicker and more effective (probably) but it does the job.
Of course you need to be able to run exec() for this to work.

PHP - How to check if process is running with multiple parameters

I know how to check if one instance of process is running but how do I check a particular process running with different parameters for example
/usr/local/bin/foo --config /home/config1.txt
/usr/local/bin/foo --config /home/config2.txt
Following code checks only process name, how do I check if a process is running with a particular parameter?
function is_process_running ($process_name) {
$result = array();
exec("/sbin/pidof {$process_name}", $result);
if(is_array($result) && isset($result[0]) && $result[0] >= 1) {
return true;
}
return false;
}
is_process_running('/usr/local/bin/foo --config /home/config1.txt') returns true
is_process_running('/usr/local/bin/foo --config /home/config3.txt') returns false
function is_process_running ($process_name) {
$result = array();
exec("ps -Af | grep {$process_name}", $result);
// A loop that checks for your result and also checks
// that the result isn't the grep command called
// ps -ax | grep firefox asdfasd
// returns grep --color=auto firefox asdfasd
return false;
}
Give it a try. The flag 'f' modifies the output so includes the full call.
Try this bash command to get more details about the processes:
ps ax | grep YourProcesName
I know that at least java processes would should its Paramerers

echo writes successfully on 8 bytes input but failed for others

Thought a lot but could not find a suitable title ... so also suggest on title.
Also I am not showing HTML code to avoid making post larger. With PHP variables names and part of figure shown, its easy to understand the situation.
Actual problem is described here -
Here is a code snippet which is just a small part of a large code. The confusion is b/w two situations. Both are mentioned below. -
PHP Code :
while($npacket>=1) {
$count=0;
$out_file=fopen("packet.txt","w");
while($count<$payload_length ) {
$c=fgetc($file);
fputs($out_file,$c);
$count++;
}
$data_file=`cat packet.txt`;
if($datatyp=="ascii")
$fpayload="-f packet.txt";
else if($datatyp=="hex")
$fpayload="-d0x$data_file";
$random="";
if( $ip=="tcp" || $ip=="udp" )
exec("sendip -v -p ipv6 $fpayload $random $adv_cont -6s $sip $optext -p $proto $cont -$s $sapp_port -$d $dapp_port $dip ");
else {
Problem is Here ....
echo "this is $fpayload it";
exec(" echo sendip -v -p ipv6 $fpayload $random $adv_cont -6s $sip $optext -p icmp $icmp_cont $dip > sample ");
}
usleep("$igap");
if(!$flag)
$npacket--;
}
fclose($out_file);
fclose($file);
}
For 1 packet and payload specified as a input,
1) Input : 616263646566
Output :
$ cat payload.txt
616263646566
$ cat packet.txt
616263646566
$ cat sample
$
2) Input : 6162636465666768
Output :
$ cat payload.txt
6162636465666768
$ cat packet.txt
6162636465666768
$ cat sample
sendip -v -p ipv6 -d0x6162636465666768 -6s 2001::100 -p icmp 2001::200
Please help me find out where does exactly problem lies ?
EDIT:
Permission of files :
$ ls -l payload.txt packet.txt sample
-rwxrwxrwx 1 udit udit 13 Nov 17 20:57 packet.txt
-rwxrwxrwx 1 udit udit 13 Nov 17 20:57 payload.txt
-rwxrwxrwx 1 udit udit 0 Nov 17 20:57 sample
Problem was actually presence of whitespaces and null characters at the end of $fpayload variable. Just needed to add one regular expression syntax in else part.
.........
else if($datatyp=="hex") {
$fpayload="-d0x$data_file";
$fpayload=preg_replace('/\s+/', '', $fpayload);
}
....

linux worker script/queue (php)

I need a binary/script (php) that does the following.
Start n process of X in the background and maintain the number processes.
An example:
n = 50
initially 50 processes are started
a process exits
49 are still running
so 1 should be started again.
P.S.: I posted the same question on SV, which makes me probably very unpopular.
Can you use the crontab linux and write to a db or file the number of current process?.
If DB, the advantage is that you can use to procedure and lock the table, and write the number of process.
But to backgroun you should use & at the end of the call to script
# php-f pro.php &
Pseudocode:
for (i=1; i<=50; i++)
myprocess
endfor
while true
while ( $(ps --no-headers -C myprocess|wc -l) < 50 )
myprocess
endwhile
endwhile
If you translate this to php and fix its flaws, it might just do what you want.
I would go in the direction that andres suggested. Just put something like this at the top of your pro.php file...
$this_file = __FILE__;
$final_count = 50;
$processes = `ps auwx | grep "php -f $this_file"`;
$processes = explode("\n", $processes);
if (count($processes)>$final_count+3) {
exit;
}
//... Remaining code goes here
Have you tried making a PHP Daemon before?
http://kevin.vanzonneveld.net/techblog/article/create_daemons_in_php/
Here's something in Perl I have in my library (and hey, let's be honest, I'm not going to rig this up in PHP just to give you something working in that language this moment. I'm just using what I can copy / paste).
#!/usr/bin/perl
use threads;
use Thread::Queue;
my #workers;
my $num_threads = shift;
my $dbname = shift;
my $queue = new Thread::Queue;
for (0..$num_threads-1) {
$workers[$_] = new threads(\&worker);
print "TEST!\n";
}
while ($_ = shift #ARGV) {
$queue->enqueue($_);
}
sub worker() {
while ($file = $queue->dequeue) {
system ('./4parser.pl', $dbname, $file);
}
}
for (0..$num_threads-1) { $queue->enqueue(undef); }
for (0..$num_threads-1) { $workers[$_]->join; }
Whenever one of those systems calls finishes up, it moves on dequeing. Oh, and damn if I know hwy I did 0..$numthreads instead of the normal my $i = 0; $i < ... idiom, but I did it that way that time.
I have to solutions to propose. Both do child process reboot on exit, do child process reloading on USR1 signal, wait for the children exit on SIGTERM and so on.
The first is based on swoole php extension. It is very performant, async, non-blocking. Here's the usage example code:
<?php
use Symfony\Component\Process\PhpExecutableFinder;
require_once __DIR__.'/../vendor/autoload.php';
$phpBin = (new PhpExecutableFinder)->find();
if (false === $phpBin) {
throw new \LogicException('Php executable could not be found');
}
$daemon = new \App\Infra\Swoole\Daemon();
$daemon->addWorker(1, $phpBin, [__DIR__ . '/console', 'quartz:scheduler', '-vvv']);
$daemon->addWorker(3, $phpBin, [__DIR__ . '/console', 'enqueue:consume', '--setup-broker', '-vvv']);
$daemon->run();
The daemon code is here
Another is based on Symfony process library. It does not require any extra extensions. The usage example and daemon code could be found here

Categories