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 nullJRegistry $config nullJEventDispatcher $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: ' $signalJLog::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($signalWNOHANG || WUNTRACED0)
  196.                 {
  197.                     usleep(1000);
  198.                 }
  199.                 break;
  200.             case SIGCLD:
  201.                 while (static::$instance->pcntlWait($signalWNOHANG0)
  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($fpfilesize($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($pid0))
  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($tmp50substr($tmp050$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($tmpFILTER_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($tmpFILTER_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($tmpFILTER_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($tmpFILTER_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->nameJLog::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->nameJLog::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->nameJLog::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 ' $pidJLog::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($signalnull);
  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($fpfilesize($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: ' $folderJLog::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: ' $fileJLog::ERROR);
  815.  
  816.             return false;
  817.         }
  818.  
  819.         // Make sure the permissions for the proccess id file are accurate.
  820.         if (!chmod($file0644))
  821.         {
  822.             JLog::add('Unable to adjust permissions for the proccess id file: ' $fileJLog::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