daemon.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <?php
  2. // This file is part of GNU social - https://www.gnu.org/software/social
  3. //
  4. // GNU social is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // GNU social is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
  16. defined('GNUSOCIAL') || die();
  17. class Daemon
  18. {
  19. public $daemonize = true;
  20. public $_id = 'generic';
  21. public function __construct($daemonize = true)
  22. {
  23. $this->daemonize = $daemonize;
  24. }
  25. public function name()
  26. {
  27. return null;
  28. }
  29. public function get_id()
  30. {
  31. return $this->_id;
  32. }
  33. public function set_id($id)
  34. {
  35. $this->_id = $id;
  36. }
  37. /**
  38. * Reconnect to the database for each child process,
  39. * or they'll get very confused trying to use the
  40. * same socket.
  41. */
  42. protected function resetDb()
  43. {
  44. global $_DB_DATAOBJECT;
  45. // Can't be called statically
  46. $user = new User();
  47. $conn = $user->getDatabaseConnection();
  48. $conn->disconnect();
  49. // Remove the disconnected connection from the list
  50. foreach ($_DB_DATAOBJECT['CONNECTIONS'] as $k => $v) {
  51. if ($v === $conn) {
  52. unset($_DB_DATAOBJECT['CONNECTIONS'][$k]);
  53. }
  54. }
  55. // Reconnect main memcached, or threads will stomp on
  56. // each other and corrupt their requests.
  57. $cache = Cache::instance();
  58. if ($cache) {
  59. $cache->reconnect();
  60. }
  61. // Also reconnect memcached for status_network table.
  62. if (!empty(Status_network::$cache)) {
  63. Status_network::$cache->close();
  64. Status_network::$cache = null;
  65. }
  66. }
  67. public function background()
  68. {
  69. // Database connection will be likely lost after forking.
  70. $this->resetDb();
  71. // Double-forking.
  72. foreach (['single', 'double'] as $v) {
  73. switch (($pid = pcntl_fork())) {
  74. case -1: // error
  75. common_log(LOG_ERR, 'Could not fork.');
  76. return false;
  77. case 0: // child
  78. if ($v === 'single') {
  79. posix_setsid();
  80. }
  81. break;
  82. default: // parent
  83. if ($v === 'double') {
  84. common_log(LOG_INFO, 'Successfully forked.');
  85. }
  86. die();
  87. }
  88. }
  89. return true;
  90. }
  91. public function alreadyRunning()
  92. {
  93. $pidfilename = $this->pidFilename();
  94. if (!$pidfilename) {
  95. return false;
  96. }
  97. if (!file_exists($pidfilename)) {
  98. return false;
  99. }
  100. $contents = file_get_contents($pidfilename);
  101. if (posix_kill(trim($contents), 0)) {
  102. return true;
  103. } else {
  104. return false;
  105. }
  106. }
  107. public function writePidFile()
  108. {
  109. $pidfilename = $this->pidFilename();
  110. if (!$pidfilename) {
  111. return false;
  112. }
  113. return file_put_contents($pidfilename, posix_getpid() . "\n");
  114. }
  115. public function clearPidFile()
  116. {
  117. $pidfilename = $this->pidFilename();
  118. if (!$pidfilename) {
  119. return false;
  120. }
  121. return unlink($pidfilename);
  122. }
  123. public function pidFilename()
  124. {
  125. $piddir = common_config('daemon', 'piddir');
  126. if (!$piddir) {
  127. return null;
  128. }
  129. $name = $this->name();
  130. if (!$name) {
  131. return null;
  132. }
  133. return $piddir . '/' . $name . '.pid';
  134. }
  135. public function changeUser()
  136. {
  137. $groupname = common_config('daemon', 'group');
  138. if ($groupname) {
  139. $group_info = posix_getgrnam($groupname);
  140. if (!$group_info) {
  141. common_log(
  142. LOG_WARNING,
  143. 'Ignoring unknown group for daemon: ' . $groupname
  144. );
  145. } else {
  146. common_log(LOG_INFO, "Setting group to " . $groupname);
  147. posix_setgid($group_info['gid']);
  148. }
  149. }
  150. $username = common_config('daemon', 'user');
  151. if ($username) {
  152. $user_info = posix_getpwnam($username);
  153. if (!$user_info) {
  154. common_log(
  155. LOG_WARNING,
  156. 'Ignoring unknown user for daemon: ' . $username
  157. );
  158. } else {
  159. common_log(LOG_INFO, "Setting user to " . $username);
  160. posix_setuid($user_info['uid']);
  161. }
  162. }
  163. }
  164. public function runOnce()
  165. {
  166. if ($this->alreadyRunning()) {
  167. common_log(LOG_INFO, $this->name() . ' already running. Exiting.');
  168. exit(0);
  169. }
  170. if ($this->daemonize) {
  171. common_log(LOG_INFO, 'Backgrounding daemon "'.$this->name().'"');
  172. $this->background();
  173. }
  174. $this->writePidFile();
  175. $this->changeUser();
  176. $this->run();
  177. $this->clearPidFile();
  178. }
  179. public function run()
  180. {
  181. return true;
  182. }
  183. }