Add daemon support

git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@4009 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
This commit is contained in:
2017-12-22 14:03:29 +00:00
parent 63dd033864
commit 2835eafd12

148
daemon.php Normal file
View File

@@ -0,0 +1,148 @@
<?php
require_once ("domframework/fork.php");
require_once ("domframework/file.php");
/** Manage a daemon.
* Allow to start, stop, get status of a callable method
*/
class daemon
{
/** Set the directory to store the PID of the daemon
*/
private $runDir = "/var/run";
/** The constructor check if /proc is mounted
*/
public function __construct ()
{
if (! file_exists ("/proc"))
throw new \Exception ("Can't manage daemon: /proc doesn't exists", 500);
}
/** Get/set the directory to store the PID of the daemon
* @param string|null $val The directory
*/
public function runDir ($val = null)
{
if ($val === null)
return $this->runDir;
if (! is_string ($val))
throw new \Exception ("Can not set daemon runDir: not a string", 500);
$this->runDir = "$val";
}
/** Start the callable method. The terminal is closed. All the displayed
* messages from the child are silently dropped.
* If some parameters are provided, the called child method will receive them
* This function fork and return the child PID
* @param string $name The name of daemon to start.
* @param callable $callable The callback method to use in child
* @param mixed|null $params The params to provide to child method
* @return The child PID
*/
public function start ($name, $callable, $params = array ())
{
$file = new \file ();
if (! $file->is_writeable ($this->runDir))
throw new \Exception (sprintf ("Run Directory '%s' is not writeable",
$this->runDir), 500);
if (! $file->is_readable ($this->runDir))
throw new \Exception (sprintf ("Run Directory '%s' is not readable",
$this->runDir), 500);
if ($file->file_exists ($this->runDir."/$name.pid"))
{
$pid = $file->file_get_contents ($this->runDir."/$name.pid");
if (file_exists ("/proc/$pid"))
throw new \Exception (sprintf (
"Can't start the daemon: already running with PID %d", $pid), 500);
}
$fork = new \fork ();
$pid = $fork->startDetachedChild ($name, $callable, $params);
file_put_contents ($this->runDir."/$name.pid", $pid);
return $pid;
}
/** Stop a $name daemon
* @param string $name The name of daemon to stop. The name must be the same
* as the name used in the start method
* @param integer $maxWaitStop The waiting maximum time after SIGTERM before
* sending SIGKILL
* @param integer $maxWaitKill The waiting maximum time after SIGKILL before
* raising an error
*/
public function stop ($name, $maxWaitStop = 3, $maxWaitKill = 3)
{
$file = new \file ();
if (! $file->is_writeable ($this->runDir))
throw new \Exception (sprintf ("Run Directory '%s' is not writeable",
$this->runDir), 500);
if (! $file->is_readable ($this->runDir))
throw new \Exception (sprintf ("Run Directory '%s' is not readable",
$this->runDir), 500);
if (! $file->file_exists ($this->runDir."/$name.pid"))
throw new \Exception (sprintf (
"PID file %s not found : can't stop the daemon",
$this->runDir."/$name.pid"), 500);
$pid = $file->file_get_contents ($this->runDir."/$name.pid");
if (! file_exists ("/proc/$pid"))
{
$file->unlink ($this->runDir."/$name.pid");
throw new \Exception (
"Can't stop daemon $name. There is no process with the PID $pid",
500);
}
posix_kill ($pid, SIGTERM);
usleep (10000);
$startWait = time ();
while (time () < $startWait + $maxWaitStop)
{
// If the child is zombie and is connected to me, pcntl_waitpid allow it
// to be destroyed to close correctely the parent
pcntl_waitpid ($pid, $status, WNOHANG);
if (! file_exists ("/proc/$pid"))
{
$file->unlink ($this->runDir."/$name.pid");
return true;
}
usleep (100000);
echo ".";
}
posix_kill ($pid, SIGKILL);
$startWait = time ();
while (time () < $startWait + $maxWaitKill)
{
if (! file_exists ("/proc/$pid"))
{
$file->unlink ($this->runDir."/$name.pid");
return true;
}
usleep (100000);
echo "X";
}
throw new \Exception ("Can't stop and can't kill the process $pid", 500);
}
/** Status of a daemon
* @param string $name The name of daemon to have status. The name must be
* the same as the name used in the start method
*/
public function status ($name)
{
$file = new \file ();
if (! $file->is_writeable ($this->runDir))
throw new \Exception (sprintf ("Run Directory '%s' is not writeable",
$this->runDir), 500);
if (! $file->is_readable ($this->runDir))
throw new \Exception (sprintf ("Run Directory '%s' is not readable",
$this->runDir), 500);
if (! $file->file_exists ($this->runDir."/$name.pid"))
return "The daemon is be stopped";
$pid = $file->file_get_contents ($this->runDir."/$name.pid");
if (! file_exists ("/proc/$pid"))
{
$file->unlink ($this->runDir."/$name.pid");
return "The daemon is stopped, but the PID file was remaining. Delete";
}
return "The daemon is running on PID $pid\n";
}
}