From 2835eafd1207dfed90a140778323f94bd10e4e29 Mon Sep 17 00:00:00 2001 From: Dominique Fournier Date: Fri, 22 Dec 2017 14:03:29 +0000 Subject: [PATCH] Add daemon support git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@4009 bf3deb0d-5f1a-0410-827f-c0cc1f45334c --- daemon.php | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 daemon.php diff --git a/daemon.php b/daemon.php new file mode 100644 index 0000000..a73613e --- /dev/null +++ b/daemon.php @@ -0,0 +1,148 @@ +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"; + } +}