Exec () with timeout

I am looking for a way to start a PHP process with a timeout. Currently, I just use exec() , but it does not provide a timeout parameter.

I also tried to open the process using proc_open() and using stream_set_timeout() on the received handset, but that didn't work either.

So, is there a way to run a command (or rather, a PHP command) with a timeout? (PS: This applies to cases where the max_execution_time limit does not work, so there is no need to suggest this.)

(By the way, I also need to get the return code of the process.)

+8
source share
6 answers

I searched this topic a bit and came to the conclusion that in some cases (if you are using Linux) you can use the "timeout" command. It's quite flexible

 Usage: timeout [OPTION] DURATION COMMAND [ARG]... or: timeout [OPTION] 

in my particular case, I am trying to run the sphinx indexer from PHP, some kind of migration data script, so I need to reindex my sphinx documents

 exec("timeout {$time} indexer --rotate --all", $output); 

Then I'm going to parse the output and decide to give it another try, or throw an exception and exit my script.

+18
source

I found this on php.net, which I think can do what you want

 <?php function PsExecute($command, $timeout = 60, $sleep = 2) { // First, execute the process, get the process ID $pid = PsExec($command); if( $pid === false ) return false; $cur = 0; // Second, loop for $timeout seconds checking if process is running while( $cur < $timeout ) { sleep($sleep); $cur += $sleep; // If process is no longer running, return true; echo "\n ---- $cur ------ \n"; if( !PsExists($pid) ) return true; // Process must have exited, success! } // If process is still running after timeout, kill the process and return false PsKill($pid); return false; } function PsExec($commandJob) { $command = $commandJob.' > /dev/null 2>&1 & echo $!'; exec($command ,$op); $pid = (int)$op[0]; if($pid!="") return $pid; return false; } function PsExists($pid) { exec("ps ax | grep $pid 2>&1", $output); while( list(,$row) = each($output) ) { $row_array = explode(" ", $row); $check_pid = $row_array[0]; if($pid == $check_pid) { return true; } } return false; } function PsKill($pid) { exec("kill -9 $pid", $output); } ?> 
+5
source

You can fork() and then exec() in one process and wait() non-block in another. Also keep track of the timeout and kill() another process if it does not end on time.

+2
source

(Disclaimer: I was surprised that I did not find a suitable solution for this, then looked at the proc documentation and found it quite straightforwardly. So, here is a simple proc answer that uses its own functions in such a way as to provide consistent results. You can also catch the output for logging purposes.)

The proc function line has proc_terminate ( process-handler ) , which, in combination with proc_get_status ( process-handler ) gets the "running" key, while you can execute a synchronous call to exec with a timeout while sleeping.

So basically:

 $ps = popen('cmd'); $timeout = 5; //5 seconds $starttime = time(); while(time() < $starttime + $timeout) //until the current time is greater than our start time, plus the timeout { $status = proc_get_status($ps); if($status['running']) sleep(1); else return true; //command completed :) } proc_terminate($ps); return false; //command timed out :( 
+1
source

The timeout {$time} command solution does not work properly when called with a PHP script. In my case, with the ssh command to the wrong server (the rsa key was not found, and the server asks for a password), the process is still saved after a certain timeout.

However, I found a function that works fine here:

http://blog.dubbelboer.com/2012/08/24/execute-with-timeout.html

C & P:

 /** * Execute a command and return it 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 from this loop if the process exited before the timeout. 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 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; } 
+1
source

I ran into the same problem as all of the above answers, but the Windows server cannot work with any of them, maybe this is my stupidity.

My ultimate working window solution runs a batch file,

timeout.bat

 ::param 1 is timeout seconds, param 2 is executable echo "running %2 with timeout %1" start %2 set time=0 :check tasklist /FI "IMAGENAME eq %2" 2>NUL | find /I /N "%2">NUL ::time limit exceed if "%time%"=="%1" goto kill ::program is running if "%ERRORLEVEL%"=="0" ( ping 127.0.0.1 -n 2 >nul & set /a time=%time%+1 & goto check) else ( goto end) :kill echo "terminate" taskkill /im %2 /f :end echo "end" 

php command

 exec("timeout.bat {$time} your_program.exe"); 
0
source

All Articles