Source for file daemon.php

Documentation is available at daemon.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Platform
  4.  * @subpackage  Application
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM') or die;
  11.  
  12. jimport('joomla.filesystem.folder');
  13.  
  14. /**
  15.  * Class to turn JCli applications into daemons.  It requires CLI and PCNTL support built into PHP.
  16.  *
  17.  * @package     Joomla.Platform
  18.  * @subpackage  Application
  19.  * @see         http://www.php.net/manual/en/book.pcntl.php
  20.  * @see         http://php.net/manual/en/features.commandline.php
  21.  * @since       11.1
  22.  */
  23. {
  24.     /**
  25.      * @var    array  The available POSIX signals to be caught by default.
  26.      * @see    http://php.net/manual/pcntl.constants.php
  27.      * @since  11.1
  28.      */
  29.     protected static $signals = array(
  30.         'SIGHUP',
  31.         'SIGINT',
  32.         'SIGQUIT',
  33.         'SIGILL',
  34.         'SIGTRAP',
  35.         'SIGABRT',
  36.         'SIGIOT',
  37.         'SIGBUS',
  38.         'SIGFPE',
  39.         'SIGUSR1',
  40.         'SIGSEGV',
  41.         'SIGUSR2',
  42.         'SIGPIPE',
  43.         'SIGALRM',
  44.         'SIGTERM',
  45.         'SIGSTKFLT',
  46.         'SIGCLD',
  47.         'SIGCHLD',
  48.         'SIGCONT',
  49.         'SIGTSTP',
  50.         'SIGTTIN',
  51.         'SIGTTOU',
  52.         'SIGURG',
  53.         'SIGXCPU',
  54.         'SIGXFSZ',
  55.         'SIGVTALRM',
  56.         'SIGPROF',
  57.         'SIGWINCH',
  58.         'SIGPOLL',
  59.         'SIGIO',
  60.         'SIGPWR',
  61.         'SIGSYS',
  62.         'SIGBABY',
  63.         'SIG_BLOCK',
  64.         'SIG_UNBLOCK',
  65.         'SIG_SETMASK'
  66.     );
  67.  
  68.     /**
  69.      * @var    boolean  True if the daemon is in the process of exiting.
  70.      * @since  11.1
  71.      */
  72.     protected $exiting = false;
  73.  
  74.     /**
  75.      * @var    integer  The parent process id.
  76.      * @since  12.1
  77.      */
  78.     protected $parentId = 0;
  79.  
  80.     /**
  81.      * @var    integer  The process id of the daemon.
  82.      * @since  11.1
  83.      */
  84.     protected $processId = 0;
  85.  
  86.     /**
  87.      * @var    boolean  True if the daemon is currently running.
  88.      * @since  11.1
  89.      */
  90.     protected $running = false;
  91.  
  92.     /**
  93.      * Class constructor.
  94.      *
  95.      * @param   mixed  $input       An optional argument to provide dependency injection for the application's
  96.      *                               input object.  If the argument is a JInputCli object that object will become
  97.      *                               the application's input object, otherwise a default input object is created.
  98.      * @param   mixed  $config      An optional argument to provide dependency injection for the application's
  99.      *                               config object.  If the argument is a JRegistry object that object will become
  100.      *                               the application's config object, otherwise a default config object is created.
  101.      * @param   mixed  $dispatcher  An optional argument to provide dependency injection for the application's
  102.      *                               event dispatcher.  If the argument is a JEventDispatcher object that object will become
  103.      *                               the application's event dispatcher, if it is null then the default event dispatcher
  104.      *                               will be created based on the application's loadDispatcher() method.
  105.      *
  106.      * @since   11.1
  107.      * @throws  RuntimeException
  108.      */
  109.     public function __construct(JInputCli $input = null, JRegistry $config = null, JEventDispatcher $dispatcher = null)
  110.     {
  111.         // Verify that the process control extension for PHP is available.
  112.         // @codeCoverageIgnoreStart
  113.         if (!defined('SIGHUP'))
  114.         {
  115.             JLog::add('The PCNTL extension for PHP is not available.', JLog::ERROR);
  116.             throw new RuntimeException('The PCNTL extension for PHP is not available.');
  117.         }
  118.  
  119.         // Verify that POSIX support for PHP is available.
  120.         if (!function_exists('posix_getpid'))
  121.         {
  122.             JLog::add('The POSIX extension for PHP is not available.', JLog::ERROR);
  123.             throw new RuntimeException('The POSIX extension for PHP is not available.');
  124.         }
  125.         // @codeCoverageIgnoreEnd
  126.  
  127.         // Call the parent constructor.
  128.         parent::__construct($input, $config, $dispatcher);
  129.  
  130.         // Set some system limits.
  131.         @set_time_limit($this->config->get('max_execution_time', 0));
  132.  
  133.         if ($this->config->get('max_memory_limit') !== null)
  134.         {
  135.             ini_set('memory_limit', $this->config->get('max_memory_limit', '256M'));
  136.         }
  137.  
  138.         // Flush content immediately.
  139.         ob_implicit_flush();
  140.     }
  141.  
  142.     /**
  143.      * Method to handle POSIX signals.
  144.      *
  145.      * @param   integer  $signal  The received POSIX signal.
  146.      *
  147.      * @return  void 
  148.      *
  149.      * @since   11.1
  150.      * @see     pcntl_signal()
  151.      * @throws  RuntimeException
  152.      */
  153.     public static function signal($signal)
  154.     {
  155.         // Log all signals sent to the daemon.
  156.         JLog::add('Received signal: ' . $signal, JLog::DEBUG);
  157.  
  158.         // Let's make sure we have an application instance.
  159.         if (!is_subclass_of(static::$instance, 'JApplicationDaemon'))
  160.         {
  161.             JLog::add('Cannot find the application instance.', JLog::EMERGENCY);
  162.             throw new RuntimeException('Cannot find the application instance.');
  163.         }
  164.  
  165.         // Fire the onReceiveSignal event.
  166.         static::$instance->triggerEvent('onReceiveSignal', array($signal));
  167.  
  168.         switch ($signal)
  169.         {
  170.             case SIGINT:
  171.             case SIGTERM:
  172.                 // Handle shutdown tasks
  173.                 if (static::$instance->running && static::$instance->isActive())
  174.                 {
  175.                     static::$instance->shutdown();
  176.                 }
  177.                 else
  178.                 {
  179.                     static::$instance->close();
  180.                 }
  181.                 break;
  182.             case SIGHUP:
  183.                 // Handle restart tasks
  184.                 if (static::$instance->running && static::$instance->isActive())
  185.                 {
  186.                     static::$instance->shutdown(true);
  187.                 }
  188.                 else
  189.                 {
  190.                     static::$instance->close();
  191.                 }
  192.                 break;
  193.             case SIGCHLD:
  194.                 // A child process has died
  195.                 while (static::$instance->pcntlWait($signal, WNOHANG || WUNTRACED) > 0)
  196.                 {
  197.                     usleep(1000);
  198.                 }
  199.                 break;
  200.             case SIGCLD:
  201.                 while (static::$instance->pcntlWait($signal, WNOHANG) > 0)
  202.                 {
  203.                     $signal = static::$instance->pcntlChildExitStatus($signal);
  204.                 }
  205.                 break;
  206.             default:
  207.                 break;
  208.         }
  209.     }
  210.  
  211.     /**
  212.      * Check to see if the daemon is active.  This does not assume that $this daemon is active, but
  213.      * only if an instance of the application is active as a daemon.
  214.      *
  215.      * @return  boolean  True if daemon is active.
  216.      *
  217.      * @since   11.1
  218.      */
  219.     public function isActive()
  220.     {
  221.         // Get the process id file location for the application.
  222.         $pidFile = $this->config->get('application_pid_file');
  223.  
  224.         // If the process id file doesn't exist then the daemon is obviously not running.
  225.         if (!is_file($pidFile))
  226.         {
  227.             return false;
  228.         }
  229.  
  230.         // Read the contents of the process id file as an integer.
  231.         $fp = fopen($pidFile, 'r');
  232.         $pid = fread($fp, filesize($pidFile));
  233.         $pid = (int) $pid;
  234.         fclose($fp);
  235.  
  236.         // Check to make sure that the process id exists as a positive integer.
  237.         if (!$pid)
  238.         {
  239.             return false;
  240.         }
  241.  
  242.         // Check to make sure the process is active by pinging it and ensure it responds.
  243.         if (!posix_kill($pid, 0))
  244.         {
  245.             // No response so remove the process id file and log the situation.
  246.             @ unlink($pidFile);
  247.             JLog::add('The process found based on PID file was unresponsive.', JLog::WARNING);
  248.  
  249.             return false;
  250.         }
  251.  
  252.         return true;
  253.     }
  254.  
  255.     /**
  256.      * Load an object or array into the application configuration object.
  257.      *
  258.      * @param   mixed  $data  Either an array or object to be loaded into the configuration object.
  259.      *
  260.      * @return  JCli  Instance of $this to allow chaining.
  261.      *
  262.      * @since   11.1
  263.      */
  264.     public function loadConfiguration($data)
  265.     {
  266.         // Execute the parent load method.
  267.         parent::loadConfiguration($data);
  268.  
  269.         /*
  270.          * Setup some application metadata options.  This is useful if we ever want to write out startup scripts
  271.          * or just have some sort of information available to share about things.
  272.          */
  273.  
  274.         // The application author name.  This string is used in generating startup scripts and has
  275.         // a maximum of 50 characters.
  276.         $tmp = (string) $this->config->get('author_name', 'Joomla Platform');
  277.         $this->config->set('author_name', (strlen($tmp) > 50) ? substr($tmp, 0, 50) : $tmp);
  278.  
  279.         // The application author email.  This string is used in generating startup scripts.
  280.         $tmp = (string) $this->config->get('author_email', 'admin@joomla.org');
  281.         $this->config->set('author_email', filter_var($tmp, FILTER_VALIDATE_EMAIL));
  282.  
  283.         // The application name.  This string is used in generating startup scripts.
  284.         $tmp = (string) $this->config->get('application_name', 'JApplicationDaemon');
  285.         $this->config->set('application_name', (string) preg_replace('/[^A-Z0-9_-]/i', '', $tmp));
  286.  
  287.         // The application description.  This string is used in generating startup scripts.
  288.         $tmp = (string) $this->config->get('application_description', 'A generic Joomla Platform application.');
  289.         $this->config->set('application_description', filter_var($tmp, FILTER_SANITIZE_STRING));
  290.  
  291.         /*
  292.          * Setup the application path options.  This defines the default executable name, executable directory,
  293.          * and also the path to the daemon process id file.
  294.          */
  295.  
  296.         // The application executable daemon.  This string is used in generating startup scripts.
  297.         $tmp = (string) $this->config->get('application_executable', basename($this->input->executable));
  298.         $this->config->set('application_executable', $tmp);
  299.  
  300.         // The home directory of the daemon.
  301.         $tmp = (string) $this->config->get('application_directory', dirname($this->input->executable));
  302.         $this->config->set('application_directory', $tmp);
  303.  
  304.         // The pid file location.  This defaults to a path inside the /tmp directory.
  305.         $name = $this->config->get('application_name');
  306.         $tmp = (string) $this->config->get('application_pid_file', strtolower('/tmp/' . $name . '/' . $name . '.pid'));
  307.         $this->config->set('application_pid_file', $tmp);
  308.  
  309.         /*
  310.          * Setup the application identity options.  It is important to remember if the default of 0 is set for
  311.          * either UID or GID then changing that setting will not be attempted as there is no real way to "change"
  312.          * the identity of a process from some user to root.
  313.          */
  314.  
  315.         // The user id under which to run the daemon.
  316.         $tmp = (int) $this->config->get('application_uid', 0);
  317.         $options = array('options' => array('min_range' => 0, 'max_range' => 65000));
  318.         $this->config->set('application_uid', filter_var($tmp, FILTER_VALIDATE_INT, $options));
  319.  
  320.         // The group id under which to run the daemon.
  321.         $tmp = (int) $this->config->get('application_gid', 0);
  322.         $options = array('options' => array('min_range' => 0, 'max_range' => 65000));
  323.         $this->config->set('application_gid', filter_var($tmp, FILTER_VALIDATE_INT, $options));
  324.  
  325.         // Option to kill the daemon if it cannot switch to the chosen identity.
  326.         $tmp = (bool) $this->config->get('application_require_identity', 1);
  327.         $this->config->set('application_require_identity', $tmp);
  328.  
  329.         /*
  330.          * Setup the application runtime options.  By default our execution time limit is infinite obviously
  331.          * because a daemon should be constantly running unless told otherwise.  The default limit for memory
  332.          * usage is 128M, which admittedly is a little high, but remember it is a "limit" and PHP's memory
  333.          * management leaves a bit to be desired :-)
  334.          */
  335.  
  336.         // The maximum execution time of the application in seconds.  Zero is infinite.
  337.         $tmp = $this->config->get('max_execution_time');
  338.  
  339.         if ($tmp !== null)
  340.         {
  341.             $this->config->set('max_execution_time', (int) $tmp);
  342.         }
  343.  
  344.         // The maximum amount of memory the application can use.
  345.         $tmp = $this->config->get('max_memory_limit', '256M');
  346.  
  347.         if ($tmp !== null)
  348.         {
  349.             $this->config->set('max_memory_limit', (string) $tmp);
  350.         }
  351.  
  352.         return $this;
  353.     }
  354.  
  355.     /**
  356.      * Execute the daemon.
  357.      *
  358.      * @return  void 
  359.      *
  360.      * @since   11.1
  361.      */
  362.     public function execute()
  363.     {
  364.         // Trigger the onBeforeExecute event.
  365.         $this->triggerEvent('onBeforeExecute');
  366.  
  367.         // Enable basic garbage collection.
  368.         gc_enable();
  369.  
  370.         JLog::add('Starting ' . $this->name, JLog::INFO);
  371.  
  372.         // Set off the process for becoming a daemon.
  373.         if ($this->daemonize())
  374.         {
  375.             // Declare ticks to start signal monitoring. When you declare ticks, PCNTL will monitor
  376.             // incoming signals after each tick and call the relevant signal handler automatically.
  377.             declare (ticks = 1);
  378.  
  379.             // Start the main execution loop.
  380.             while (true)
  381.             {
  382.                 // Perform basic garbage collection.
  383.                 $this->gc();
  384.  
  385.                 // Don't completely overload the CPU.
  386.                 usleep(1000);
  387.  
  388.                 // Execute the main application logic.
  389.                 $this->doExecute();
  390.             }
  391.         }
  392.         // We were not able to daemonize the application so log the failure and die gracefully.
  393.         else
  394.         {
  395.             JLog::add('Starting ' . $this->name . ' failed', JLog::INFO);
  396.         }
  397.  
  398.         // Trigger the onAfterExecute event.
  399.         $this->triggerEvent('onAfterExecute');
  400.     }
  401.  
  402.     /**
  403.      * Restart daemon process.
  404.      *
  405.      * @return  void 
  406.      *
  407.      * @codeCoverageIgnore
  408.      * @since   11.1
  409.      */
  410.     public function restart()
  411.     {
  412.         JLog::add('Stopping ' . $this->name, JLog::INFO);
  413.         $this->shutdown(true);
  414.     }
  415.  
  416.     /**
  417.      * Stop daemon process.
  418.      *
  419.      * @return  void 
  420.      *
  421.      * @codeCoverageIgnore
  422.      * @since   11.1
  423.      */
  424.     public function stop()
  425.     {
  426.         JLog::add('Stopping ' . $this->name, JLog::INFO);
  427.         $this->shutdown();
  428.     }
  429.  
  430.     /**
  431.      * Method to change the identity of the daemon process and resources.
  432.      *
  433.      * @return  boolean  True if identity successfully changed
  434.      *
  435.      * @since   11.1
  436.      * @see     posix_setuid()
  437.      */
  438.     protected function changeIdentity()
  439.     {
  440.         // Get the group and user ids to set for the daemon.
  441.         $uid = (int) $this->config->get('application_uid', 0);
  442.         $gid = (int) $this->config->get('application_gid', 0);
  443.  
  444.         // Get the application process id file path.
  445.         $file = $this->config->get('application_pid_file');
  446.  
  447.         // Change the user id for the process id file if necessary.
  448.         if ($uid && (fileowner($file) != $uid) && (!@ chown($file, $uid)))
  449.         {
  450.             JLog::add('Unable to change user ownership of the process id file.', JLog::ERROR);
  451.  
  452.             return false;
  453.         }
  454.  
  455.         // Change the group id for the process id file if necessary.
  456.         if ($gid && (filegroup($file) != $gid) && (!@ chgrp($file, $gid)))
  457.         {
  458.             JLog::add('Unable to change group ownership of the process id file.', JLog::ERROR);
  459.  
  460.             return false;
  461.         }
  462.  
  463.         // Set the correct home directory for the process.
  464.         if ($uid && ($info = posix_getpwuid($uid)) && is_dir($info['dir']))
  465.         {
  466.             system('export HOME="' . $info['dir'] . '"');
  467.         }
  468.  
  469.         // Change the user id for the process necessary.
  470.         if ($uid && (posix_getuid($file) != $uid) && (!@ posix_setuid($uid)))
  471.         {
  472.             JLog::add('Unable to change user ownership of the proccess.', JLog::ERROR);
  473.  
  474.             return false;
  475.         }
  476.  
  477.         // Change the group id for the process necessary.
  478.         if ($gid && (posix_getgid($file) != $gid) && (!@ posix_setgid($gid)))
  479.         {
  480.             JLog::add('Unable to change group ownership of the proccess.', JLog::ERROR);
  481.  
  482.             return false;
  483.         }
  484.  
  485.         // Get the user and group information based on uid and gid.
  486.         $user = posix_getpwuid($uid);
  487.         $group = posix_getgrgid($gid);
  488.  
  489.         JLog::add('Changed daemon identity to ' . $user['name'] . ':' . $group['name'], JLog::INFO);
  490.  
  491.         return true;
  492.     }
  493.  
  494.     /**
  495.      * Method to put the application into the background.
  496.      *
  497.      * @return  boolean 
  498.      *
  499.      * @since   11.1
  500.      * @throws  RuntimeException
  501.      */
  502.     protected function daemonize()
  503.     {
  504.         // Is there already an active daemon running?
  505.         if ($this->isActive())
  506.         {
  507.             JLog::add($this->name . ' daemon is still running. Exiting the application.', JLog::EMERGENCY);
  508.  
  509.             return false;
  510.         }
  511.  
  512.         // Reset Process Information
  513.         $this->safeMode = !!@ ini_get('safe_mode');
  514.         $this->processId = 0;
  515.         $this->running = false;
  516.  
  517.         // Detach process!
  518.         try
  519.         {
  520.             // Check if we should run in the foreground.
  521.             if (!$this->input->get('f'))
  522.             {
  523.                 // Detach from the terminal.
  524.                 $this->detach();
  525.             }
  526.             else
  527.             {
  528.                 // Setup running values.
  529.                 $this->exiting = false;
  530.                 $this->running = true;
  531.  
  532.                 // Set the process id.
  533.                 $this->processId = (int) posix_getpid();
  534.                 $this->parentId = $this->processId;
  535.             }
  536.         }
  537.         catch (RuntimeException $e)
  538.         {
  539.             JLog::add('Unable to fork.', JLog::EMERGENCY);
  540.  
  541.             return false;
  542.         }
  543.  
  544.         // Verify the process id is valid.
  545.         if ($this->processId < 1)
  546.         {
  547.             JLog::add('The process id is invalid; the fork failed.', JLog::EMERGENCY);
  548.  
  549.             return false;
  550.         }
  551.  
  552.         // Clear the umask.
  553.         @ umask(0);
  554.  
  555.         // Write out the process id file for concurrency management.
  556.         if (!$this->writeProcessIdFile())
  557.         {
  558.             JLog::add('Unable to write the pid file at: ' . $this->config->get('application_pid_file'), JLog::EMERGENCY);
  559.  
  560.             return false;
  561.         }
  562.  
  563.         // Attempt to change the identity of user running the process.
  564.         if (!$this->changeIdentity())
  565.         {
  566.  
  567.             // If the identity change was required then we need to return false.
  568.             if ($this->config->get('application_require_identity'))
  569.             {
  570.                 JLog::add('Unable to change process owner.', JLog::CRITICAL);
  571.  
  572.                 return false;
  573.             }
  574.             else
  575.             {
  576.                 JLog::add('Unable to change process owner.', JLog::WARNING);
  577.             }
  578.         }
  579.  
  580.         // Setup the signal handlers for the daemon.
  581.         if (!$this->setupSignalHandlers())
  582.         {
  583.             return false;
  584.         }
  585.  
  586.         // Change the current working directory to the application working directory.
  587.         @ chdir($this->config->get('application_directory'));
  588.  
  589.         return true;
  590.     }
  591.  
  592.     /**
  593.      * This is truly where the magic happens.  This is where we fork the process and kill the parent
  594.      * process, which is essentially what turns the application into a daemon.
  595.      *
  596.      * @return  void 
  597.      *
  598.      * @since   12.1
  599.      * @throws  RuntimeException
  600.      */
  601.     protected function detach()
  602.     {
  603.         JLog::add('Detaching the ' . $this->name . ' daemon.', JLog::DEBUG);
  604.  
  605.         // Attempt to fork the process.
  606.         $pid = $this->fork();
  607.  
  608.         // If the pid is positive then we successfully forked, and can close this application.
  609.         if ($pid)
  610.         {
  611.             // Add the log entry for debugging purposes and exit gracefully.
  612.             JLog::add('Ending ' . $this->name . ' parent process', JLog::DEBUG);
  613.             $this->close();
  614.         }
  615.         // We are in the forked child process.
  616.         else
  617.         {
  618.             // Setup some protected values.
  619.             $this->exiting = false;
  620.             $this->running = true;
  621.  
  622.             // Set the parent to self.
  623.             $this->parentId = $this->processId;
  624.         }
  625.     }
  626.  
  627.     /**
  628.      * Method to fork the process.
  629.      *
  630.      * @return  integer  The child process id to the parent process, zero to the child process.
  631.      *
  632.      * @since   11.1
  633.      * @throws  RuntimeException
  634.      */
  635.     protected function fork()
  636.     {
  637.         // Attempt to fork the process.
  638.         $pid = $this->pcntlFork();
  639.  
  640.         // If the fork failed, throw an exception.
  641.         if ($pid === -1)
  642.         {
  643.             throw new RuntimeException('The process could not be forked.');
  644.         }
  645.         // Update the process id for the child.
  646.         elseif ($pid === 0)
  647.         {
  648.             $this->processId = (int) posix_getpid();
  649.         }
  650.         // Log the fork in the parent.
  651.         else
  652.         {
  653.             // Log the fork.
  654.             JLog::add('Process forked ' . $pid, JLog::DEBUG);
  655.         }
  656.  
  657.         // Trigger the onFork event.
  658.         $this->postFork();
  659.  
  660.         return $pid;
  661.     }
  662.  
  663.     /**
  664.      * Method to perform basic garbage collection and memory management in the sense of clearing the
  665.      * stat cache.  We will probably call this method pretty regularly in our main loop.
  666.      *
  667.      * @return  void 
  668.      *
  669.      * @codeCoverageIgnore
  670.      * @since   11.1
  671.      */
  672.     protected function gc()
  673.     {
  674.         // Perform generic garbage collection.
  675.         gc_collect_cycles();
  676.  
  677.         // Clear the stat cache so it doesn't blow up memory.
  678.         clearstatcache();
  679.     }
  680.  
  681.     /**
  682.      * Method to attach the JApplicationDaemon signal handler to the known signals.  Applications
  683.      * can override these handlers by using the pcntl_signal() function and attaching a different
  684.      * callback method.
  685.      *
  686.      * @return  boolean 
  687.      *
  688.      * @since   11.1
  689.      * @see     pcntl_signal()
  690.      */
  691.     protected function setupSignalHandlers()
  692.     {
  693.         // We add the error suppression for the loop because on some platforms some constants are not defined.
  694.         foreach (self::$signals as $signal)
  695.         {
  696.             // Ignore signals that are not defined.
  697.             if (!defined($signal) || !is_int(constant($signal)) || (constant($signal) === 0))
  698.             {
  699.                 // Define the signal to avoid notices.
  700.                 JLog::add('Signal "' . $signal . '" not defined. Defining it as null.', JLog::DEBUG);
  701.                 define($signal, null);
  702.  
  703.                 // Don't listen for signal.
  704.                 continue;
  705.             }
  706.  
  707.             // Attach the signal handler for the signal.
  708.             if (!$this->pcntlSignal(constant($signal), array('JApplicationDaemon', 'signal')))
  709.             {
  710.                 JLog::add(sprintf('Unable to reroute signal handler: %s', $signal), JLog::EMERGENCY);
  711.  
  712.                 return false;
  713.             }
  714.         }
  715.  
  716.         return true;
  717.     }
  718.  
  719.     /**
  720.      * Method to shut down the daemon and optionally restart it.
  721.      *
  722.      * @param   boolean  $restart  True to restart the daemon on exit.
  723.      *
  724.      * @return  void 
  725.      *
  726.      * @since   11.1
  727.      */
  728.     protected function shutdown($restart = false)
  729.     {
  730.         // If we are already exiting, chill.
  731.         if ($this->exiting)
  732.         {
  733.             return;
  734.         }
  735.         // If not, now we are.
  736.         else
  737.         {
  738.             $this->exiting = true;
  739.         }
  740.  
  741.         // If we aren't already daemonized then just kill the application.
  742.         if (!$this->running && !$this->isActive())
  743.         {
  744.             JLog::add('Process was not daemonized yet, just halting current process', JLog::INFO);
  745.             $this->close();
  746.         }
  747.  
  748.         // Only read the pid for the parent file.
  749.         if ($this->parentId == $this->processId)
  750.         {
  751.             // Read the contents of the process id file as an integer.
  752.             $fp = fopen($this->config->get('application_pid_file'), 'r');
  753.             $pid = fread($fp, filesize($this->config->get('application_pid_file')));
  754.             $pid = (int) $pid;
  755.             fclose($fp);
  756.  
  757.             // Remove the process id file.
  758.             @ unlink($this->config->get('application_pid_file'));
  759.  
  760.             // If we are supposed to restart the daemon we need to execute the same command.
  761.             if ($restart)
  762.             {
  763.                 $this->close(exec(implode(' ', $GLOBALS['argv']) . ' > /dev/null &'));
  764.             }
  765.             // If we are not supposed to restart the daemon let's just kill -9.
  766.             else
  767.             {
  768.                 passthru('kill -9 ' . $pid);
  769.                 $this->close();
  770.             }
  771.         }
  772.     }
  773.  
  774.     /**
  775.      * Method to write the process id file out to disk.
  776.      *
  777.      * @return  boolean 
  778.      *
  779.      * @since   11.1
  780.      */
  781.     protected function writeProcessIdFile()
  782.     {
  783.         // Verify the process id is valid.
  784.         if ($this->processId < 1)
  785.         {
  786.             JLog::add('The process id is invalid.', JLog::EMERGENCY);
  787.  
  788.             return false;
  789.         }
  790.  
  791.         // Get the application process id file path.
  792.         $file = $this->config->get('application_pid_file');
  793.  
  794.         if (empty($file))
  795.         {
  796.             JLog::add('The process id file path is empty.', JLog::ERROR);
  797.  
  798.             return false;
  799.         }
  800.  
  801.         // Make sure that the folder where we are writing the process id file exists.
  802.         $folder = dirname($file);
  803.  
  804.         if (!is_dir($folder) && !JFolder::create($folder))
  805.         {
  806.             JLog::add('Unable to create directory: ' . $folder, JLog::ERROR);
  807.  
  808.             return false;
  809.         }
  810.  
  811.         // Write the process id file out to disk.
  812.         if (!file_put_contents($file, $this->processId))
  813.         {
  814.             JLog::add('Unable to write proccess id file: ' . $file, JLog::ERROR);
  815.  
  816.             return false;
  817.         }
  818.  
  819.         // Make sure the permissions for the proccess id file are accurate.
  820.         if (!chmod($file, 0644))
  821.         {
  822.             JLog::add('Unable to adjust permissions for the proccess id file: ' . $file, JLog::ERROR);
  823.  
  824.             return false;
  825.         }
  826.  
  827.         return true;
  828.     }
  829.  
  830.     /**
  831.      * Method to handle post-fork triggering of the onFork event.
  832.      *
  833.      * @return  void 
  834.      *
  835.      * @since   12.1
  836.      */
  837.     protected function postFork()
  838.     {
  839.         // Trigger the onFork event.
  840.         $this->triggerEvent('onFork');
  841.     }
  842.  
  843.     /**
  844.      * Method to return the exit code of a terminated child process.
  845.      *
  846.      * @param   integer  $status  The status parameter is the status parameter supplied to a successful call to pcntl_waitpid().
  847.      *
  848.      * @return  integer  The child process exit code.
  849.      *
  850.      * @codeCoverageIgnore
  851.      * @see     pcntl_wexitstatus()
  852.      * @since   11.3
  853.      */
  854.     protected function pcntlChildExitStatus($status)
  855.     {
  856.         return pcntl_wexitstatus($status);
  857.     }
  858.  
  859.     /**
  860.      * Method to return the exit code of a terminated child process.
  861.      *
  862.      * @return  integer  On success, the PID of the child process is returned in the parent's thread
  863.      *                    of execution, and a 0 is returned in the child's thread of execution. On
  864.      *                    failure, a -1 will be returned in the parent's context, no child process
  865.      *                    will be created, and a PHP error is raised.
  866.      *
  867.      * @codeCoverageIgnore
  868.      * @see     pcntl_fork()
  869.      * @since   11.3
  870.      */
  871.     protected function pcntlFork()
  872.     {
  873.         return pcntl_fork();
  874.     }
  875.  
  876.     /**
  877.      * Method to install a signal handler.
  878.      *
  879.      * @param   integer   $signal   The signal number.
  880.      * @param   callable  $handler  The signal handler which may be the name of a user created function,
  881.      *                               or method, or either of the two global constants SIG_IGN or SIG_DFL.
  882.      * @param   boolean   $restart  Specifies whether system call restarting should be used when this
  883.      *                               signal arrives.
  884.      *
  885.      * @return  boolean  True on success.
  886.      *
  887.      * @codeCoverageIgnore
  888.      * @see     pcntl_signal()
  889.      * @since   11.3
  890.      */
  891.     protected function pcntlSignal($signal , $handler, $restart = true)
  892.     {
  893.         return pcntl_signal($signal, $handler, $restart);
  894.     }
  895.  
  896.     /**
  897.      * Method to wait on or return the status of a forked child.
  898.      *
  899.      * @param   integer  &$status  Status information.
  900.      * @param   integer  $options  If wait3 is available on your system (mostly BSD-style systems),
  901.      *                              you can provide the optional options parameter.
  902.      *
  903.      * @return  integer  The process ID of the child which exited, -1 on error or zero if WNOHANG
  904.      *                    was provided as an option (on wait3-available systems) and no child was available.
  905.      *
  906.      * @codeCoverageIgnore
  907.      * @see     pcntl_wait()
  908.      * @since   11.3
  909.      */
  910.     protected function pcntlWait(&$status, $options = 0)
  911.     {
  912.         return pcntl_wait($status, $options);
  913.     }
  914. }

Documentation generated on Tue, 19 Nov 2013 14:57:44 +0100 by phpDocumentor 1.4.3