exec() with timeout

后端 未结 7 1065
北荒
北荒 2020-12-03 04:27

I\'m looking for a way to run a PHP process with a timeout. Currently I\'m simply using exec(), but it does not provide a timeout option.

What I also tried is openin

相关标签:
7条回答
  • 2020-12-03 04:29

    I found this on php.net that 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); 
    } 
    ?>
    
    0 讨论(0)
  • 2020-12-03 04:30

    I've searched a bit on this topic and came to conclusion that in some case (if you are using linux) you can use 'timeout' command. It's pretty flexible

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

    in my particular case I'm trying to run sphinx indexer from PHP, kinda migration data script so I need to reindex my sphinx documents

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

    Then I'm going to analyze output and decide to give it one more try, or throw an exception and quit my script.

    0 讨论(0)
  • 2020-12-03 04:38

    I am facing the same problem that I have tried all the answers above, but windows server cant work with any of these, maybe it is my stupidity.

    My final working solution for windows is executing 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"
    

    the php command

    exec("timeout.bat {$time} your_program.exe");
    
    0 讨论(0)
  • 2020-12-03 04:47

    (Disclaimer: I was surprised to find no good solution for this, then I browsed the proc documentation and found it pretty straight forward. So here is a simple proc answer, that uses native functions in a way that provides consistent results. You can also still catch the output for logging purposes.)

    The proc line of functions has proc_terminate ( process-handler ), which combined with proc_get_status ( process-handler ) getting the "running" key, you can while loop with sleep to do a synchronous exec call with a timeout.

    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 :(
    
    0 讨论(0)
  • 2020-12-03 04:48

    The timeout {$time} command solution does not work properly when it's called from a PHP script. In my case, with a ssh command to a wrong server (rsa key not found, and the server ask for a password), the process still alive after the defined timeout.

    However I've 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'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 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'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;
    }
    
    0 讨论(0)
  • 2020-12-03 04:51

    You could fork() and then exec() in one process and wait() non-blocking in the other. Also keep track of the timeout and kill() the other process if it does not finish in time.

    0 讨论(0)
提交回复
热议问题