helices Posted June 9, 2015 Share Posted June 9, 2015 I execute a system call via proc_open() that usually completes in less than one minuteSometimes, it hangs for many hoursI want to exit this parent script after thirty (30) minutes with the error description on STDERR and I also want that system call process killed at this pointAlthough, a test script using ticks and pcntl_alarm(1800) works using a while() loop, this does NOT work with my production system callSince ticks is documented as deprecated, I want to find a solution that does not use ticksWhat am I missing?Please, advise. Thank you~ Mike Quote Link to comment Share on other sites More sharing options...
requinix Posted June 9, 2015 Share Posted June 9, 2015 Ticks aren't deprecated... Either ticks won't help you because you're waiting on an external process to complete, or ticks can help because you have code that's actively running but then you don't need ticks in the first place. Apparently it's the latter. Besides, ticks only work in certain PHP setups. What's your code? Quote Link to comment Share on other sites More sharing options...
helices Posted June 9, 2015 Author Share Posted June 9, 2015 It's your first: "waiting on an external process to complete" This works, but sometimes exceeds (30) minutes: $proc = proc_open($prog, $fd, $pipes, $ldir, null); I have a test script using: pcntl_alarm($time); before this: while ( $count < $max ) { ... } which works as desired It took me too long to realize that I needed to proceed pcntl_alarm() with this: declare(ticks=1); or it does not work :-( Does pcntl_alarm() require ticks? Why isn't this documented on the pcntl_alarm() manual page? When I do the $proc = ... thing - even with ticks - the alarm is never caught In the shell or even Perl, alarms work as I expect. What am I missing? Quote Link to comment Share on other sites More sharing options...
requinix Posted June 9, 2015 Share Posted June 9, 2015 It's your first: "waiting on an external process to complete"What's your code? This works, but sometimes exceeds (30) minutes: $proc = proc_open($prog, $fd, $pipes, $ldir, null); That should not block, or at least not for long. What you do or don't do afterwards may cause problems. It took me too long to realize that I needed to proceed pcntl_alarm() with this: declare(ticks=1); or it does not work :-( Does pcntl_alarm() require ticks? Why isn't this documented on the pcntl_alarm() manual page? It does not require ticks, but depending on what you do in your code the alarm signal may not be handled without using ticks. When I do the $proc = ... thing - even with ticks - the alarm is never caughtAgain, depends on your code. Quote Link to comment Share on other sites More sharing options...
helices Posted June 9, 2015 Author Share Posted June 9, 2015 As I posted, this IS my code: $proc = proc_open($prog, $fd, $pipes, $ldir, null); One specific case, $prog is an lftp session to a remote server, getting or putting files Sometimes, unknown problem on that remote server leave this proc_open() call hanging. In these cases, I want an alarm-like process to do something after (30) minutes, such as write a message to STDERR and exit the script. What else do you need to know? Quote Link to comment Share on other sites More sharing options...
requinix Posted June 9, 2015 Share Posted June 9, 2015 (edited) $proc = proc_open($prog, $fd, $pipes, $ldir, null);Well then, if that's your code: 1. You're missing an opening <?php tag 2. $prog is undefined 3. $fd is undefined 4. $pipes is undefined 5. $ldir is undefined 6. You don't do anything with $proc I'm not asking for a single line of code where you think the problem is. I'm asking for ALL of your code. Edited June 9, 2015 by requinix Quote Link to comment Share on other sites More sharing options...
helices Posted June 10, 2015 Author Share Posted June 10, 2015 Suffice it to say that the 10,000 line script works 100% - except that sometimes (1 in 500 runs) that line of code hangs for >30 minutes I've been coding for more than 40 years and PHP for more than 10 years. My code works. The rare hangs are not due to faulty code; rather, they are due to system problems, mostly on remote systems, over which I have 0 control All I am asking for is an example of a multi-tasked timer that can be set immediately prior to the function/subroutine call that includes: proc_open() - so that after X time, I can do something else, other than what that proc_open() is doing. Since "my" code includes PCI related security routines and the bulk of this has zero to do with this particular challenge, I will not be be posting: "ALL of your code" Is this too difficult for you? If so, please, accept my humblest apologies ... ~ Mike Quote Link to comment Share on other sites More sharing options...
ginerjm Posted June 10, 2015 Share Posted June 10, 2015 Nice attitude to someone who is only trying to help. How are we supposed to know about the size of your code and the implications of asking to see 'it'? Sheesh! Quote Link to comment Share on other sites More sharing options...
helices Posted June 10, 2015 Author Share Posted June 10, 2015 OK, I'm sorry for being abrasive today. If I can find a code example that works without a "loop" to increment ticks, I can take it from there. I don't know why alarm in PHP doesn't work like it does in system and Perl. The following test script does what I want, except I must replace the while() loop with the function call to proc_open(): #!/usr/bin/php<?php$count = 0;$interval = 1;### $interval = 5;$max = 5;### $max = 10;$time = 7;# $time = 20;### $time = 900;$one = alarm_test();echo "\n\tONE: {$one}\n\n";$two = post_alarm();echo "\n\tTWO: {$two}\n\n";function post_alarm() { global $count, $interval, $time; $max = $time + 10; while ( $count < $max ) { $count += $interval; print $count."\n"; sleep($interval); } return($count);}function alarm_test() { global $count, $interval, $max, $time; declare(ticks=1); pcntl_signal(SIGALRM, "signal_handler"); pcntl_signal(SIGINT, "signal_handler"); pcntl_signal(SIGQUIT, "signal_handler"); pcntl_signal(SIGTERM, "signal_handler"); pcntl_alarm($time); # Execute time sensitive stuff HERE: while ( $count < $max ) { $count += $interval; print $count."\n"; sleep($interval); } # Any call to pcntl_alarm() will cancel any previously set alarm # If seconds is zero, no new alarm is created pcntl_alarm(0); echo "\n\tAlarm is RESET - We can do something else NOT time sensitive\n\n"; return($max);}function signal_handler($signal) { switch($signal) { case SIGTERM: echo "Caught SIGTERM\n"; exit; case SIGQUIT: echo "Caught SIGQUIT\n"; exit; case SIGINT: echo "Caught SIGINT\n"; exit; case SIGALRM: echo "\nCaught SIGALRM!\n"; echo "DO SOMETHING HERE ...\n\n"; exit; }}?> Following is the function that calls proc_open() from one of many scripts that have worked for more than 10,000 times, with the rare (less than 1 in 500 times) case of taking "too long:" function extProg ($array) { $ldir = $array['local_directory']; $prog = $array['prog']; $sdir = $array['remote_dir']; $fd = array( 1 => array("pipe", "w"), // stdout 2 => array("pipe", "w"), // stderr ); $proc = proc_open($prog, $fd, $pipes, $ldir, null); $stdout = stream_get_contents($pipes[1]); fclose($pipes[1]); $stderr = explode("\n",stream_get_contents($pipes[2])); fclose($pipes[2]); proc_close($proc); $err = $stderr[0]; if ($sdir != '/') { $str = "cd ok, cwd={$sdir}"; if ($stderr[0] == $str) $err = $stderr[1]; } if ($err > '') return $err; return 0;} Does this suffice for my code examples? Thank you. ~ Mike Quote Link to comment Share on other sites More sharing options...
ginerjm Posted June 10, 2015 Share Posted June 10, 2015 Would have been nice if you posted it in the proper tags. Quote Link to comment Share on other sites More sharing options...
helices Posted June 10, 2015 Author Share Posted June 10, 2015 Is this better? #!/usr/bin/php <?php $count = 0; $interval = 1; ### $interval = 5; $max = 5; ### $max = 10; $time = 7; # $time = 20; ### $time = 900; $one = alarm_test(); echo "\n\tONE: {$one}\n\n"; $two = post_alarm(); echo "\n\tTWO: {$two}\n\n"; function post_alarm() { global $count, $interval, $time; $max = $time + 10; while ( $count < $max ) { $count += $interval; print $count."\n"; sleep($interval); } return($count); } function alarm_test() { global $count, $interval, $max, $time; declare(ticks=1); pcntl_signal(SIGALRM, "signal_handler"); pcntl_signal(SIGINT, "signal_handler"); pcntl_signal(SIGQUIT, "signal_handler"); pcntl_signal(SIGTERM, "signal_handler"); pcntl_alarm($time); # Execute time sensitive stuff HERE: while ( $count < $max ) { $count += $interval; print $count."\n"; sleep($interval); } # Any call to pcntl_alarm() will cancel any previously set alarm # If seconds is zero, no new alarm is created pcntl_alarm(0); echo "\n\tAlarm is RESET - We can do something else NOT time sensitive\n\n"; return($max); } function signal_handler($signal) { switch($signal) { case SIGTERM: echo "Caught SIGTERM\n"; exit; case SIGQUIT: echo "Caught SIGQUIT\n"; exit; case SIGINT: echo "Caught SIGINT\n"; exit; case SIGALRM: echo "\nCaught SIGALRM!\n"; echo "DO SOMETHING HERE ...\n\n"; exit; } } ?> function extProg ($array) { $ldir = $array['local_directory']; $prog = $array['prog']; $sdir = $array['remote_dir']; $fd = array( 1 => array("pipe", "w"), // stdout 2 => array("pipe", "w"), // stderr ); $proc = proc_open($prog, $fd, $pipes, $ldir, null); $stdout = stream_get_contents($pipes[1]); fclose($pipes[1]); $stderr = explode("\n",stream_get_contents($pipes[2])); fclose($pipes[2]); proc_close($proc); $err = $stderr[0]; if ($sdir != '/') { $str = "cd ok, cwd={$sdir}"; if ($stderr[0] == $str) $err = $stderr[1]; } if ($err > '') return $err; return 0; } Quote Link to comment Share on other sites More sharing options...
ginerjm Posted June 10, 2015 Share Posted June 10, 2015 Have you read the notes in the manual under proc_open function? I don't know a lot about pipes and such but the language there sounds like it describes the problem you are having. Quote Link to comment Share on other sites More sharing options...
helices Posted June 10, 2015 Author Share Posted June 10, 2015 I have; but, I'm reading them again and not seeing anything jump out at me By "notes," are you referring to this? http://php.net/manual/en/function.proc-open.php#refsect1-function.proc-open-notes Or, User Contributed Notes: http://php.net/manual/en/function.proc-open.php#usernotes If the former, are you suggesting that I ought to use popen() instead of proc_open() ? If the latter, please, point me to the specific user note that you mean. Thank you. Quote Link to comment Share on other sites More sharing options...
helices Posted June 11, 2015 Author Share Posted June 11, 2015 For those who come here later, I have found a solution. I could not find a solution using alarms. Instead, my solution forks a 2nd process to time the 1st. The only problem I've not resolved is eliminating the annoying text "Terminated" from main script output ... #!/usr/bin/php <?php // GLOBALS $CHILD_PID = 0; $PID = getmypid(); $TIMEOUT = 5; // number of seconds to timeout echo "TIMER: {$TIMEOUT} seconds\n\n"; // Make sure program execution doesn't time out // If set to (0), no time limit is imposed set_time_limit(0); // Begin timer here set_timeout(); // Do system stuff here echo "Doing STUFF here\n\n"; extProg(); // End timer here clear_timeout($CHILD_PID); echo "STUFF is _COMPLETE_ here\n\n"; exit(0); // Stop timer function clear_timeout($CHILD_PID) { posix_kill($CHILD_PID, SIGTERM); } // Execute external program function extProg () { $ldir = 'abcdef'; $prog = "/usr/bin/lftp -e \"cd /_DIR_/;pwd;cls -lrt;\" -u '_ACCOUNT_,bogus' sftp://DOMAIN.com"; $sdir = 'uvwxyz'; $fd = array( 1 => array("pipe", "w"), // stdout 2 => array("pipe", "w"), // stderr ); $proc = proc_open($prog, $fd, $pipes, $ldir, null); $stdout = stream_get_contents($pipes[1]); fclose($pipes[1]); $stderr = explode("\n",stream_get_contents($pipes[2])); fclose($pipes[2]); proc_close($proc); $err = $stderr[0]; if ($sdir != '/') { $str = "cd ok, cwd={$sdir}"; if ($stderr[0] == $str) $err = $stderr[1]; } if ($err > '') return $err; return 0; } // Set timeout timer function set_timeout() { global $CHILD_PID; global $PID; global $TIMEOUT; $CHILD_PID = pcntl_fork(); if($CHILD_PID == 0) { sleep($TIMEOUT); posix_kill($PID, SIGTERM); echo "ERROR: extProg() stopped after {$TIMEOUT} seconds: "; exit(999); } } ?> Of course, I have "dummied up" the lftp connection parameters - suffice it to say that, with my real world parameters, this script throws an error, stops the lftp process and exits as I see fit Any ideas how to silence the errant text output? ~ Mike Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.