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