/includes/Daemon.class.php

https://github.com/sseshachala/Open-Web-Analytics · PHP · 380 lines · 159 code · 53 blank · 168 comment · 22 complexity · d9d2bc6697acd3b52e9b9009e5a5d227 MD5 · raw file

  1. <?php
  2. /**
  3. * @package binarychoice.system.unix
  4. * @since 1.0.3
  5. */
  6. // Log message levels
  7. define('DLOG_TO_CONSOLE', 1);
  8. define('DLOG_NOTICE', 2);
  9. define('DLOG_WARNING', 4);
  10. define('DLOG_ERROR', 8);
  11. define('DLOG_CRITICAL', 16);
  12. /**
  13. * Daemon base class
  14. *
  15. * Requirements:
  16. * Unix like operating system
  17. * PHP 4 >= 4.3.0 or PHP 5
  18. * PHP compiled with:
  19. * --enable-sigchild
  20. * --enable-pcntl
  21. *
  22. * @package binarychoice.system.unix
  23. * @author Michal 'Seth' Golebiowski <seth at binarychoice dot pl>
  24. * @copyright Copyright 2005 Seth
  25. * @since 1.0.3
  26. */
  27. class Daemon
  28. {
  29. /**#@+
  30. * @access public
  31. */
  32. /**
  33. * User ID
  34. *
  35. * @var int
  36. * @since 1.0
  37. */
  38. var $userID = 99;
  39. /**
  40. * Group ID
  41. *
  42. * @var integer
  43. * @since 1.0
  44. */
  45. var $groupID = 99;
  46. /**
  47. * Terminate daemon when set identity failure ?
  48. *
  49. * @var bool
  50. * @since 1.0.3
  51. */
  52. var $requireSetIdentity = false;
  53. /**
  54. * Path to PID file
  55. *
  56. * @var string
  57. * @since 1.0.1
  58. */
  59. var $pidFileLocation = '/tmp/daemon.pid';
  60. /**
  61. * Home path
  62. *
  63. * @var string
  64. * @since 1.0
  65. */
  66. var $homePath = '/';
  67. /**#@-*/
  68. /**#@+
  69. * @access protected
  70. */
  71. /**
  72. * Current process ID
  73. *
  74. * @var int
  75. * @since 1.0
  76. */
  77. var $_pid = 0;
  78. /**
  79. * Is this process a children
  80. *
  81. * @var boolean
  82. * @since 1.0
  83. */
  84. var $_isChildren = false;
  85. /**
  86. * Is daemon running
  87. *
  88. * @var boolean
  89. * @since 1.0
  90. */
  91. var $_isRunning = false;
  92. /**#@-*/
  93. /**
  94. * Constructor
  95. *
  96. * @access public
  97. * @since 1.0
  98. * @return void
  99. */
  100. function __construct()
  101. {
  102. error_reporting(0);
  103. set_time_limit(0);
  104. ob_implicit_flush();
  105. }
  106. /**
  107. * Starts daemon
  108. *
  109. * @access public
  110. * @since 1.0
  111. * @return bool
  112. */
  113. function start()
  114. {
  115. $this->_logMessage('Starting daemon');
  116. if (!$this->_daemonize())
  117. {
  118. $this->_logMessage('Could not start daemon', DLOG_ERROR);
  119. return false;
  120. }
  121. $this->_logMessage('Running...');
  122. $this->_isRunning = true;
  123. while ($this->_isRunning)
  124. {
  125. $this->_doTask();
  126. }
  127. return true;
  128. }
  129. /**
  130. * Stops daemon
  131. *
  132. * @access public
  133. * @since 1.0
  134. * @return void
  135. */
  136. function stop()
  137. {
  138. $this->_logMessage('Stoping daemon');
  139. $this->_isRunning = false;
  140. }
  141. /**
  142. * Do task
  143. *
  144. * @access protected
  145. * @since 1.0
  146. * @return void
  147. */
  148. function _doTask()
  149. {
  150. // override this method
  151. }
  152. /**
  153. * Logs message
  154. *
  155. * @access protected
  156. * @since 1.0
  157. * @return void
  158. */
  159. function _logMessage($msg, $level = DLOG_NOTICE)
  160. {
  161. // override this method
  162. }
  163. /**
  164. * Daemonize
  165. *
  166. * Several rules or characteristics that most daemons possess:
  167. * 1) Check is daemon already running
  168. * 2) Fork child process
  169. * 3) Sets identity
  170. * 4) Make current process a session laeder
  171. * 5) Write process ID to file
  172. * 6) Change home path
  173. * 7) umask(0)
  174. *
  175. * @access private
  176. * @since 1.0
  177. * @return void
  178. */
  179. function _daemonize()
  180. {
  181. ob_end_flush();
  182. if ($this->_isDaemonRunning())
  183. {
  184. // Deamon is already running. Exiting
  185. return false;
  186. }
  187. if (!$this->_fork())
  188. {
  189. // Coudn't fork. Exiting.
  190. return false;
  191. }
  192. if (!$this->_setIdentity() && $this->requireSetIdentity)
  193. {
  194. // Required identity set failed. Exiting
  195. return false;
  196. }
  197. if (!posix_setsid())
  198. {
  199. $this->_logMessage('Could not make the current process a session leader', DLOG_ERROR);
  200. return false;
  201. }
  202. if (!$fp = @fopen($this->pidFileLocation, 'w'))
  203. {
  204. $this->_logMessage('Could not write to PID file', DLOG_ERROR);
  205. return false;
  206. }
  207. else
  208. {
  209. fputs($fp, $this->_pid);
  210. fclose($fp);
  211. }
  212. @chdir($this->homePath);
  213. umask(0);
  214. declare(ticks = 1);
  215. pcntl_signal(SIGCHLD, array(&$this, 'sigHandler'));
  216. pcntl_signal(SIGTERM, array(&$this, 'sigHandler'));
  217. return true;
  218. }
  219. /**
  220. * Cheks is daemon already running
  221. *
  222. * @access private
  223. * @since 1.0.3
  224. * @return bool
  225. */
  226. function _isDaemonRunning()
  227. {
  228. $oldPid = @file_get_contents($this->pidFileLocation);
  229. if ($oldPid !== false && posix_kill(trim($oldPid),0))
  230. {
  231. $this->_logMessage('Daemon already running with PID: '.$oldPid, (DLOG_TO_CONSOLE | DLOG_ERROR));
  232. return true;
  233. }
  234. else
  235. {
  236. return false;
  237. }
  238. }
  239. /**
  240. * Forks process
  241. *
  242. * @access private
  243. * @since 1.0
  244. * @return bool
  245. */
  246. function _fork()
  247. {
  248. $this->_logMessage('Forking...');
  249. if (!function_exists('pcntl_fork')) {
  250. $this->_logMessage('Forking 2...');
  251. }
  252. $pid = pcntl_fork();
  253. if ($pid == -1) // error
  254. {
  255. $this->_logMessage('Could not fork', DLOG_ERROR);
  256. return false;
  257. }
  258. else if ($pid) // parent
  259. {
  260. $this->_logMessage('Killing parent');
  261. exit();
  262. }
  263. else // children
  264. {
  265. $this->_isChildren = true;
  266. $this->_pid = posix_getpid();
  267. return true;
  268. }
  269. }
  270. /**
  271. * Sets identity of a daemon and returns result
  272. *
  273. * @access private
  274. * @since 1.0
  275. * @return bool
  276. */
  277. function _setIdentity()
  278. {
  279. if (!posix_setgid($this->groupID) || !posix_setuid($this->userID))
  280. {
  281. $this->_logMessage('Could not set identity', DLOG_WARNING);
  282. return false;
  283. }
  284. else
  285. {
  286. return true;
  287. }
  288. }
  289. /**
  290. * Signals handler
  291. *
  292. * @access public
  293. * @since 1.0
  294. * @return void
  295. */
  296. function sigHandler($sigNo)
  297. {
  298. switch ($sigNo)
  299. {
  300. case SIGTERM: // Shutdown
  301. $this->_logMessage('Shutdown signal');
  302. exit();
  303. break;
  304. case SIGCHLD: // Halt
  305. $this->_logMessage('Halt signal');
  306. while (pcntl_waitpid(-1, $status, WNOHANG) > 0);
  307. break;
  308. }
  309. }
  310. /**
  311. * Releases daemon pid file
  312. * This method is called on exit (destructor like)
  313. *
  314. * @access public
  315. * @since 1.0
  316. * @return void
  317. */
  318. function __destruct()
  319. {
  320. if ($this->_isChildren && file_exists($this->pidFileLocation))
  321. {
  322. $this->_logMessage('Releasing daemon');
  323. unlink($this->pidFileLocation);
  324. }
  325. }
  326. }
  327. ?>