Why timeout is not working - php

sorry for stupid question, but
Im trying to set the timeout for program called cirsym. Because sometimes Im facing with infinity process and I have to cancel the operation using ssh...
Would be really thankfull if someone help...
<?
/**
* Execute a command and return it's output. Either wait until the command exits or the timeout has expired.
*
* #param string $cmd Command to execute.
* #param number $timeout Timeout in seconds.
* #return string Output of the command.
* #throws \Exception
*/
function exec_timeout($cmd, $timeout) {
// File descriptors passed to the process.
$descriptors = array(
0 => array('pipe', 'r'), // stdin
1 => array('pipe', 'w'), // stdout
2 => array('pipe', 'w') // stderr
);
// Start the process.
$process = proc_open('exec ' . $cmd, $descriptors, $pipes);
if (!is_resource($process)) {
throw new \Exception('Could not execute process');
}
// Set the stdout stream to none-blocking.
stream_set_blocking($pipes[1], 0);
// Turn the timeout into microseconds.
$timeout = $timeout * 1000000;
// Output buffer.
$buffer = '';
// While we have time to wait.
while ($timeout > 0) {
$start = microtime(true);
// Wait until we have output or the timer expired.
$read = array($pipes[1]);
$other = array();
stream_select($read, $other, $other, 0, $timeout);
// Get the status of the process.
// Do this before we read from the stream,
// this way we can't lose the last bit of output if the process dies between these functions.
$status = proc_get_status($process);
// Read the contents from the buffer.
// This function will always return immediately as the stream is none-blocking.
$buffer .= stream_get_contents($pipes[1]);
if (!$status['running']) {
break;
}
// Subtract the number of microseconds that we waited.
$timeout -= (microtime(true) - $start) * 1000000;
}
// Check if there were any errors.
$errors = stream_get_contents($pipes[2]);
if (!empty($errors)) {
throw new \Exception($errors);
}
// Kill the process in case the timeout expired and it's still running.
// If the process already exited this won't do anything.
proc_terminate($process, 9);
// Close all streams.
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return $buffer;
}
This is the function
private function Exec(){
if(shell_exec($this->canonical_path."cirsym_v2.1.bin") != NULL)
{
return true;
}else{
return false;
}
}

Related

while using proc_open(), my time stamp to terminate the function is not working properly

I am having issues with my php code. I am executing c++rom fffff code using proc_open() in php.
//from another function I'm calling this function with parameters execute("code.exe", $STDIN, 0.02, 268435456 * 2);
//my c++ code is already executed to .exe file.
function execute($cmd, $stdin, $timeout, $memoryout)
{
$descriptorspec = [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']];
$timer = microtime(true);
$TL = false;
$ML = 0;
$stdout = '';
$process = proc_open($cmd, $descriptorspec, $pipes);
fwrite($pipes[0], $stdin);
fclose($pipes[0]);
while(!feof($pipes[1]))
{
if($timer + $timeout < microtime(true))
{
$TL = true;
break;
}
$ML = max($ML, memory_get_usage());
$stdout .= fread($pipes[1], 1);// . "<br/>";
if($timer + $timeout < microtime(true))
{
$TL = true;
break;
}
}
$status = proc_get_status($process);
if(!$status['running'])
if($status['exitcode'] !== 0) $stdout = 'code:RE';
if($TL) $stdout = 'code:TL';
if($ML > $memoryout) $stdout = 'code:ML';
$pid = proc_get_status($process)['pid'];
strstr(PHP_OS, 'WIN') ? exec("taskkill /F /T /PID $pid") : exec("kill -9 $pid");
fclose($pipes[1]);
proc_close($process);
return $stdout;
}
So, when my c++ code is correctly written, there is no problem, but when i've written something like "while(1);" in my c++ code, my php code does not terminate. while(!feof($pipes[1])) cycle loops forever and I don't know what to do.
I've tried to use getrusage() instead of time stamp but it was worse.

Run Function if Different Function Fails to Complete Within Time Frame

I already have a function on a website for a client that grabs data from a live server to save it locally, but something I didn't expect was that sometimes these local servers aren't in an area with good service, so every now and then the script dies after a certain amount of time because it failed to connect.
I already have a system implemented to disable external calls for these types of situations, but the clients can't get to the option to set this "Offline Mode" to begin with because the service is bad and the server is trying to reach the live server.
So what I need to do, is wrap my SyncTable function with a function like set_time_limit(8) that calls a different function that sets the "Offline Mode" automatically if the SyncTable Function fails to complete in the 8 seconds.
Is something like this possible? If so, I would love to know how so I can save these clients some time in areas with rough service.
You can accomplish this using proc_open, proc_get_status, and proc_terminate to start the SyncTable operation as a process, monitor it, and terminate it if necessary. Note: You may need to create a simple wrapper script so you can start the SyncTable function as a standalone process.
Here's a function I use to do this and enforce a timeout:
/// Executes a command and returns the output
/// If the timeout value (in seconds) is reached, it terminates the process
/// and returns FALSE
function exec_timeout($cmd, $timeout=30)
{
$descriptors = array(
0 => array('pipe', 'r'), // stdin
1 => array('pipe', 'w'), // stdout
2 => array('pipe', 'w') // stderr
);
$pipes = Array();
$process = proc_open($cmd, $descriptors, $pipes);
$result = '';
$end_time = time() + $timeout;
if (is_resource($process))
{
// set the streams to non-blocking
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
$timeleft = $end_time - time();
while ($timeleft > 0)
{
$status = proc_get_status($process);
$result .= stream_get_contents($pipes[1]);
// leave the loop if the process has already finished
if (!$status['running'])
break;
$timeleft = $end_time - time();
}
if ($timeleft <= 0)
{
proc_terminate($process);
$result = FALSE;
}
}
// check for errors
$errors = stream_get_contents($pipes[2]);
if (!empty($errors))
fwrite(STDERR, "$errors\n");
// close streams
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return $result;
}

PHP Process Execution Timeout

I have the following code:
/**
* Executes a program and waits for it to finish, taking pipes into account.
* #param string $cmd Command line to execute, including any arguments.
* #param string $input Data for standard input.
* #param integer $timeout How much to wait from program in msecs (-1 to wait indefinitely).
* #return array Array of "stdout", "stderr" and "return".
*/
function execute($cmd,$stdin=null,$timeout=-1){
$proc=proc_open(
$cmd,
array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
$pipes=null
);
fwrite($pipes[0],$stdin); fclose($pipes[0]);
$stdout=stream_get_contents($pipes[1]); fclose($pipes[1]);
$stderr=stream_get_contents($pipes[2]); fclose($pipes[2]);
$return=proc_close($proc);
return array(
'stdout' => $stdout,
'stderr' => $stderr,
'return' => $return
);
}
It has two "problems".
The code is synchronous; it freezes until the target process closes.
So far, I've not been able to it from "freezing" without issuing a different kind of command (such as $cmd > /dev/null & on linux and start /B $cmd on windows)
I don't mind the "freeze", at all. I just need to implement that timeout.
Note: It is important that the solution is cross-platform compatible. It is also important that the $cmd doesn't have to change - I'm running some complex commands and I'm afraid there may be some issues, however, this depends on the type of fix - I'm happy to hear these out, just that I'd prefer a different alternative.
I've found some resources that may help:
Run perl file from PHP script but not wait for output on Windows Server
PHP set timeout for script with system call, set_time_limit not working
http://www.shapeshifter.se/2008/08/04/asynchronous-background-execution-with-php/
There is a few mistakes on the code.
That is actually working:
function execute($cmd, $stdin = null, $timeout = -1)
{
$proc=proc_open(
$cmd,
array(array('pipe','r'), array('pipe','w'), array('pipe','w')),
$pipes
);
var_dump($pipes);
if (isset($stdin))
{
fwrite($pipes[0],$stdin);
}
fclose($pipes[0]);
stream_set_timeout($pipes[1], 0);
stream_set_timeout($pipes[2], 0);
$stdout = '';
$start = microtime();
while ($data = fread($pipes[1], 4096))
{
$meta = stream_get_meta_data($pipes[1]);
if (microtime()-$start>$timeout) break;
if ($meta['timed_out']) continue;
$stdout .= $data;
}
$stdout .= stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
$return = proc_close($proc);
return array(
'stdout' => $stdout,
'stderr' => $stderr,
'return' => $return
);
}
Rather than stream_get_contents, you could look at using fread to gain more finely grained control over what your code is doing. That combined with stream_set_timeout may give you what you're looking for.
I tossed something together as a demonstration of what I was thinking might work - this code is completely untested and comes with no guarantees, but might send you in the right direction. ;)
function execute($cmd,$stdin=null,$timeout=-1){
$proc=proc_open(
$cmd,
array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
$pipes=null
);
fwrite($pipes[0],$stdin); fclose($pipes[0]);
stream_set_timeout($pipes[1], 0);
stream_set_timeout($pipes[2], 0);
$stdout = '';
$start = microtime();
while ($data = fread($pipes[1], 4096))
{
$meta = stream_get_meta_data($pipes[1]);
if (microtime()-$start>$timeout) break;
if ($meta['timed_out']) continue;
$stdout .= $data;
}
$return = proc_close($proc);
$stdout .= stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
return array(
'stdout' => $stdout,
'stderr' => $stderr,
'return' => $return
);
}
This seems to be working for me:
public function toPDF() {
$doc = $this->getDocument();
$descriptor = [
['pipe','r'],
['pipe','w'],
['file','/dev/null','w'], // STDERR
];
$proc = proc_open('/usr/local/project/scripts/dompdf_cli.php',$descriptor,$pipes,sys_get_temp_dir());
fwrite($pipes[0],"$doc[paper]\n$doc[html]");
fclose($pipes[0]);
$timeout = 30;
stream_set_blocking($pipes[1], false);
$pdf = '';
$now = microtime(true);
try {
do {
$elapsed = microtime(true) - $now;
if($elapsed > $timeout) {
throw new \Exception("PDF generation timed out after $timeout seconds");
}
$data = fread($pipes[1], 4096);
if($data === false) {
throw new \Exception("Read failed");
}
if(strlen($data) === 0) {
usleep(50);
continue;
}
$pdf .= $data;
} while(!feof($pipes[1]));
fclose($pipes[1]);
$ret = proc_close($proc);
} catch(\Exception $ex) {
fclose($pipes[1]);
proc_terminate($proc); // proc_close tends to hang if the process is timing out
throw $ex;
}
if($ret !== 0) {
throw new \Exception("dompdf_cli returned non-zero exit status: $ret");
}
// dump('returning pdf');
return $pdf;
}
I'm not sure what the purpose of stream_set_timeout is -- that just sets the per-read timeout, but if you want to limit the overall time, you just have to set the stream to non-blocking mode and then time how long it takes.

Using linux 'dialog' command from PHP

I'm trying to write a helper script for doing various admin tasks on a server which can only be done from the command line and trying to use the 'dialog' command to display message boxes, inputs, password prompts etc, however, the needs of this task call for me to process the data in PHP.
I'm having problems getting the dialog command to work in this way and can't figure out what i'm doing wrong.
There's an example here
Unfortunately it doesn't work.
When you run PHP and exec/backtick/system to an external application, the IO doesn't appear to work how you'd expect.
The nearest I can get is using the passthru() command:
<?php
$CMD = "dialog --menu \"Please select\" 10 40 3 backup \"Backup Files\" restore \"Restore Files\"";
passthru($CMD);
?>
This is the only way that PHP will let dialog use the STDOUT properly, anything else results in no display but you can press return to select an option.
I've tried backticks, exec() and system() but nothing seems to work.
What I wondered was how to read STDERR properly from within PHP to get the return value into a variable called $result.
I'm sure some other sysadmins have had to do this before.
My reasons for not using bash for this are that one command I have to execute as a result of a selection produces XML output only and I can't parse that effectively in bash.
Just in case someone else is searching for this:
function dialog ($args) {
$pipes = array (NULL, NULL, NULL);
// Allow user to interact with dialog
$in = fopen ('php://stdin', 'r');
$out = fopen ('php://stdout', 'w');
// But tell PHP to redirect stderr so we can read it
$p = proc_open ('dialog '.$args, array (
0 => $in,
1 => $out,
2 => array ('pipe', 'w')
), $pipes);
// Wait for and read result
$result = stream_get_contents ($pipes[2]);
// Close all handles
fclose ($pipes[2]);
fclose ($out);
fclose ($in);
proc_close ($p);
// Return result
return $result;
}
It requires dialog (apt-get install dialog) and proc_xxx (PHP 4.3.0, PHP 5)
It works, at least for me. :)
You can use proc_open() but not as show above... All dialog boxes do not work in same way. I provides a concrete sample below:
#!/usr/bin/env php
<?php
$pipes = array();
$process = null;
$output = '';
$ret = -1;
/**
* Start process
*
* #param string $cmd Command to execute
* #param bool $wantinputfd Whether or not input fd (pipe) is required
* #retun void
*/
function processStart($cmd, $wantinputfd = false)
{
global $process, $pipes;
$process = proc_open(
$cmd,
array(
0 => ($wantinputfd) ? array('pipe', 'r') : STDIN, // pipe/fd from which child will read
1 => STDOUT,
2 => array('pipe', 'w'), // pipe to which child will write any errors
3 => array('pipe', 'w') // pipe to which child will write any output
),
$pipes
);
}
/**
* Stop process
*
* #return void
*/
function processStop()
{
global $output, $pipes, $process, $ret;
if (isset($pipes[0]) {
fclose($pipes[0]);
usleep(2000);
}
$output = '';
while ($_ = fgets($pipes[3])) {
$output .= $_;
}
$errors = '';
while ($_ = fgets($pipes[2])) {
fwrite(STDERR, $_);
$errors++;
}
if ($errors) {
fwrite(STDERR, "dialog output the above errors, giving up!\n");
exit(1);
}
fclose($pipes[2]);
fclose($pipes[3]);
do {
usleep(2000);
$status = proc_get_status($process);
} while ($status['running']);
proc_close($process);
$ret = $status['exitcode'];
}
// Test for yesno dialog box
processStart("dialog --backtitle 'dialog test' --title 'Little test' --output-fd 3 --yesno 'yesno dialog box' 0 70");
processStop();
echo "Exit code is $ret\n";
// Test for gauge dialog box
processStart("dialog --backtitle 'dialog test' --title 'Little test' --output-fd 3 --gauge 'Gauge dialog box' 0 70 0", true);
sleep(1);
fwrite($pipes[0], "XXX\n0\nFirst step\nXXX\n20\n");
sleep(1);
fwrite($pipes[0], "XXX\n20\nSecond step\nXXX\n50\n");
sleep(1);
fwrite($pipes[0], "XXX\n50\nThird step\nXXX\n80\n");
sleep(1);
fwrite($pipes[0], "XXX\n80\nFourth step\nXXX\n100\n");
sleep(1);
processStop();
echo "Exit code is $ret\n";
// Test for input dialog box
processStart("dialog --backtitle 'dialog test' --title 'Little test' --output-fd 3 --inputbox 'input dialog box' 0 70");
processStop();
echo "Output is $output\n";
echo "Exit code is $ret\n";
// Test for errors output
processStart("dialog --backtitle 'dialog test' --title 'Little test' --output-fd 3 --dummy 'my input box' 0 70");
processStop();
exit(0);
You can use proc_open() to run a command and interact on all pipes, STDIN, STDOUT and STDERR:
$pipes = array(NULL, NULL, NULL);
$proc = proc_open(
"dialog --gauge ..",
array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w'),
),
$pipes
);
print fgets($pipes[2]);
See the manual for more examples.
PHP-GTK looks like a solution for this problem
http://gtk.php.net/
I think you can't run ncurses application through PHP like this - maybe you should take a look at this: http://php.net/manual/de/book.ncurses.php

How to make fread blocking and read until the end?

I am executing a TCL script from PHP using proc_open.
I first open the TCL shell
2) Send a command using fwrite
3) What I need is fread to wait/block until the
command sent by fwrite is complete
and get all the contents .The command may take some time to complete.
(I am able to read just 2 lines and then it is going off to the next loop)
Can someone guide me.
The present code is
<?php
$app = 'tclsh84';
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","C:/wamp/www/tcl/bin/g.txt","w")
) ;
$process = proc_open($app, $descriptorspec, $pipes);
if (is_resource($process))
{
for($i=0;$i<4;$i++)
{
fwrite($pipes[0], 'source c:/wamp/www/tcl/bin/test.tcl'."\n");
$content= fread($pipes[1],8192)
print "$content";
}
fclose($pipes[0]);
fclose($pipes[1]);
proc_close($process);
}
?>
I'm thinking about a combination of
stream_select and/or feof()
fread() and concatenation of the partial results ($result .= fread())
and maybe proc_get_status() to determine the end of the process
You want to wait until the tcl application doesn't write something to its stdout for a certain amount of time (presuming that this means the end of the last command) and then send the next command/line to its stdin?
edit:
Seems like you can send all commands to the tcl shell at once and they are processed one by one, i.e. the shell reads the next input line/command when it's done with the previous one. I've tested this with the script.
incr a 1
after 1000
puts [concat [clock seconds] $a]
and
<?php
$app = 'c:/programme/tcl/bin/tclsh85.exe';
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","C:/god.txt","w")
) ;
$process = proc_open($app, $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], "set a 1\n");
for($i=0;$i<4;$i++) {
fwrite($pipes[0], "source c:/helloworld.tcl\n");
}
// when all scripts are done the shell shall exit
fwrite($pipes[0], "exit\n");
fclose($pipes[0]);
do {
$read=array($pipes[1]); $write=array(); $except=array($pipes[1]);
// wait up to 1 second for new output of the tcl process
$ready = stream_select($read, $write, $except, 1, 0);
if ( $ready && $read /* is not empty */) {
// get the partial output
$r = fread($pipes[1], 2048);
echo $r;
}
// is the process still running?
$status = proc_get_status($process);
} while($status['running']);
fclose($pipes[1]);
proc_close($process);
}
?>
You probably want to add some more error handling. E.g. if stream_select() returns x times with an timeout something might have gone wrong.
edit2:
Let the shell print something you can scan for after each script.
<?php
// something that's not in the "normal" output of the scripts
$id = 'done'. time();
$app = 'c:/programme/tcl/bin/tclsh85.exe';
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","C:/god.txt","w")
) ;
$process = proc_open($app, $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], "set a 1\n");
for($i=0;$i<4;$i++) {
$output = '';
$continue = true;
$cTimeout = 0;
echo 'loop ', $i, "\n";
fwrite($pipes[0], "source c:/helloworld.tcl\n");
fwrite($pipes[0], "puts $id\n");
echo "waiting for idle\n";
do {
$read=array($pipes[1]);
$write=array();
$except=array($pipes[1]);
$ready = stream_select($read, $write, $except, 1, 0);
if ( $ready && $read ) {
$output .= fread($pipes[1], 2048);
// if the delimiter id shows up in $output
if ( false!==strpos($output, $id) ) {
// the script is done
$continue = false;
}
}
} while($continue);
echo 'loop ', $i, " finished\n";
}
proc_close($process);
}
?>
Try:
$content = '';
while(!feof($pipes[1]))
{
$content .= fread($pipes[1],8192);
}
Does that wait?

Categories