home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / System / Daemon.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  31.4 KB  |  1,019 lines

  1. <?php
  2. /* vim: set noai expandtab tabstop=4 softtabstop=4 shiftwidth=4: */
  3.  
  4. /**
  5.  * System_Daemon turns PHP-CLI scripts into daemons.
  6.  * 
  7.  * PHP version 5
  8.  *
  9.  * @category  System
  10.  * @package   System_Daemon
  11.  * @author    Kevin van Zonneveld <kevin@vanzonneveld.net>
  12.  * @copyright 2008 Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  13.  * @license   http://www.opensource.org/licenses/bsd-license.php New BSD Licence
  14.  * @version   SVN: Release: $Id: Daemon.php 134 2008-05-22 15:41:53Z kevin $
  15.  * @link      http://trac.plutonia.nl/projects/system_daemon
  16.  */
  17.  
  18. // Autoloader borrowed from PHP_CodeSniffer, see function for credits
  19. spl_autoload_register(array("System_Daemon", "autoload"));
  20.  
  21. /**
  22.  * System_Daemon. Create daemons with practicle functions 
  23.  * like $daemon->start()
  24.  *
  25.  * Requires PHP build with --enable-cli --with-pcntl.
  26.  * Only runs on *NIX systems, because Windows lacks of the pcntl ext.
  27.  *
  28.  * PHP version 5
  29.  *
  30.  * @category  System
  31.  * @package   System_Daemon
  32.  * @author    Kevin van Zonneveld <kevin@vanzonneveld.net>
  33.  * @copyright 2008 Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  34.  * @license   http://www.opensource.org/licenses/bsd-license.php New BSD Licence
  35.  * @version   SVN: Release: $Id: Daemon.php 134 2008-05-22 15:41:53Z kevin $
  36.  * @link      http://trac.plutonia.nl/projects/system_daemon
  37.  * 
  38.  */
  39. class System_Daemon
  40. {
  41.     // Make these corresponding with PEAR
  42.     // Ensures compatibility while maintaining independency
  43.     
  44.     /**
  45.      * System is unusable
  46.      */
  47.     const LOG_EMERG = 0;
  48.     
  49.     /**
  50.      * Immediate action required
  51.      */ 
  52.     const LOG_ALERT = 1;
  53.     
  54.     /**
  55.      * Critical conditions
  56.      */
  57.     const LOG_CRIT = 2;
  58.     
  59.     /**
  60.      * Error conditions
  61.      */
  62.     const LOG_ERR = 3;
  63.     
  64.     /**
  65.      * Warning conditions
  66.      */
  67.     const LOG_WARNING = 4;
  68.     
  69.     /**
  70.      * Normal but significant
  71.      */
  72.     const LOG_NOTICE = 5;
  73.     
  74.     /**
  75.      * Informational
  76.      */
  77.     const LOG_INFO = 6;
  78.     
  79.     /**
  80.      * Debug-level messages
  81.      */
  82.     const LOG_DEBUG = 7;
  83.     
  84.     
  85.     
  86.     /**
  87.      * The current process identifier
  88.      *
  89.      * @var integer
  90.      */
  91.     static private $_processId = 0;
  92.  
  93.     /**
  94.      * Wether the our daemon is being killed
  95.      *
  96.      * @var boolean
  97.      */
  98.     static private $_isDying = false;    
  99.     
  100.     /**
  101.      * Wether the current process is a forked child
  102.      *
  103.      * @var boolean
  104.      */
  105.     static private $_processIsChild = false;
  106.     
  107.     /**
  108.      * Wether SAFE_MODE is on or off. This is important for ini_set
  109.      * behavior
  110.      *
  111.      * @var boolean
  112.      */
  113.     static private $_safeMode = false;
  114.     
  115.     /**
  116.      * Available log levels
  117.      *
  118.      * @var array
  119.      */
  120.     static private $_logLevels = array(
  121.         self::LOG_EMERG => "emerg",
  122.         self::LOG_ALERT => "alert",
  123.         self::LOG_CRIT => "crit",
  124.         self::LOG_ERR => "err",
  125.         self::LOG_WARNING => "warning",
  126.         self::LOG_NOTICE => "notice",
  127.         self::LOG_INFO => "info",
  128.         self::LOG_DEBUG => "debug"        
  129.     );
  130.     
  131.     /**
  132.      * Holds Option Object
  133.      * 
  134.      * @var mixed object or boolean
  135.      */
  136.     static private $_optObj = false;
  137.     
  138.     /**
  139.      * Holds OS Object
  140.      * 
  141.      * @var mixed object or boolean
  142.      */
  143.     static private $_osObj = false;
  144.     
  145.     /**
  146.      * Definitions for all Options
  147.      *
  148.      * @var array
  149.      * @see setOption()
  150.      * @see getOption()
  151.      */
  152.     static private $_optionDefinitions = array(
  153.         "usePEAR" => array(
  154.             "type" => "boolean",
  155.             "default" => true,
  156.             "punch" => "Wether to run this class using PEAR",
  157.             "detail" => "Will run standalone when false",
  158.             "required" => true 
  159.         ),
  160.         "usePEARLogInstance" => array(
  161.             "type" => "boolean|object",
  162.             "default" => false,
  163.             "punch" => "Accepts a PEAR_Log instance to handle all logging",
  164.             "detail" => "This will replace System_Daemon's own logging facility",
  165.             "required" => true
  166.         ),
  167.         
  168.         "authorName" => array(
  169.             "type" => "string/0-50",
  170.             "punch" => "Author name",
  171.             "example" => "Kevin van zonneveld",   
  172.             "detail" => "Required for forging init.d script"
  173.         ),
  174.         "authorEmail" => array(
  175.             "type" => "string/email",
  176.             "punch" => "Author e-mail",
  177.             "example" => "kevin@vanzonneveld.net",
  178.             "detail" => "Required for forging init.d script"
  179.         ),
  180.         "appName" => array(
  181.             "type" => "string/unix",
  182.             "punch" => "The application name",
  183.             "example" => "logparser",
  184.             "detail" => "Must be UNIX-proof; Required for running daemon",
  185.             "required" => true
  186.         ),
  187.         "appDescription" => array(
  188.             "type" => "string",
  189.             "punch" => "Daemon description",
  190.             "example" => "Parses logfiles of vsftpd and stores them in MySQL",
  191.             "detail" => "Required for forging init.d script"
  192.         ),
  193.         "appDir" => array(
  194.             "type" => "string/existing_dirpath",
  195.             "default" => "@dirname({SERVER.SCRIPT_NAME})",
  196.             "punch" => "The home directory of the daemon",
  197.             "example" => "/usr/local/logparser",
  198.             "detail" => "Highly recommended to set this yourself",
  199.             "required" => true
  200.         ),
  201.         "appExecutable" => array(
  202.             "type" => "string/existing_filepath",
  203.             "default" => "@basename({SERVER.SCRIPT_NAME})",
  204.             "punch" => "The executable daemon file",
  205.             "example" => "logparser.php",
  206.             "detail" => "Recommended to set this yourself; Required for init.d",
  207.             "required" => true
  208.         ),
  209.         
  210.         "logVerbosity" => array(
  211.             "type" => "number/0-7",
  212.             "default" => self::LOG_INFO,
  213.             "punch" => "Messages below this log level are ignored",
  214.             "example" => "",
  215.             "detail" => "Not written to logfile; not displayed on screen",
  216.             "required" => true
  217.         ),
  218.         "logLocation" => array(
  219.             "type" => "string/creatable_filepath",
  220.             "default" => "/var/log/{OPTIONS.appName}.log",
  221.             "punch" => "The log filepath",
  222.             "example" => "/var/log/logparser_daemon.log",
  223.             "detail" => "",
  224.             "required" => true
  225.         ),
  226.         
  227.         "appRunAsUID" => array(
  228.             "type" => "number/0-65000",
  229.             "default" => 0,
  230.             "punch" => "The user id under which to run the process",
  231.             "example" => "1000",
  232.             "detail" => "Defaults to root which is insecure!",
  233.             "required" => true
  234.         ),
  235.         "appRunAsGID" => array(
  236.             "type" => "number/0-65000",
  237.             "default" => 0,
  238.             "punch" => "The group id under which to run the process",
  239.             "example" => "1000",
  240.             "detail" => "Defaults to root which is insecure!",
  241.             "required" => true
  242.         ),
  243.         "appPidLocation" => array(
  244.             "type" => "string/creatable_filepath",
  245.             "default" => "/var/run/{OPTIONS.appName}.pid",
  246.             "punch" => "The pid filepath",
  247.             "example" => "/var/run/logparser.pid",
  248.             "detail" => "",
  249.             "required" => true
  250.         ),
  251.         "appDieOnIdentityCrisis" => array(
  252.             "type" => "boolean",
  253.             "default" => true,
  254.             "punch" => "Kill daemon if it cannot assume the identity",
  255.             "detail" => "",
  256.             "required" => true
  257.         ),
  258.  
  259.         "sysMaxExecutionTime" => array(
  260.             "type" => "number",
  261.             "default" => 0,
  262.             "punch" => "Maximum execution time of each script in seconds",
  263.             "detail" => "0 is infinite"
  264.         ),
  265.         "sysMaxInputTime" => array(
  266.             "type" => "number",
  267.             "default" => 0,
  268.             "punch" => "Maximum time to spend parsing request data",
  269.             "detail" => "0 is infinite"
  270.         ),
  271.         "sysMemoryLimit" => array(
  272.             "type" => "string",
  273.             "default" => "128M",
  274.             "punch" => "Maximum amount of memory a script may consume",
  275.             "detail" => "0 is infinite"
  276.         ),        
  277.     );
  278.     
  279.     /**
  280.      * Available signal handlers
  281.      * setSigHandler can overwrite these values individually.
  282.      *
  283.      * @var array
  284.      * @see setSigHandler()
  285.      */
  286.     static private $_sigHandlers = array(
  287.         SIGCONT => array("System_Daemon", "defaultSigHandler"),
  288.         SIGALRM => array("System_Daemon", "defaultSigHandler"),
  289.         SIGINT => array("System_Daemon", "defaultSigHandler"),
  290.         SIGABRT => array("System_Daemon", "defaultSigHandler"),
  291.         SIGTERM => array("System_Daemon", "defaultSigHandler"),
  292.         SIGHUP => array("System_Daemon", "defaultSigHandler"),
  293.         SIGUSR1 => array("System_Daemon", "defaultSigHandler"),
  294.         SIGCHLD => array("System_Daemon", "defaultSigHandler")
  295.     );
  296.     
  297.  
  298.     
  299.     /**
  300.      * Making the class non-abstract with a private constructor does a better
  301.      * job of preventing instantiation than just marking the class as abstract.
  302.      * 
  303.      * @see start()
  304.      */
  305.     private function __construct() 
  306.     {
  307.         
  308.     }    
  309.     
  310.     
  311.     
  312.     /**
  313.      * Autoload static method for loading classes and interfaces.
  314.      * Code from the PHP_CodeSniffer package by Greg Sherwood and 
  315.      * Marc McIntyre
  316.      *
  317.      * @param string $className The name of the class or interface.
  318.      *
  319.      * @return void
  320.      */
  321.     static public function autoload($className)
  322.     {
  323.         $parent     = 'System_';
  324.         $parent_len = strlen($parent);
  325.         if (substr($className, 0, $parent_len) == $parent) {
  326.             $newClassName = substr($className, $parent_len);
  327.         } else {
  328.             $newClassName = $className;
  329.         }
  330.  
  331.         $path = str_replace('_', '/', $newClassName).'.php';
  332.  
  333.         if (is_file(dirname(__FILE__).'/'.$path) === true) {
  334.             // Check standard file locations based on class name.
  335.             include dirname(__FILE__).'/'.$path;
  336.         } else {
  337.             // Everything else.
  338.             @include $path;
  339.         }
  340.  
  341.     }//end autoload()
  342.     
  343.     
  344.     /**
  345.      * Spawn daemon process.
  346.      * 
  347.      * @return boolean
  348.      * @see stop()
  349.      * @see autoload()
  350.      * @see _optionsInit()
  351.      * @see _summon()
  352.      */
  353.     static public function start()
  354.     {        
  355.         
  356.         // Quickly initialize some defaults like usePEAR 
  357.         // by adding the $premature flag
  358.         self::_optionsInit(true);
  359.         
  360.         // To run as a part of PEAR
  361.         if (self::getOption("usePEAR")) {
  362.             // SPL's autoload will make sure classes are automatically loaded
  363.             if (class_exists('PEAR', true) === false) {
  364.                 $msg = "PEAR not found. Install PEAR or run with option: ".
  365.                     "usePEAR = false";
  366.                 trigger_error($msg, E_USER_ERROR);                
  367.             }
  368.             
  369.             if (class_exists('PEAR_Exception', true) === false) {
  370.                 $msg = "PEAR_Exception not found?!";
  371.                 trigger_error($msg, E_USER_ERROR);                
  372.             }
  373.                         
  374.             if (class_exists('System_Daemon_Exception', true) === false) {
  375.                 // PEAR_Exception is OK. PEAR was found already.
  376.                 throw new PEAR_Exception('Class System_Daemon_Exception not found');
  377.             }            
  378.         }
  379.         
  380.         // Check the PHP configuration
  381.         if (!defined("SIGHUP")) {
  382.             $msg = "PHP is compiled without --enable-pcntl directive";
  383.             if (self::getOption("usePEAR")) {
  384.                 throw new System_Daemon_Exception($msg);
  385.             } else {
  386.                 trigger_error($msg, E_USER_ERROR);
  387.             }
  388.         }        
  389.         
  390.         // Check for CLI
  391.         if ((php_sapi_name() != 'cli')) {
  392.             $msg = "You can only create daemon from the command line";
  393.             if (self::getOption("usePEAR")) {
  394.                 throw new System_Daemon_Exception($msg);
  395.             } else {
  396.                 trigger_error($msg, E_USER_ERROR);
  397.             }
  398.         }
  399.         
  400.         // Initialize & check variables
  401.         if (self::_optionsInit(false) === false) {
  402.             if (is_object(self::$_optObj) && is_array(self::$_optObj->errors)) {
  403.                 foreach (self::$_optObj->errors as $error) {
  404.                     self::log(self::LOG_NOTICE, $error);
  405.                 }
  406.             }
  407.             
  408.             $msg = "Crucial options are not set. Review log:";
  409.             if (self::getOption("usePEAR")) {
  410.                 throw new System_Daemon_Exception($msg);
  411.             } else {
  412.                 trigger_error($msg, E_USER_ERROR);
  413.             } 
  414.         }
  415.                 
  416.         // Become daemon
  417.         self::_summon();
  418.         
  419.         return true;
  420.  
  421.     }//end start()
  422.     
  423.     /**
  424.      * Stop daemon process.
  425.      *
  426.      * @return void
  427.      * @see start()
  428.      */
  429.     static public function stop()
  430.     {
  431.         self::log(self::LOG_INFO, "stopping ".
  432.             self::getOption("appName")." daemon", 
  433.             __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  434.         self::_die();
  435.     }//end stop()
  436.     
  437.     
  438.     /**
  439.      * Overrule or add signal handlers.
  440.      *
  441.      * @param string $signal  Signal constant (e.g. SIGHUP)
  442.      * @param mixed  $handler Which handler to call on signal
  443.      * 
  444.      * @return boolean
  445.      * @see $_sigHandlers
  446.      */
  447.     static public function setSigHandler($signal, $handler)
  448.     {
  449.         if (!isset(self::$_sigHandlers[$signal])) {
  450.             // The signal should be defined already
  451.             self::log(self::LOG_NOTICE, "You can only overrule a ".
  452.                 "handler that has been defined already.", 
  453.                 __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  454.             return false;
  455.         }
  456.         
  457.         // Overwrite on existance
  458.         self::$_sigHandlers[$signal] = $handler;
  459.         return true;
  460.     }//end setSigHandler()
  461.  
  462.     /**
  463.      * Sets any option found in $_optionDefinitions
  464.      * Public interface to talk with with private option methods
  465.      * 
  466.      * @param string $name  Name of the Option
  467.      * @param mixed  $value Value of the Option
  468.      *
  469.      * @return boolean
  470.      */
  471.     static public function setOption($name, $value)
  472.     {
  473.         if (!self::_optionObjSetup()) {
  474.             return false;
  475.         }
  476.                 
  477.         return self::$_optObj->setOption($name, $value);
  478.     }//end setOption()    
  479.     
  480.     /**
  481.      * Sets an array of options found in $_optionDefinitions
  482.      * Public interface to talk with with private option methods
  483.      * 
  484.      * @param array $use_options Array with Options
  485.      *
  486.      * @return boolean
  487.      */
  488.     static public function setOptions($use_options)
  489.     {
  490.         if (!self::_optionObjSetup()) {
  491.             return false;
  492.         }
  493.         
  494.         return self::$_optObj->setOptions($use_options);
  495.     }//end setOptions()    
  496.     
  497.     /**
  498.      * Gets any option found in $_optionDefinitions
  499.      * Public interface to talk with with private option methods
  500.      * 
  501.      * @param string $name Name of the Option
  502.      *
  503.      * @return mixed
  504.      */
  505.     static public function getOption($name)
  506.     {
  507.         if (!self::_optionObjSetup()) {
  508.             return false;
  509.         }
  510.                 
  511.         return self::$_optObj->getOption($name);
  512.     }//end getOption()    
  513.  
  514.     /**
  515.      * Gets an array of options found
  516.      * 
  517.      * @return array
  518.      */
  519.     static public function getOptions()
  520.     {
  521.         if (!self::_optionObjSetup()) {
  522.             return false;
  523.         }
  524.         
  525.         return self::$_optObj->getOptions();
  526.     }//end setOptions()      
  527.     
  528.     
  529.     /**
  530.      * Almost every deamon requires a log file, this function can
  531.      * facilitate that. Also handles class-generated errors, chooses 
  532.      * either PEAR handling or PEAR-independant handling, depending on:
  533.      * self::getOption("usePEAR").
  534.      * Also supports PEAR_Log if you referenc to a valid instance of it
  535.      * in self::getOption("usePEARLogInstance").
  536.      * 
  537.      * It logs a string according to error levels specified in array: 
  538.      * self::$_logLevels (0 is fatal and handles daemon's death)
  539.      *
  540.      * @param integer $level    What function the log record is from
  541.      * @param string  $str      The log record
  542.      * @param string  $file     What code file the log record is from
  543.      * @param string  $class    What class the log record is from
  544.      * @param string  $function What function the log record is from
  545.      * @param integer $line     What code line the log record is from
  546.      *
  547.      * @throws System_Daemon_Exception  
  548.      * @return boolean
  549.      * @see _logLevels
  550.      * @see logLocation
  551.      */
  552.     static public function log($level, $str, $file = false, $class = false, 
  553.         $function = false, $line = false)
  554.     {
  555.         // If verbosity level is not matched, don't do anything        
  556.         if (self::getOption("logVerbosity") === null 
  557.             || self::getOption("logVerbosity") === false) {
  558.             // Somebody is calling log before launching daemon..
  559.             // fair enough, but we have to init some log options
  560.             self::_optionsInit(true);
  561.         }
  562.         
  563.         if (!self::getOption("appName")) {
  564.             // Not logging for anything without a name
  565.             return false;
  566.         }
  567.         
  568.         if ($level > self::getOption("logVerbosity")) {
  569.             return true;
  570.         }
  571.         
  572.         // Make use of a PEAR_Log() instance
  573.         if (self::getOption("usePEARLogInstance") !== false) {
  574.             self::getOption("usePEARLogInstance")->log($str, $level);
  575.             return true;
  576.         }
  577.         
  578.         // Save resources if arguments are passed.
  579.         // But by falling back to debug_backtrace() it still works 
  580.         // if someone forgets to pass them.
  581.         if (function_exists("debug_backtrace") && ($file == false 
  582.             || $class == false || $function == false || $line == false)) {
  583.             $dbg_bt   = @debug_backtrace();
  584.             $class    = (isset($dbg_bt[1]["class"])?$dbg_bt[1]["class"]:"");
  585.             $function = (isset($dbg_bt[1]["function"])?$dbg_bt[1]["function"]:"");
  586.             $file     = $dbg_bt[0]["file"];
  587.             $line     = $dbg_bt[0]["line"];
  588.         }
  589.  
  590.         // Determine what process the log is originating from and forge a logline
  591.         //$str_ident = "@".substr(self::_whatIAm(), 0, 1)."-".posix_getpid();
  592.         $str_date  = "[".date("M d H:i:s")."]"; 
  593.         $str_level = str_pad(self::$_logLevels[$level]."", 8, " ", STR_PAD_LEFT);
  594.         $log_line  = $str_date." ".$str_level.": ".$str; // $str_ident
  595.         if ($level < self::LOG_NOTICE) {
  596.             $log_line .= " [l:".$line."]"; 
  597.         }
  598.         
  599.         $non_debug     = ($level < self::LOG_DEBUG);
  600.         $log_succeeded = true;
  601.         $log_echoed    = false;
  602.         
  603.         if (!self::isInBackground() && $non_debug && !$log_echoed) {
  604.             // It's okay to echo if you're running as a foreground process.
  605.             // Maybe the command to write an init.d file was issued.
  606.             // In such a case it's important to echo failures to the 
  607.             // STDOUT
  608.             echo $log_line."\n";
  609.             $log_echoed = true;
  610.             // but still try to also log to file for future reference
  611.         } 
  612.         
  613.         // 'Touch' logfile 
  614.         if (!file_exists(self::getOption("logLocation"))) {
  615.             file_put_contents(self::getOption("logLocation"), "");
  616.         }
  617.         
  618.         // Not writable even after touch? Allowed to echo again!!
  619.         if (!is_writable(self::getOption("logLocation")) 
  620.             && $non_debug && !$log_echoed) { 
  621.             echo $log_line."\n";
  622.             $log_echoed    = true;
  623.             $log_succeeded = false;
  624.         } 
  625.         
  626.         // Append to logfile
  627.         if (!file_put_contents(self::getOption("logLocation"), 
  628.             $log_line."\n", FILE_APPEND)) {
  629.             $log_succeeded = false;
  630.         }
  631.         
  632.         // These are pretty serious errors
  633.         if ($level < self::LOG_WARNING) {
  634.             // So Throw an exception
  635.             if (self::getOption("usePEAR")) {
  636.                 throw new System_Daemon_Exception($log_line);
  637.             }
  638.             // An emergency logentry is reason for the deamon to 
  639.             // die immediately 
  640.             if ($level == self::LOG_EMERG) {
  641.                 self::_die();
  642.             }
  643.         }
  644.         
  645.         return $log_succeeded;
  646.         
  647.     }//end log()    
  648.  
  649.     /**
  650.      * Uses OS class to write an: 'init.d' script on the filesystem
  651.      *  
  652.      * @param boolean $overwrite May the existing init.d file be overwritten?
  653.      * 
  654.      * @return boolean
  655.      */
  656.     static public function writeAutoRun($overwrite=false)
  657.     {
  658.         // Init Options (needed for properties of init.d script)
  659.         if (self::_optionsInit(false) === false) {
  660.             return false;
  661.         }
  662.         
  663.         // Init OS Object
  664.         if (!self::_osObjSetup()) {
  665.             return false;
  666.         }
  667.         
  668.         // Get daemon properties
  669.         $options = self::getOptions();
  670.         
  671.         // Try to write init.d 
  672.         if (($res = self::$_osObj->writeAutoRun($options, $overwrite)) === false) {
  673.             if (is_array(self::$_osObj->errors)) {
  674.                 foreach (self::$_osObj->errors as $error) {
  675.                     self::log(self::LOG_NOTICE, $error);
  676.                 }
  677.             }
  678.             self::log(self::LOG_WARNING, "Unable to create startup file.");
  679.             return false;
  680.         }
  681.         
  682.         if ($res === true) {
  683.             self::log(self::LOG_NOTICE, "Startup was already written");
  684.         } else {
  685.             self::log(self::LOG_NOTICE, "Startup written to ".$res."");
  686.         }
  687.         
  688.         return true;
  689.     }//end writeAutoRun()       
  690.     
  691.     /**
  692.      * Default signal handler.
  693.      * You can overrule various signals with the 
  694.      * setSigHandler() method
  695.      *
  696.      * @param integer $signo The posix signal received.
  697.      * 
  698.      * @return void
  699.      * @see setSigHandler()
  700.      * @see $_sigHandlers
  701.      */
  702.     static public function defaultSigHandler( $signo )
  703.     {
  704.         // Must be public or else will throw a 
  705.         // fatal error: Call to private method 
  706.          
  707.         self::log(self::LOG_DEBUG, self::getOption("appName").
  708.             " daemon received signal: ".$signo, 
  709.             __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  710.             
  711.         switch ($signo) {
  712.         case SIGTERM:
  713.             // Handle shutdown tasks
  714.             if (self::isInBackground()) {
  715.                 self::_die();
  716.             } else {
  717.                 exit;
  718.             }
  719.             break;
  720.         case SIGHUP:
  721.             // Handle restart tasks
  722.             self::log(self::LOG_INFO, self::getOption("appName").
  723.                 " daemon received signal: restart", 
  724.                 __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  725.             break;
  726.         case SIGCHLD:
  727.             self::log(self::LOG_INFO, self::getOption("appName").
  728.                 " daemon received signal: hold", 
  729.                 __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  730.             while (pcntl_wait($status, WNOHANG OR WUNTRACED) > 0) {
  731.                 usleep(1000);
  732.             }
  733.             break;
  734.         default:
  735.             // Handle all other signals
  736.             break;
  737.         }
  738.     }//end defaultSigHandler()
  739.  
  740.     /**
  741.      * Wether the class is already running in the background
  742.      * 
  743.      * @return boolean
  744.      */
  745.     static public function isInBackground()
  746.     {
  747.         return self::$_processIsChild;
  748.     }//end isInBackground()
  749.     
  750.     /**
  751.      * Wether the our daemon is being killed, you might 
  752.      * want to include this in your loop
  753.      * 
  754.      * @return boolean
  755.      */
  756.     static public function isDying()
  757.     {
  758.         return self::$_isDying;
  759.     }//end isDying() 
  760.  
  761.     /**
  762.      * Check if a previous process with same pidfile was already running
  763.      *
  764.      * @return boolean
  765.      */
  766.     static public function isRunning() 
  767.     {
  768.         if(!file_exists(self::getOption("appPidLocation"))) return false;
  769.         $pid = @file_get_contents(self::getOption("appPidLocation"));
  770.  
  771.         if ($pid !== false) {
  772.             // Ping app
  773.             if (!posix_kill(intval($pid), 0)) {
  774.                 // Not responding so unlink pidfile
  775.                 @unlink(self::getOption("appPidLocation"));
  776.                 self::log(self::LOG_WARNING, "".self::getOption("appName").
  777.                     " daemon orphaned pidfile ".
  778.                     "found and removed: ".self::getOption("appPidLocation"), 
  779.                     __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  780.                 return false;
  781.             } else {
  782.                 return true;
  783.             }
  784.         } else {
  785.             return false;
  786.         }
  787.     }//end isRunning()
  788.  
  789.     
  790.     
  791.     /**
  792.      * Put the running script in background
  793.      *
  794.      * @return void
  795.      */
  796.     static private function _summon() 
  797.     {
  798.  
  799.         self::log(self::LOG_INFO, "starting ".self::getOption("appName").
  800.             " daemon, output in: ". 
  801.             self::getOption("logLocation"), 
  802.             __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  803.         
  804.         // Important for daemons
  805.         // See http://nl2.php.net/manual/en/function.pcntl-signal.php
  806.         declare(ticks = 1);
  807.         
  808.         // Setup signal handlers
  809.         // Handlers for individual signals can be overrulled with
  810.         // setSigHandler()
  811.         foreach (self::$_sigHandlers as $signal=>$handler) {
  812.             pcntl_signal($signal, $handler);
  813.         }
  814.         
  815.         // Allowed?
  816.         if (self::isRunning()) {
  817.             self::log(self::LOG_EMERG, "".self::getOption("appName").
  818.                 " daemon is still running. ".
  819.                 "exiting", 
  820.                 __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  821.         }
  822.         
  823.         // Reset Process Information
  824.         self::$_safeMode       = ((boolean)@ini_get("safe_mode") === false) ? 
  825.             false : true;
  826.         self::$_processId      = 0;
  827.         self::$_processIsChild = false;
  828.         
  829.         // Fork process!
  830.         if (!self::_fork()) {
  831.             self::log(self::LOG_EMERG, "".self::getOption("appName").
  832.                 " daemon was unable to fork", 
  833.                 __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  834.         }
  835.  
  836.         // Assume specified identity (uid & gid)
  837.         if (!posix_setuid(self::getOption("appRunAsUID")) || 
  838.             !posix_setgid(self::getOption("appRunAsGID"))) {
  839.             $lvl = self::LOG_CRIT;
  840.             $swt = "off";
  841.             if (self::getOption("appDieOnIdentityCrisis")) {
  842.                 $lvl = self::LOG_EMERG;
  843.                 $swt = "on";
  844.             }
  845.             
  846.             self::log($lvl, "".self::getOption("appName").
  847.                 " daemon was unable assume ".
  848.                 "identity (uid=".self::getOption("appRunAsUID").", gid=".
  849.                 self::getOption("appRunAsGID").") ".
  850.                 "and appDieOnIdentityCrisis was ". $swt, 
  851.                 __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  852.         }
  853.  
  854.         // Additional PID succeeded check
  855.         if (!is_numeric(self::$_processId) || self::$_processId < 1) {
  856.             self::log(self::LOG_EMERG, "".self::getOption("appName").
  857.                 " daemon didn't have a valid ".
  858.                 "pid: '".self::$_processId."'", 
  859.                 __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  860.         }
  861.          
  862.         if (!file_put_contents(self::getOption("appPidLocation"), 
  863.             self::$_processId)) {
  864.             self::log(self::LOG_EMERG, "".self::getOption("appName").
  865.                 " daemon was unable ".
  866.                 "to write to pidfile: ".self::getOption("appPidLocation")."", 
  867.                 __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  868.         }
  869.         
  870.         // System settings
  871.         if (!self::$_safeMode) {       
  872.             $options = self::getOptions();
  873.             if (is_array($options)) {
  874.                 foreach ($options as $name=>$value) {
  875.                     if (substr($name, 0, 3) == "sys" ) {
  876.                         ini_set($name, $value);
  877.                     }
  878.                 }
  879.             }
  880.         }
  881.         set_time_limit(0);        
  882.         ob_implicit_flush();        
  883.         
  884.  
  885.         // Change dir & umask
  886.         @chdir(self::getOption("appDir"));
  887.         @umask(0);
  888.     }//end _summon()
  889.     
  890.     /**
  891.      * Fork process and kill parent process, the heart of the 'daemonization'
  892.      *
  893.      * @return boolean
  894.      */
  895.     static private function _fork()
  896.     {
  897.         self::log(self::LOG_DEBUG, "forking ".self::getOption("appName").
  898.             " daemon", 
  899.             __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  900.         $pid = pcntl_fork();
  901.         if ( $pid == -1 ) {
  902.             // Error
  903.             self::log(self::LOG_WARNING, "".self::getOption("appName").
  904.                 " daemon could not be forked", 
  905.                 __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  906.             return false;
  907.         } else if ($pid) {
  908.             // Parent
  909.             self::log(self::LOG_DEBUG, "ending ".self::getOption("appName").
  910.                 " parent process", 
  911.                 __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  912.             // Die without attracting attention
  913.             exit();
  914.         } else {
  915.             // Child
  916.             self::$_processIsChild = true;
  917.             self::$_isDying  = false;
  918.             self::$_processId      = posix_getpid();
  919.             return true;
  920.         }
  921.     }//end _fork()
  922.  
  923.     /**
  924.      * Return what the current process is: child or parent
  925.      *
  926.      * @return string
  927.      */
  928.     static private function _whatIAm()
  929.     {
  930.         return (self::isInBackground()?"child":"parent");
  931.     }//end _whatIAm()
  932.  
  933.     /**
  934.      * Sytem_Daemon::_die()
  935.      * Kill the daemon
  936.      *
  937.      * @return void
  938.      */
  939.     static private function _die()
  940.     {
  941.         if (!self::isDying()) {
  942.             self::$_isDying = true;
  943.             if (!self::isInBackground() || 
  944.                 !file_exists(self::getOption("appPidLocation"))) {
  945.                 self::log(self::LOG_INFO, "Not stopping ".
  946.                     self::getOption("appName").
  947.                     ", daemon was not running",
  948.                     __FILE__, __CLASS__, __FUNCTION__, __LINE__);
  949.                 return false;
  950.             }
  951.             
  952.             @unlink(self::getOption("appPidLocation"));
  953.             exit();
  954.         }
  955.     }//end _die()
  956.     
  957.     
  958.     /**
  959.      * Sets up Option Object instance
  960.      *
  961.      * @return boolean
  962.      */
  963.     static private function _osObjSetup() 
  964.     {
  965.         // Create Option Object if nescessary
  966.         if (self::$_osObj === false) {
  967.             self::$_osObj = new System_Daemon_OS();
  968.         }
  969.         
  970.         // Still false? This was an error!
  971.         if (self::$_osObj === false) {
  972.             self::log(self::LOG_EMERG, "Unable to setup OS object. ");
  973.             return false;
  974.         } 
  975.         
  976.         return true;
  977.     }
  978.     
  979.     /**
  980.      * Sets up Option Object instance
  981.      *
  982.      * @return boolean
  983.      */
  984.     static private function _optionObjSetup() 
  985.     {
  986.         // Create Option Object if nescessary
  987.         if (self::$_optObj === false) {
  988.             self::$_optObj = new System_Daemon_Options(self::$_optionDefinitions);
  989.         }
  990.         
  991.         // Still false? This was an error!
  992.         if (self::$_optObj === false) {
  993.             self::log(self::LOG_EMERG, "Unable to setup Options object. ".
  994.                 "You must provide valid option definitions");
  995.             return false;
  996.         } 
  997.         
  998.         return true;
  999.     }
  1000.     
  1001.     /**
  1002.      * Checks if all the required options are set.
  1003.      * Initializes, sanitizes & defaults unset variables
  1004.      * 
  1005.      * @param boolean $premature Whether to do a premature option init
  1006.      * 
  1007.      * @return mixed integer or boolean
  1008.      */
  1009.     static private function _optionsInit($premature=false) 
  1010.     {
  1011.         if (!self::_optionObjSetup()) {
  1012.             return false;
  1013.         }
  1014.         
  1015.         return self::$_optObj->init($premature);        
  1016.     }//end _optionsInit()   
  1017.  
  1018. }//end class
  1019. ?>