10 defined(
'JPATH_PLATFORM') or die;
12 jimport('joomla.filesystem.folder');
30 protected static $signals = array(
73 protected $exiting =
false;
79 protected $parentId = 0;
85 protected $processId = 0;
91 protected $running =
false;
114 if (!defined(
'SIGHUP'))
117 throw new RuntimeException(
'The PCNTL extension for PHP is not available.');
121 if (!function_exists(
'posix_getpid'))
124 throw new RuntimeException(
'The POSIX extension for PHP is not available.');
129 parent::__construct($input, $config, $dispatcher);
132 @set_time_limit($this->config->get(
'max_execution_time', 0));
134 if ($this->config->get(
'max_memory_limit') !== null)
136 ini_set(
'memory_limit', $this->config->get(
'max_memory_limit',
'256M'));
154 public static function signal($signal)
160 if (!is_subclass_of(static::$instance,
'JApplicationDaemon'))
163 throw new RuntimeException(
'Cannot find the application instance.');
167 static::$instance->triggerEvent(
'onReceiveSignal', array($signal));
174 if (static::$instance->running && static::$instance->isActive())
176 static::$instance->shutdown();
180 static::$instance->close();
185 if (static::$instance->running && static::$instance->isActive())
187 static::$instance->shutdown(
true);
191 static::$instance->close();
196 while (static::$instance->pcntlWait($signal, WNOHANG || WUNTRACED) > 0)
202 while (static::$instance->pcntlWait($signal, WNOHANG) > 0)
204 $signal = static::$instance->pcntlChildExitStatus($signal);
220 public function isActive()
223 $pidFile = $this->config->get(
'application_pid_file');
226 if (!is_file($pidFile))
232 $fp = fopen($pidFile,
'r');
233 $pid = fread($fp, filesize($pidFile));
244 if (!posix_kill($pid, 0))
265 public function loadConfiguration($data)
268 parent::loadConfiguration($data);
277 $tmp = (string) $this->config->get(
'author_name',
'Joomla Platform');
278 $this->config->set(
'author_name', (strlen($tmp) > 50) ? substr($tmp, 0, 50) : $tmp);
281 $tmp = (string) $this->config->get(
'author_email',
'admin@joomla.org');
282 $this->config->set(
'author_email', filter_var($tmp, FILTER_VALIDATE_EMAIL));
285 $tmp = (string) $this->config->get(
'application_name',
'JApplicationDaemon');
286 $this->config->set(
'application_name', (
string) preg_replace(
'/[^A-Z0-9_-]/i',
'', $tmp));
289 $tmp = (string) $this->config->get(
'application_description',
'A generic Joomla Platform application.');
290 $this->config->set(
'application_description', filter_var($tmp, FILTER_SANITIZE_STRING));
298 $tmp = (string) $this->config->get(
'application_executable', basename($this->input->executable));
299 $this->config->set(
'application_executable', $tmp);
302 $tmp = (string) $this->config->get(
'application_directory', dirname($this->input->executable));
303 $this->config->set(
'application_directory', $tmp);
306 $name = $this->config->get(
'application_name');
307 $tmp = (string) $this->config->get(
'application_pid_file', strtolower(
'/tmp/' . $name .
'/' . $name .
'.pid'));
308 $this->config->set(
'application_pid_file', $tmp);
317 $tmp = (int) $this->config->get(
'application_uid', 0);
318 $options = array(
'options' => array(
'min_range' => 0,
'max_range' => 65000));
319 $this->config->set(
'application_uid', filter_var($tmp, FILTER_VALIDATE_INT, $options));
322 $tmp = (int) $this->config->get(
'application_gid', 0);
323 $options = array(
'options' => array(
'min_range' => 0,
'max_range' => 65000));
324 $this->config->set(
'application_gid', filter_var($tmp, FILTER_VALIDATE_INT, $options));
327 $tmp = (bool) $this->config->get(
'application_require_identity', 1);
328 $this->config->set(
'application_require_identity', $tmp);
338 $tmp = $this->config->get(
'max_execution_time');
342 $this->config->set(
'max_execution_time', (
int) $tmp);
346 $tmp = $this->config->get(
'max_memory_limit',
'256M');
350 $this->config->set(
'max_memory_limit', (
string) $tmp);
363 public function execute()
366 $this->triggerEvent(
'onBeforeExecute');
374 if ($this->daemonize())
400 $this->triggerEvent(
'onAfterExecute');
411 public function restart()
414 $this->shutdown(
true);
425 public function stop()
439 protected function changeIdentity()
442 $uid = (int) $this->config->get(
'application_uid', 0);
443 $gid = (int) $this->config->get(
'application_gid', 0);
446 $file = $this->config->get(
'application_pid_file');
449 if ($uid && (fileowner($file) != $uid) && (!@ chown($file, $uid)))
457 if ($gid && (filegroup($file) != $gid) && (!@ chgrp($file, $gid)))
465 if ($uid && ($info = posix_getpwuid($uid)) && is_dir($info[
'dir']))
467 system(
'export HOME="' . $info[
'dir'] .
'"');
471 if ($uid && (posix_getuid($file) != $uid) && (!@ posix_setuid($uid)))
479 if ($gid && (posix_getgid($file) != $gid) && (!@ posix_setgid($gid)))
487 $user = posix_getpwuid($uid);
488 $group = posix_getgrgid($gid);
490 JLog::add(
'Changed daemon identity to ' . $user[
'name'] .
':' . $group[
'name'],
JLog::INFO);
503 protected function daemonize()
506 if ($this->isActive())
514 $this->safeMode = !!@ ini_get(
'safe_mode');
515 $this->processId = 0;
516 $this->running =
false;
522 if (!$this->input->get(
'f'))
530 $this->exiting =
false;
531 $this->running =
true;
534 $this->processId = (int) posix_getpid();
535 $this->parentId = $this->processId;
538 catch (RuntimeException $e)
546 if ($this->processId < 1)
557 if (!$this->writeProcessIdFile())
565 if (!$this->changeIdentity())
569 if ($this->config->get(
'application_require_identity'))
582 if (!$this->setupSignalHandlers())
588 @ chdir($this->config->get(
'application_directory'));
602 protected function detach()
607 $pid = $this->fork();
620 $this->exiting =
false;
621 $this->running =
true;
624 $this->parentId = $this->processId;
636 protected function fork()
639 $pid = $this->pcntlFork();
644 throw new RuntimeException(
'The process could not be forked.');
649 $this->processId = (int) posix_getpid();
673 protected function gc()
692 protected function setupSignalHandlers()
695 foreach (self::$signals as $signal)
698 if (!defined($signal) || !is_int(constant($signal)) || (constant($signal) === 0))
702 define($signal, null);
709 if (!$this->pcntlSignal(constant($signal), array(
'JApplicationDaemon',
'signal')))
729 protected function shutdown($restart =
false)
739 $this->exiting =
true;
743 if (!$this->running && !$this->isActive())
750 if ($this->parentId == $this->processId)
753 $fp = fopen($this->config->get(
'application_pid_file'),
'r');
754 $pid = fread($fp, filesize($this->config->get(
'application_pid_file')));
759 @ unlink($this->config->get(
'application_pid_file'));
764 $this->close(exec(implode(
' ',
$GLOBALS[
'argv']) .
' > /dev/null &'));
769 passthru(
'kill -9 ' . $pid);
782 protected function writeProcessIdFile()
785 if ($this->processId < 1)
793 $file = $this->config->get(
'application_pid_file');
803 $folder = dirname($file);
813 if (!file_put_contents($file, $this->processId))
821 if (!chmod($file, 0644))
838 protected function postFork()
841 $this->triggerEvent(
'onFork');
855 protected function pcntlChildExitStatus($status)
857 return pcntl_wexitstatus($status);
872 protected function pcntlFork()
892 protected function pcntlSignal($signal , $handler, $restart =
true)
894 return pcntl_signal($signal, $handler, $restart);
911 protected function pcntlWait(&$status, $options = 0)
913 return pcntl_wait($status, $options);