I have a cronjob that runs a PHP file that runs a DAEMON written in PHP, but I only want to run the DAEMON if no other instances of it are running, how can I get a list of P
To get the list of PHP processes see this question:
How to get list of running php scripts using PHP exec()?
Another option is that you can acquire a lock of the file and then check it before running: for example:
$thisfilepath = $_SERVER['SCRIPT_FILENAME'];
$thisfilepath = fopen($thisfilepath,'r');
if (!flock($thisfilepath,LOCK_EX | LOCK_NB))
{
customlogfunctionandemail("File is Locked");
exit();
}
elseif(flock($thisfilepath,LOCK_EX | LOCK_NB)) // Acquire Lock
{
// Write your code
// Unlock before finish
flock($thisfilepath,LOCK_UN); // Unlock the file and Exit
customlogfunctionandemail("Process completed. File is unlocked");
exit();
}
Basically in the above example you are first checking if the file is locked or not and if it is not locked (means process is completed) you can acquire lock and begin your code.
Thanks
Here is my full solution to take control over cron with php script which envolves mysql db for configuration and logging. You should find answers to your questions in source code.
Cron_Job class:
class Cron_Job
{
/**
* check if job is still running
* @param int $pid
* @return boolean
*/
public function isJobRunning($pid)
{
try
{
$result = shell_exec(sprintf("ps %d", $pid));
if (count(preg_split("/\n/", $result)) > 2)
{
return true;
}
} catch (Exception $e)
{
}
return false;
}
/**
* deletes job from run stack
* @param int $pid
*/
public function deleteRunningJob($pid)
{
$sql = "delete from croner_running_pids where pid = '$pid'";
$ret = framework_Database::getInstance("****")->delete($sql);
}
/**
* adds job into run stack
* @param int $pid
*/
public function addRunningJob($pid, $outputfile)
{
$sql = "insert into croner_running_pids (pid, `outfile`) values ('$pid', '$outputfile')";
$id = framework_Database::getInstance("****")->insert_db($sql);
}
}
class Cron_Log:
class Cron_Log
{
public static function setRunLog($jobid, $message)
{
$date = date("Y-m-d H:i:s");
$sql = "insert into croner_run_log (jobid, cas, message) values ('$jobid', '$date', '$message')";
framework_Database::getInstance("****")->insert($sql);
}
/**
* sets first run of job log
* @param int $jobid
* @param int $pid
*
* @return $id
*/
public static function setJobLogStart($jobid, $pid)
{
$start = date("Y-m-d H:i:s");
$sql = "insert into croner_log (job_id, start_run, pid) values ('$jobid', '$start', '$pid')";
$id = framework_Database::getInstance("****")->insert_db($sql);
return $id;
}
/**
* finalize log for specified run
* @param int $logid
*/
public static function setJobLogEnd($pid, $endRunTime, $message)
{
$endRunTime = date("Y-m-d H:i:s", $endRunTime);
$message = mysql_real_escape_string($message);
$sql = "update croner_log set end_run = '$endRunTime', output = '$message' where pid = '$pid' and end_run is null";
framework_Database::getInstance("****")->update($sql);
}
}
executing script:
$sql = "select id, runtime, execute_path from croner where runtime is not null and execute_path is not null";
//I am using database wrapper
$ret = framework_Database::getInstance("****")->select($sql);
$cj = new Cron_Job();
//echo date('d.m.Y.N.W H:i:s');
echo "<br>";
if(count($ret['id']) > 0)
{
foreach($ret['id'] as $key=>$id)
{
$runtime = $ret['runtime'][$key];
if(empty($runtime))
continue;
$cmd = $ret['execute_path'][$key];
$outputfile = "/var/www-intranet/croner/outputs/" . $id . "_" . time();
//echo $runtime;
//if pregmatch than get details
if(preg_match($runtime, date('d.m.Y.N.W H:i'), $matches))
{
Cron_Log::setRunLog($id, "Starting job $cmd");
$cmd = sprintf("%s > %s 2>&1 & echo $!", $cmd, $outputfile);
exec($cmd, $pid);
$pid = $pid[0];
//add log that job has started
$cj->addRunningJob($pid, $outputfile);
Cron_Log::setJobLogStart($id, $pid);
usleep(2000);
}
else {
continue;
}
}
}
sleep(1);
//check running pids
$sql = "SELECT * FROM croner_running_pids";
$ret = framework_Database::getInstance("****")->select($sql);
//print_r($ret);
if(isset($ret['pid']) && count($ret['pid']))
{
foreach($ret['pid'] as $key=>$pid)
{
if(is_numeric($pid) && !$cj->isJobRunning($pid) && file_exists($ret['outfile'][$key]))
{ //delete pid from run table
$cj->deleteRunningJob($pid);
$outfile = $ret['outfile'][$key];
$endRunTime = filemtime($outfile);
//echo $endRunTime;
$message = file_get_contents($outfile);
Cron_Log::setJobLogEnd($pid, $endRunTime, $message);
@unlink($outfile);
}
}
}
DB tables:
Croner table:
Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
jobname varchar(250) NO NULL
descr text NO NULL
runtime varchar(150) NO NULL
execute_path text NO NULL
creator int(11) NO NULL
last_run_time timestamp NO CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP
Croner_log table:
Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
job_id int(11) NO NULL
start_run timestamp YES NULL
end_run timestamp YES NULL
pid int(11) NO NULL
output longtext NO NULL
croner_run_log table:
Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
jobid int(11) NO NULL
cas timestamp NO CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP
message varchar(255) NO NULL
croner_running_pids table:
Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
pid int(11) NO NULL
pidfile varchar(250) NO NULL
outfile varchar(250) NO NULL
The idea is, to have configuration in database, where I have stored patterns for preg_match in specific format (date('d.m.Y.N.W H:i:s')). This script is schleduled to run every minute.
Then, I am always selecting all patterns and matching them against result from date function.
If match is found, I will run command stored for that job - put it in background and store its pid into database - separate table where all outputs from jobs are stored.
At the end, I am checking in database for jobs that are marked as running and looking if they are still in process list.