Here is my complete solution to take control of cron with a php script that envolves mysql db for configuration and logging. You should find the answers to your questions in the source code.
Cron_Job Class:
class Cron_Job { 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; } public function deleteRunningJob($pid) { $sql = "delete from croner_running_pids where pid = '$pid'"; $ret = framework_Database::getInstance("****")->delete($sql); } 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("Ymd H:i:s"); $sql = "insert into croner_run_log (jobid, cas, message) values ('$jobid', '$date', '$message')"; framework_Database::getInstance("****")->insert($sql); } public static function setJobLogStart($jobid, $pid) { $start = date("Ymd 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; } public static function setJobLogEnd($pid, $endRunTime, $message) { $endRunTime = date("Ymd 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); } }
Script execution:
$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('dmYNW 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('dmYNW 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 the configuration in the database, where I saved the templates for preg_match in a specific format (date ('dmYNW H: i: s')). This script runs every minute.
Then I always select all the templates and compare them with the results from the date function.
If a match is found, I launched the command saved for this task - put it in the background and save its pid in the database - a separate table that stores all the output from the tasks.
In the end, I check the database for jobs that are marked as running and looking for whether they are in the process list.