shell - How can I restart a php script if it freezes?
Get the solution ↓↓↓Solution:
I did a stall detection on a command line script not too long ago. It sounds like a few things need to happen...
1) Add a timeout for the http:// request within that command you're running
2) You can monitor it using a while() statement, you'll have to use something like this:
$stillRunning = true;
while ($stillRunning) {
exec('ps -ef | grep httpdocs/shell/run.php', $processes); //will get all process which contain the path to the file
//loop through all processes to determine if it's still running. If so, grab the process ID and kill it'
exec('kill '.$processId);
//if it's still there, this will do the job
if ($this->_ifProcessExists($processId)) {
exec('kill -9 '.$processId);
}
sleep(2);
}
Here's the class I built to detect stalling to meet our needs...
<?php
/**
* GMC Management Class
*
*/
class GMC {
private $executablePath = '/usr/local/PNetT/PNetTCNetClient',
$outPath = '/usr/local/PNetT/tmpData/SOB/',
$workflowPath = '',
$graphicPath = '',
$logRecipient = '[email protected]',
$stalledThreshold = 10, //in seconds
$originalProcesses = array(),
$processes = array(),
$log = array();
public function __construct () {
$this->workflowPath = $this->outPath.'workflows/';
$this->graphicPath = $this->outPath.'graphics/';
}
/**
* Adds an image to the GMC graphics directory
*
* @param string $imgPath The absolute path of the image to add
*/
public function addImage ($imgPath) {
exec('cp '.$imgPath.' '.$this->graphicPath);
}
/**
* Determines if a process is stalled
*/
public function isStalled ($processes = false) {
if ($processes === false) {
$processes = $this->_getProcesses();
$this->originalProcesses = $processes;
}
foreach ($processes as $process) {
if (array_key_exists('duration', $process) && $process['duration'] <= (time() - $this->stalledThreshold)) {
$this->_addLog("detected #".$process['processId']." as a stalled process");
return true;
}
}
return false;
}
/**
* Fixes GMC's being stalled
* Will also report the document that caused the stall
*/
public function fixStalled () {
$this->_restart();
$this->reportLog();
}
/**
* Sends the log to the development team for review
*/
public function reportLog () {
$body = "Here's a report how GMC was handled during this last stall:
Oldest process:\n
ID | COMMAND\n";
$oldest = $this->_getOldestProcess();
$body .= " ".$oldest['processId']." | ".$oldest['command']."\n";
$body .= "
Logs:\n";
foreach ($this->log as $log) {
$body .= " ".$log."\n";
}
$body .= "
Processes:\n
ID | COMMAND\n";
foreach ($this->processes as $process) {
$body .= " ".$process['processId']." | ".$process['command']."\n";
}
mail($this->logRecipient, 'GMC Stalled Report', $body);
}
/**
* Restarts GMC
* Must be conducted after GMC's stalled processes have been killed
*/
private function _restart () {
//kill all GMC processes
foreach ($this->_getProcesses(true) as $process) {
$this->_killProcess($process['processId']);
}
//restart GMC
exec('bash /usr/local/PNetT/pnet.sh');
$this->_addLog("GMC has been restarted");
}
/**
* Restarts GMC
* Must be conducted after GMC's stalled processes have been killed
*/
private function _getOldestProcess () {
$oldest = array(
'timestamp' => 0,
'processId' => 0
);
foreach ($this->_getProcesses() as $process) {
if ($process['user'] == 'wwwuser' && $process['duration'] > $oldest['timestamp']) {
$oldest = array(
'timestamp' => $process['duration'],
'processId' => $process['processId']
);
}
}
return $this->processes[$oldest['processId']];
}
/**
* Get a list of all GMC processes
*/
private function _getProcesses ($newList = false) {
if ($newList == true || count($this->processes) == 0) {
//exec('ps -ef | grep GMC', $processes);
//sample processes for testing, above command is for live usage
$processes = array(
'ps -ef | grep GMC',
'wwwuser 1767 31199 0 12:40 ? 00:00:00 /scripts/GMC/PNetT-5.1-SP1/PNetTCNetClient.bin -o /usr/local/PNetT/tmpData/SOB/workflows/master_ppo_sob_3col.wfd -e PDF -f /usr/local/PNetT/tmpData/SOB/Master PPO-LP2069 Solution PPO 1500-15-20 MOCKUP_temp.pdf -useincluded * -difDataInput1 /usr/local/PNetT/tmpData/SOB/Master PPO-LP2069 Solution PPO 1500-15-20 MOCKUP.txt -difDataInput2 /usr/local/PNetT/tmpData/SOB/Master PPO-LP2069 Solution PPO 1500-15-20 MOCKUP-table.txt',
'wwwuser 2364 1207 0 12:41 ? 00:00:00 /scripts/GMC/PNetT-5.1-SP1/PNetTCNetClient.bin -o /usr/local/PNetT/tmpData/SOB/workflows/master_ppo_sob_3col.wfd -e PDF -f /usr/local/PNetT/tmpData/SOB/Master PPO-LP2045 mock up_temp.pdf -useincluded * -difDataInput1 /usr/local/PNetT/tmpData/SOB/Master PPO-LP2045 mock up.txt -difDataInput2 /usr/local/PNetT/tmpData/SOB/Master PPO-LP2045 mock up-table.txt',
'wwwuser 2465 2378 0 12:42 ? 00:00:00 /scripts/GMC/PNetT-5.1-SP1/PNetTCNetClient.bin -o /usr/local/PNetT/tmpData/SOB/workflows/master_ppo_sob_3col.wfd -e PDF -f /usr/local/PNetT/tmpData/SOB/Master PPO-PBARR Test 12-20_temp.pdf -useincluded * -difDataInput1 /usr/local/PNetT/tmpData/SOB/Master PPO-PBARR Test 12-20.txt -difDataInput2 /usr/local/PNetT/tmpData/SOB/Master PPO-PBARR Test 12-20-table.txt',
'wwwuser 19370 7900 0 13:17 ? 00:00:00 /scripts/GMC/PNetT-5.1-SP1/PNetTCNetClient.bin -o /usr/local/PNetT/tmpData/SOB/workflows/master_ppo_sob_3col.wfd -e PDF -f /usr/local/PNetT/tmpData/SOB/Master PPO-LP2069_temp.pdf -useincluded * -difDataInput1 /usr/local/PNetT/tmpData/SOB/Master PPO-LP2069.txt -difDataInput2 /usr/local/PNetT/tmpData/SOB/Master PPO-LP2069-table.txt',
'root 19948 19508 0 13:18 pts/6 00:00:00 grep GMC',
'wwwuser 26685 17061 0 12:25 ? 00:00:00 /scripts/GMC/PNetT-5.1-SP1/PNetTCNetClient.bin -o /usr/local/PNetT/tmpData/SOB/workflows/diff_report.wfd -e PDF -f /usr/local/PNetT/tmpData/SOB/Master PPO-LP2069 Solution PPO 1500-15-20 MOCKUP_differences.pdf -useincluded * -difDataInput1 /usr/local/PNetT/tmpData/SOB/Master PPO-LP2069 Solution PPO 1500-15-20 MOCKUP-report.txt -difDataInput2 /usr/local/PNetT/tmpData/SOB/Master PPO-LP2069 Solution PPO 1500-15-20 MOCKUP-table-report.txt',
'root 32017 1 89 May03 ? 12-18:02:22 /scripts/GMC/PNetT-5.1-SP1/PNetTNetServer.bin -tempdir /usr/local/GMC/PNetT-5.1-SP1/tmpData -D'
);
$this->processes = array();
foreach ($processes as $idx => $process) {
if ($idx > 1) {
$pieces = preg_split("/\s+/", $process, 8);
//skip the grep filter process
if ($pieces[7] != 'grep GMC') {
$this->processes[$pieces[1]] = array(
'user' => $pieces[0],
'processId' => $pieces[1],
'duration' => strtotime($pieces[4]),
'command' => $pieces[7]
);
}
}
}
}
return $this->processes;
}
/**
* Kills a process
*
* @param int $processId The process to be killed
*/
private function _killProcess ($processId) {
//nicely
exec('kill '.$processId);
//if it's still there, this will do the job
if ($this->_ifProcessExists($processId)) {
exec('kill -9 '.$processId);
$this->_addLog("process #".$processId." wasn't nicely killed, I had to force it");
} else {
$this->_addLog("process #".$processId." was nicely killed");
}
}
/**
* Determines if a process is still running
*
* @param int $processId The process to be checked
*/
private function _ifProcessExists ($processId) {
exec('ps '.$processId, $return);
return count($return) > 1;
}
/**
* Adds a message to the log
*/
private function _addLog ($message) {
$this->log[] = $message;
}
}
?>
Answer
Solution:
It is in fact better to solve the problem rather than to work around it. in my case I was dealing with 3rd party code. however phps tick handling function gave me the possibility to pinpoint the correct code part and the problem was right on the php.net homepage as a big fat warning, along with the solution.
It has nothing to do with timeouts but with the behaviour of this function on broken sockets.
Warning If a connection opened by fsockopen() wasn't closed by the server, feof() will hang. To workaround this, see below example:
Example #1 Handling timeouts with feof()
return feof($fp); }
/* Assuming $fp is previously opened by fsockopen() */
$start = NULL; $timeout = ini_get('default_socket_timeout');
while(!safe_feof($fp, $start) && (microtime(true) - $start) < $timeout) { /* Handle */ } ?>
Share solution ↓
Additional Information:
Link To Answer People are also looking for solutions of the problem: filter_sanitize_string deprecated
Didn't find the answer?
Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.
Similar questions
Find the answer in similar questions on our website.
Write quick answer
Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.