PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/daemon.php

https://bitbucket.org/shamanis/daemon-php
PHP | 234 lines | 134 code | 21 blank | 79 comment | 25 complexity | a146e44e90060ab07d95059c38e14da1 MD5 | raw file
  1. <?php
  2. /*
  3. Author: Petr Bondarenko
  4. E-mail: public@shamanis.com
  5. Date: 31 May 2012
  6. License: BSD
  7. Description: Class for create UNIX-daemon
  8. */
  9. class DaemonException extends Exception {}
  10. /**
  11. * Класс для реализации UNIX-демонов
  12. * @author Petr Bondarenko
  13. * @copyright Petr Bondarenko 2012
  14. * @version 1.0
  15. * @abstract
  16. */
  17. abstract class DaemonPHP {
  18. protected $_baseDir;
  19. protected $_chrootDir = null;
  20. protected $_pid;
  21. protected $_log;
  22. protected $_err;
  23. /**
  24. * Конструктор класса. Принимает путь к pid-файлу
  25. * @param string $path Абсолютный путь к PID-файлу
  26. * @access public
  27. */
  28. public function __construct($path=null) {
  29. $this->_baseDir = dirname(__FILE__);
  30. $this->_log = $this->_baseDir . '/daemon-php.log';
  31. $this->_err = $this->_baseDir . '/daemon-php.err';
  32. if ($path === null) {
  33. $this->_pid = $this->_baseDir . '/daemon-php.pid';
  34. } else {
  35. $this->_pid = $path;
  36. }
  37. }
  38. /**
  39. * Метод устанавливает путь log-файла
  40. * @param string $path Абсолютный путь к log-файлу
  41. * @return DaemonPHP
  42. * @access public
  43. */
  44. final public function setLog($path) {
  45. $this->_log = $path;
  46. return $this;
  47. }
  48. /**
  49. * Метод устанавливает путь err-файла
  50. * @param string $path Абсолютный путь к err-файлу
  51. * @return DaemonPHP
  52. * @access public
  53. */
  54. final public function setErr($path) {
  55. $this->_err = $path;
  56. return $this;
  57. }
  58. /**
  59. * Метод позволяет установить директорию,
  60. * в которую будет выполнен chroot после старта демона.
  61. * Данный метод служит для решения проблем безопасности.
  62. * @param string $path Абсолютный путь chroot-директории
  63. * @throws DaemonException
  64. * @access public
  65. */
  66. final public function setChroot($path) {
  67. if (!function_exists('chroot')) {
  68. throw new DaemonException('Function chroot() has no. Please update you PHP version.');
  69. }
  70. $this->_chrootDir = $path;
  71. return $this;
  72. }
  73. /**
  74. * Метод выполняет демонизацию процесса, через double fork
  75. * @throws DaemonException
  76. * @access protected
  77. */
  78. final protected function demonize() {
  79. $pid = pcntl_fork();
  80. if ($pid == -1) {
  81. throw new DaemonException('Not fork process!');
  82. } else if ($pid) {
  83. exit(0);
  84. }
  85. posix_setsid();
  86. chdir('/');
  87. $pid = pcntl_fork();
  88. if ($pid == -1) {
  89. throw new DaemonException('Not double fork process!');
  90. } else if ($pid) {
  91. $fpid = fopen($this->_pid, 'wb');
  92. fwrite($fpid, $pid);
  93. fclose($fpid);
  94. exit(0);
  95. }
  96. posix_setsid();
  97. chdir('/');
  98. ini_set('error_log', $this->_baseDir . '/php_error.log');
  99. fclose(STDIN);
  100. fclose(STDOUT);
  101. fclose(STDERR);
  102. $STDIN = fopen('/dev/null', 'r');
  103. if ($this->_chrootDir !== null) {
  104. chroot($this->_chrootDir);
  105. }
  106. $STDOUT = fopen($this->_log, 'ab');
  107. if (!is_writable($this->_log))
  108. throw new DaemonException('LOG-file is not writable!');
  109. $STDERR = fopen($this->_err, 'ab');
  110. if (!is_writable($this->_err))
  111. throw new DaemonException('ERR-file is not writable!');
  112. $this->run();
  113. }
  114. /**
  115. * Метод возвращает PID процесса
  116. * @return int PID процесса либо 0
  117. * @access protected
  118. */
  119. final protected function getPID() {
  120. if (file_exists($this->_pid)) {
  121. $pid = (int) file_get_contents($this->_pid);
  122. if (posix_kill($pid, SIG_DFL)) {
  123. return $pid;
  124. } else {
  125. //Если демон не откликается, а PID-файл существует
  126. unlink($this->_pid);
  127. return 0;
  128. }
  129. } else {
  130. return 0;
  131. }
  132. }
  133. /**
  134. * Метод стартует работу и вызывает метод demonize()
  135. * @access public
  136. */
  137. final public function start() {
  138. if (($pid = $this->getPID()) > 0) {
  139. echo "Process is running on PID: " . $pid . PHP_EOL;
  140. } else {
  141. echo "Starting..." . PHP_EOL;
  142. $this->demonize();
  143. }
  144. }
  145. /**
  146. * Метод останавливает демон
  147. * @access public
  148. */
  149. final public function stop() {
  150. if (($pid = $this->getPID()) > 0) {
  151. echo "Stopping ... ";
  152. posix_kill($pid, SIGTERM);
  153. unlink($this->_pid);
  154. echo "OK" . PHP_EOL;
  155. } else {
  156. echo "Process not running!" . PHP_EOL;
  157. }
  158. }
  159. /**
  160. * Метод рестартует демон последовательно вызвав stop() и start()
  161. * @see start()
  162. * @see stop()
  163. * @access public
  164. */
  165. final public function restart() {
  166. $this->stop();
  167. $this->start();
  168. }
  169. /**
  170. * Метод проверяет работу демона
  171. * @access public
  172. */
  173. final public function status() {
  174. if (($pid = $this->getPID()) > 0) {
  175. echo "Process is running on PID: " . $pid . PHP_EOL;
  176. } else {
  177. echo "Process not running!" . PHP_EOL;
  178. }
  179. }
  180. /**
  181. * Метод обрабатывает аргументы командной строки
  182. * @param array $argv Массив с аргументами коммандной строки
  183. * @access public
  184. */
  185. final public function handle($argv) {
  186. switch ($argv[1]) {
  187. case 'start':
  188. $this->start();
  189. break;
  190. case 'stop':
  191. $this->stop();
  192. break;
  193. case 'restart':
  194. $this->restart();
  195. break;
  196. case 'status':
  197. $this->status();
  198. break;
  199. default:
  200. echo "Unknown command!" . PHP_EOL .
  201. "Use: " . $argv[0] . " start|stop|restart|status" . PHP_EOL;
  202. break;
  203. }
  204. }
  205. /**
  206. * Основной класс демона, в котором выполняется работа.
  207. * Его необходимо переопределить
  208. * @access public
  209. * @abstract
  210. */
  211. abstract public function run();
  212. }
  213. ?>