34

As a way to build a poor-man's watchdog and make sure an application is restarted in case it crashes (until I figure out why), I need to write a PHP CLI script that will be run by cron every 5mn to check whether the process is still running.

Based on this page, I tried the following code, but it always returns True even if I call it with bogus data:

function processExists($file = false) {
    $exists= false;
    $file= $file ? $file : __FILE__;

    // Check if file is in process list
    exec("ps -C $file -o pid=", $pids);
    if (count($pids) > 1) {
    $exists = true;
    }
    return $exists;
}

#if(processExists("lighttpd"))
if(processExists("dummy"))
    print("Exists\n")
else
    print("Doesn't exist\n");

Next, I tried this code...

(exec("ps -A | grep -i 'lighttpd -D' | grep -v grep", $output);)
print $output;

... but don't get what I expect:

/tmp> ./mycron.phpcli 
Arrayroot:/tmp> 

FWIW, this script is run with the CLI version of PHP 5.2.5, and the OS is uClinux 2.6.19.3.

Thank you for any hint.


Edit: This seems to work fine

exec("ps aux | grep -i 'lighttpd -D' | grep -v grep", $pids);
if(empty($pids)) {
        print "Lighttpd not running!\n";
} else {
        print "Lighttpd OK\n";
}
kenorb
  • 155,785
  • 88
  • 678
  • 743
Gulbahar
  • 5,343
  • 20
  • 70
  • 93

9 Answers9

45

If you're doing it in php, why not use php code:

In the running program:

define('PIDFILE', '/var/run/myfile.pid');

file_put_contents(PIDFILE, posix_getpid());
function removePidFile() {
    unlink(PIDFILE);
}
register_shutdown_function('removePidFile');   

Then, in the watchdog program, all you need to do is:

function isProcessRunning($pidFile = '/var/run/myfile.pid') {
    if (!file_exists($pidFile) || !is_file($pidFile)) return false;
    $pid = file_get_contents($pidFile);
    return posix_kill($pid, 0);
}

Basically, posix_kill has a special signal 0 that doesn't actually send a signal to the process, but it does check to see if a signal can be sent (the process is actually running).

And yes, I do use this quite often when I need long running (or at least watchable) php processes. Typically I write init scripts to start the PHP program, and then have a cron watchdog to check hourly to see if it's running (and if not restart it)...

ircmaxell
  • 163,128
  • 34
  • 264
  • 314
  • Thanks for this awesome answer ircmaxell. What could be the difference between !file_exists($pidFile) and !is_file($pidFile) in this case? – RafaSashi Apr 01 '15 at 11:53
  • 1
    @RafaSashi: "file_exists — Checks whether a file or directory exists" ([link](http://us1.php.net/manual/en/function.file-exists.php)). So the if checks both that it exists and that it is a file. – eugen_nw Aug 21 '15 at 23:25
  • 1
    What would happen if the script being watched shut down abnormally such that `removePidFile()` didn't run, and then another process on the system was started that was assigned the same PID? It seems your watchdog script would consider that your script being watched was still running. Maybe there's a way to not only verify that a script with PID X is running, but that it's actually the process that you want to be watching. – Nick Coons Oct 24 '18 at 06:46
  • I ran the script under CLI and couldn't get file_put_contents() to create the pid file: `failed to open stream: Permission denied` – The Onin Jan 22 '19 at 05:12
29

I'd use pgrep to do this (caution, untested code):


exec("pgrep lighttpd", $pids);
if(empty($pids)) {

    // lighttpd is not running!
}

I have a bash script that does something similar (but with SSH tunnels):


#!/bin/sh

MYSQL_TUNNEL="ssh -f -N -L 33060:127.0.0.1:3306 tunnel@db"
RSYNC_TUNNEL="ssh -f -N -L 8730:127.0.0.1:873 tunnel@db"

# MYSQL
if [ -z `pgrep -f -x "$MYSQL_TUNNEL"` ] 
then
    echo Creating tunnel for MySQL.
    $MYSQL_TUNNEL
fi

# RSYNC
if [ -z `pgrep -f -x "$RSYNC_TUNNEL"` ]
then
    echo Creating tunnel for rsync.
    $RSYNC_TUNNEL
fi


You could alter this script with the commands that you want to monitor.

mmattax
  • 27,172
  • 41
  • 116
  • 149
  • Thanks everyone. Calling exec() with grep (pgrep isn't available on the embedded device I'm running this) seems to do the job. – Gulbahar Jun 24 '10 at 22:56
  • 5
    Thought i would share a quick comment here.. Tested `pgrep` with `exec` as shown, It appears that the process of exec will quickly create another process...which has the same string in it. So it always returns a single ID.. thinking perhaps using something like `if count = 2`... So far using a count=2 works.. But just a word of caution here... – Angry 84 Aug 01 '15 at 00:41
  • I'm using php 7.1 and Amazon Linux and do not have a duplicated process. – dtbarne Oct 19 '18 at 21:54
14

You can try this, which combines bits of those two approaches:

function processExists($processName) {
    $exists= false;
    exec("ps -A | grep -i $processName | grep -v grep", $pids);
    if (count($pids) > 0) {
        $exists = true;
    }
    return $exists;
}

If that doesn't work, you may want to just try running the ps command on your system and seeing what output it gives.

John Lawrence
  • 2,923
  • 17
  • 23
8

Try this one

function processExists ($pid) {
    return file_exists("/proc/{$pid}");
}

Function checks whether process file is exists in /proc/ root directory. Works for Linux only

Ethan
  • 4,295
  • 4
  • 25
  • 44
Anatoly Rugalev
  • 1,088
  • 9
  • 11
  • 2
    To clarify, this will only necessarily work for Linux. There is no guarantee that other UNIX boxes will have a /proc/ directory – Doug Apr 03 '16 at 12:52
1
<?php

function check_if_process_is_running($process)
{
    exec("/bin/pidof $process",$response);
    if ($response)
    {
         return true;
    } else
    {
         return false;
    }
}

if (check_if_process_is_running("mysqld"))
{
      echo "MySQL is running";
} else
{
      echo "Mysql stopped";
}

?>
Softmixt
  • 1,658
  • 20
  • 20
1

I didn't see this mentioned here, but here's another approach taking the second grep out of the equation, i use this with alot of my PHP scripts and should work universally

exec("ps aux | grep -i '[l]ighttpd -D'", $pids);
if(empty($pids)) {
        print "Lighttpd not running!\n";
} else {
        print "Lighttpd OK\n";
}

Enjoy.

Destreyf
  • 441
  • 5
  • 7
1

The main problem is the if you run a php script, the exec command will be run as the web-servers user (www-data); this user can't see pid's from other users, unless you use "pidof"

<?php
//##########################################
// desc: Diese PHP Script zeig euch ob ein Prozess läuft oder nicht
// autor: seevenup
// version: 1.3
// info: Da das exec kommando als apache user (www-data) ausgefuert
//       wird, muss pidof benutzt werden da es prozesse von
//       anderen usern anzeigen kann
//##########################################

if (!function_exists('server_status')) {
        function server_status($string,$name) {
                $pid=exec("pidof $name");
                exec("ps -p $pid", $output);

                if (count($output) > 1) {
                        echo "$string: <font color='green'><b>RUNNING</b></font><br>";
                }
                else {
                        echo "$string: <font color='red'><b>DOWN</b></font><br>";
                }
        }
}

//Beispiel "Text zum anzeigen", "Prozess Name auf dem Server"
server_status("Running With Rifles","rwr_server");
server_status("Starbound","starbound_server");
server_status("Minecraft","minecarf");
?>

More information about the script here http://umbru.ch/?p=328

Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
0

i have a function to get the pid of a process...

function getRunningPid($processName) {
    $pid = 0;
    $processes = array();
    $command = 'ps ax | grep '.$processName;
    exec($command, $processes);
    foreach ($processes as $processString) {
        $processArr = explode(' ', trim($processString));
            if (
            (intval($processArr[0]) != getmypid())&&
            (strpos($processString, 'grep '.$processName) === false)
        ) {
            $pid = intval($processArr[0]);
        }
    }
    return $pid;
}
Sergey Eremin
  • 10,994
  • 2
  • 38
  • 44
-1

To check whether process is running by its name, you can use pgrep, e.g.

$is_running = shell_exec("pgrep -f lighttpd");

or:

exec("pgrep lighttpd", $output, $return);
if ($return == 0) {
    echo "Ok, process is running\n";
}

as per this post.

If you know the PID of the process, you can use one the following functions:

  /**
   * Checks whether the process is running.
   *
   * @param int $pid Process PID.
   * @return bool
   */
  public static function isProcessRunning($pid) {
    // Calling with 0 kill signal will return true if process is running.
    return posix_kill((int) $pid, 0);
  }

  /**
   * Get the command of the process.
   * For example apache2 in case that's the Apache process.
   *
   * @param int $pid Process PID.
   * @return string
   */
  public static function getProcessCommand($pid) {
    $pid = (int) $pid;
    return trim(shell_exec("ps o comm= $pid"));
  }

Related: How to check whether specified PID is currently running without invoking ps from PHP?

kenorb
  • 155,785
  • 88
  • 678
  • 743
  • `$return` returns `0` regardless if the process is running. Rather, you should be looking at `$output` which returns an array containing process id's. it's important to note that `exec()` will generate its own process, and include itself in that array, meaning it will always return at least 1 process id. so a `count()` greater than 2, means the process you are looking for, is actually running. – Crayons Feb 21 '19 at 10:02