PageRenderTime 71ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/overwatchd/lib/classes/daemon.php

https://code.google.com/p/overwatch/
PHP | 130 lines | 124 code | 0 blank | 6 comment | 32 complexity | 82fb49a2c6974d3280dc9d082916b71b MD5 | raw file
  1. <?php abstract class Daemon implements iExtensionsRequired, iReconfigurable, iSignalHandler {
  2. protected static $Detached = FALSE;
  3. protected static $Sleep = 30;
  4. protected static $Queue = array ();
  5. protected static $Cyclers = array ();
  6. protected static $SigMap = array ();
  7. public static function __GetRequiredExtensions () {return (array ('pcntl', 'posix'));}
  8. public static function __Signals ($Sig = NULL) {
  9. if (NULL === $Sig) return (array (SIGHUP, SIGINT, SIGQUIT, SIGTERM));
  10. switch ($Sig) {
  11. case SIGINT: case SIGQUIT: case SIGTERM:
  12. Out::I (Msg::SigHandled, $Sig);
  13. self::Down ();
  14. break;
  15. case SIGHUP:
  16. Out::I (Msg::SigHandled, $Sig);
  17. self::Push2Q (array (__CLASS__, 'ParseConfig'));
  18. break;
  19. }
  20. }
  21. public static function Detach () {
  22. if (Options::NoFork ()) {
  23. Out::I (Msg::NotForkingOut);
  24. return (NULL);
  25. }
  26. Out::I (Msg::ForkingOut);
  27. switch (pcntl_fork ()) {
  28. default: exit (0);
  29. case -1: Out::W (Msg::ForkFailed); return (NULL);
  30. case 0:
  31. file_put_contents ($PidFile = Options::PidFile (), ($Pid = getmypid ())."\n") or Out::W (Msg::PidSaveError, $Pid, $PidFile);
  32. ini_set ('display_errors', 0);
  33. ini_set ('log_errors', 1);
  34. ini_set ('error_log', 'syslog');
  35. posix_setsid ();
  36. self::$Detached = TRUE;
  37. fclose (STDIN);
  38. fclose (STDOUT);
  39. fclose (STDERR);
  40. }
  41. }
  42. public static function ParseConfig () {
  43. if (is_readable ($Filename = Options::Config ())) {
  44. $Config = parse_ini_file ($Filename, TRUE);
  45. Out::D (Msg::ConfigDump, $Filename, print_r ($Config, TRUE));
  46. foreach ($Config as $Class => $ClassConfig) if (class_exists ($Class) and HasInterface ($Class, 'iReconfigurable')) {
  47. Out::I (Msg::Reconfigure, $Class);
  48. call_user_func (array ($Class, 'Reconfigure'), $ClassConfig);
  49. }
  50. } else Out::W (Msg::ReadFileFail, $Filename);
  51. }
  52. public static function Reconfigure (array $Config) {
  53. if (array_key_exists ('sleep', $Config)) {
  54. Out::I (Msg::ConfigGot, 'sleep', $Sleep = $Config ['sleep']);
  55. if ($Sleep >= 1) {
  56. Out::I (Msg::ConfigApplied, 'sleep', self::$Sleep = $Sleep);
  57. } else Out::W (Msg::BadSleep, $Sleep, self::$Sleep);
  58. }
  59. // try to change user and group
  60. if (array_key_exists ('user', $Config) and strlen ($UID = $Config ['user'])) {
  61. Out::I (Msg::ConfigGot, 'user', $UID);
  62. // determine numbers
  63. $UID = ($UID !== (string) (int) $UID) ? posix_getpwnam ($UID) : posix_getpwuid ((int) $UID);
  64. if (array_key_exists ('group', $Config) and strlen ($GID = $Config ['group'])) {
  65. Out::I (Msg::ConfigGot, 'group', $GID);
  66. $GID = ($GID !== (string) (int) $GID) ? posix_getgrnam ($GID) : posix_getgrgid ((int) $GID);
  67. $GID = $GID ['gid'];
  68. } else $GID = $UID ['gid'];
  69. $UID = $UID ['uid'];
  70. // changing if nessesary
  71. if ($GID !== ($CurGID = posix_getgid ())) {
  72. if (posix_setgid ($GID)) {
  73. Out::I (Msg::ChangedID, 'GID', $GID);
  74. } else Out::W (Msg::ChangeIDFailed, 'GID', $GID, $CurGID);
  75. } else Out::I (Msg::NotChangingID, 'GID', $GID);
  76. if ($UID !== ($CurUID = posix_getuid ())) {
  77. if (posix_setuid ($UID)) {
  78. Out::I (Msg::ChangedID, 'UID', $UID);
  79. } else Out::W (Msg::ChangeIDFailed, 'UID', $UID, $CurUID);
  80. } else Out::I (Msg::NotChangingID, 'UID', $UID);
  81. }
  82. }
  83. public static function Detached () {return (self::$Detached);}
  84. public static function Push2Q ($Callback, $Args = NULL) {
  85. if (! is_callable ($Callback)) return (FALSE);
  86. $Callback = array ('cb' => $Callback);
  87. $Callback ['args'] = (NULL === $Args) ? array () : $Args;
  88. is_array ($Callback ['args']) or $Callback ['args'] = array ($Callback ['args']);
  89. array_push (self::$Queue, $Callback);
  90. }
  91. public static function SigHandler ($Sig = NULL) {
  92. if (NULL === $Sig) {
  93. foreach (get_declared_classes () as $Class) if (HasInterface ($Class, 'iSignalHandler')) {
  94. foreach (call_user_func (array ($Class, '__Signals')) as $Sig) self::$SigMap [$Sig][] = $Class;
  95. }
  96. Out::I (Msg::SigInstall);
  97. foreach (array_keys (self::$SigMap) as $Sig) pcntl_signal ($Sig, array (__CLASS__, __FUNCTION__));
  98. return (TRUE);
  99. }
  100. Out::I (Msg::SigCaught, $Sig);
  101. if (array_key_exists ($Sig, self::$SigMap)) {
  102. foreach (self::$SigMap [$Sig] as $Class) {
  103. if (is_callable ($Callback = array ($Class, '__Signals'))) {
  104. Out::I (Msg::Callback, implode ('::', $Callback));
  105. call_user_func ($Callback, $Sig);
  106. } else Out::W (Msg::Uncallable, implode ('::', $Callback));
  107. }
  108. } else Out::W (Msg::SigUnknown, $Sig);
  109. }
  110. public static function Main () {
  111. Out::I (Msg::MainLoop);
  112. foreach (get_declared_classes () as $Class) if (HasInterface ($Class, 'iCycler')) array_push (self::$Cyclers, $Class);
  113. while (TRUE) {
  114. for ($i = 1; $i < self::$Sleep; $i += 1) sleep (1); // this quantisation is needed so that any signal handled will not break the whole sleep
  115. // run filters
  116. if (is_array ($Lines = DB::Fetch ())) foreach ($Lines as $Line) Filter::Examine ($Line);
  117. Filter::Commit ();
  118. // process queue
  119. foreach (self::$Queue as $Callback) call_user_func_array ($Callback ['cb'], $Callback ['args']);
  120. self::$Queue = array ();
  121. // process cyclers
  122. foreach (self::$Cyclers as $Cycler) call_user_func (array ($Cycler, 'Cycle'));
  123. }
  124. }
  125. public static function Down () {
  126. Out::I (Msg::ExitingClean);
  127. self::$Detached and @unlink (Options::PidFile ());
  128. exit (0);
  129. }
  130. } ?>