Eduardo Ponce de Leon
Eduardo Ponce de Leon

Reputation: 9696

How to get list of PHP processes running on server with PHP

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 PHP processes running in order to find if my DAEMON is running. I thought about some kind of exec that will generate a list that I can store in an array. Any ideas? thanks

Upvotes: 4

Views: 13629

Answers (2)

Peter Pivarc
Peter Pivarc

Reputation: 469

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.

Upvotes: 0

Umair Khan
Umair Khan

Reputation: 473

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

Upvotes: 4

Related Questions