. defined('GNUSOCIAL') || die(); class Daemon { public $daemonize = true; public $_id = 'generic'; public function __construct($daemonize = true) { $this->daemonize = $daemonize; } public function name() { return null; } public function get_id() { return $this->_id; } public function set_id($id) { $this->_id = $id; } /** * Reconnect to the database for each child process, * or they'll get very confused trying to use the * same socket. */ protected function resetDb() { global $_DB_DATAOBJECT; // Can't be called statically $user = new User(); $conn = $user->getDatabaseConnection(); $conn->disconnect(); // Remove the disconnected connection from the list foreach ($_DB_DATAOBJECT['CONNECTIONS'] as $k => $v) { if ($v === $conn) { unset($_DB_DATAOBJECT['CONNECTIONS'][$k]); } } // Reconnect main memcached, or threads will stomp on // each other and corrupt their requests. $cache = Cache::instance(); if ($cache) { $cache->reconnect(); } // Also reconnect memcached for status_network table. if (!empty(Status_network::$cache)) { Status_network::$cache->close(); Status_network::$cache = null; } } public function background() { // Database connection will be likely lost after forking. $this->resetDb(); // Double-forking. foreach (['single', 'double'] as $v) { switch (($pid = pcntl_fork())) { case -1: // error common_log(LOG_ERR, 'Could not fork.'); return false; case 0: // child if ($v === 'single') { posix_setsid(); } break; default: // parent if ($v === 'double') { common_log(LOG_INFO, 'Successfully forked.'); } die(); } } return true; } public function alreadyRunning() { $pidfilename = $this->pidFilename(); if (!$pidfilename) { return false; } if (!file_exists($pidfilename)) { return false; } $contents = file_get_contents($pidfilename); if (posix_kill(trim($contents), 0)) { return true; } else { return false; } } public function writePidFile() { $pidfilename = $this->pidFilename(); if (!$pidfilename) { return false; } return file_put_contents($pidfilename, posix_getpid() . "\n"); } public function clearPidFile() { $pidfilename = $this->pidFilename(); if (!$pidfilename) { return false; } return unlink($pidfilename); } public function pidFilename() { $piddir = common_config('daemon', 'piddir'); if (!$piddir) { return null; } $name = $this->name(); if (!$name) { return null; } return $piddir . '/' . $name . '.pid'; } public function changeUser() { $groupname = common_config('daemon', 'group'); if ($groupname) { $group_info = posix_getgrnam($groupname); if (!$group_info) { common_log( LOG_WARNING, 'Ignoring unknown group for daemon: ' . $groupname ); } else { common_log(LOG_INFO, "Setting group to " . $groupname); posix_setgid($group_info['gid']); } } $username = common_config('daemon', 'user'); if ($username) { $user_info = posix_getpwnam($username); if (!$user_info) { common_log( LOG_WARNING, 'Ignoring unknown user for daemon: ' . $username ); } else { common_log(LOG_INFO, "Setting user to " . $username); posix_setuid($user_info['uid']); } } } public function runOnce() { if ($this->alreadyRunning()) { common_log(LOG_INFO, $this->name() . ' already running. Exiting.'); exit(0); } if ($this->daemonize) { common_log(LOG_INFO, 'Backgrounding daemon "'.$this->name().'"'); $this->background(); } $this->writePidFile(); $this->changeUser(); $this->run(); $this->clearPidFile(); } public function run() { return true; } }