/** * Handle parent registered sigchild callbacks. * * @param int $signal_number is the signal that called this function. * @access public */ public function signal_handler_sigchild($signal_number) { // do not allow signals to interrupt this declare (ticks=0) { // reap all child zombie processes if (self::$parent_pid == getmypid()) { $status = ''; do { // get child pid that exited $child_pid = pcntl_waitpid(0, $status, WNOHANG); if ($child_pid > 0) { // child exited $identifier = false; if (!isset($this->forked_children[$child_pid])) { $this->log("Cannot find {$child_pid} in array! (This may be a subprocess not in our fork)", self::LOG_LEVEL_INFO); continue; } $child = $this->forked_children[$child_pid]; $identifier = $child['identifier']; // call exit function if and only if its declared */ if ($child['status'] == self::WORKER) { $this->invoke_callback($this->parent_function_child_exited[$this->forked_children[$child_pid]['bucket']], array($child_pid, $this->forked_children[$child_pid]['identifier']), true); } // stop the child pid $this->forked_children[$child_pid]['status'] = self::STOPPED; $this->forked_children_count--; // respawn helper processes if ($child['status'] == self::HELPER && $child['respawn'] === true) { $this->log('Helper process ' . $child_pid . ' died, respawning', self::LOG_LEVEL_INFO); $this->helper_process_spawn($child['function'], $child['arguments'], $child['identifier'], true); } // Poll for results from any children $this->post_results($child['bucket']); } elseif ($child_pid < 0) { // ignore acceptable error 'No child processes' given we force this signal to run potentially when no children exist if (pcntl_get_last_error() == 10) { continue; } // pcntl_wait got an error $this->log('pcntl_waitpid failed with error ' . pcntl_get_last_error() . ':' . pcntl_strerror(pcntl_get_last_error()), self::LOG_LEVEL_DEBUG); } } while ($child_pid > 0); } } }
/** * Daemonize the current process so it can run in the background. * * If $pidfile is supplied, the process ID is written there. * Provide absolute path names for the parameters to avoid file not found errors or logs inside the source code folder. * * If an error occurred, a RuntimeException is thrown. * * @param string $pidfile File to write the process ID of the daemon to * @param string $stderr File to redirect STDERR to * @param string $stdout File to redirect STDOUT to * @param string $stdin File to read STDIN from * @throws \RuntimeException * @return true */ public static function daemonize($pidfile = null, $stderr = '/dev/null', $stdout = '/dev/null', $stdin = '/dev/null') { // Allow only cli scripts to daemonize, otherwise you may confuse your webserver if (\php_sapi_name() !== 'cli') { throw new \RuntimeException('Can only daemonize a CLI process!'); } self::checkPID($pidfile); self::reopenFDs($stdin, $stdout, $stderr); if (($pid1 = @\pcntl_fork()) < 0) { throw new \RuntimeException('Failed to fork, reason: "' . \pcntl_strerror(\pcntl_get_last_error()) . '"'); } elseif ($pid1 > 0) { exit; } if (@posix_setsid() === -1) { throw new \RuntimeException('Failed to become session leader, reason: "' . \posix_strerror(\posix_get_last_error()) . '"'); } if (($pid2 = @\pcntl_fork()) < 0) { throw new \RuntimeException('Failed to fork, reason: "' . \pcntl_strerror(\pcntl_get_last_error()) . '"'); } elseif ($pid2 > 0) { exit; } chdir('/'); umask(022); self::writePID($pidfile); return true; }
/** * @param \Extended\Process\Runnable $child * A runnable process to fork */ public function fork(Runnable $child) { $this->pid = pcntl_fork(); if ($this->pid == -1) { $error = pcntl_strerror(pcntl_get_last_error()); throw new ProcessException('Unable to fork the process: ' . $error); } elseif ($this->pid) { return pcntl_wait($this->pid); } self::$buffer->append($child->run()); }
/** * Fork. * * fork() helper method for php-resque * * @see pcntl_fork() * * @return Process|null An instance of Process representing the child in the parent, or null in the child. * @throws \Resque\Component\Core\Exception\ResqueRuntimeException when cannot fork, or fork failed. */ public function fork() { if (!function_exists('pcntl_fork')) { throw new ResqueRuntimeException('pcntl_fork is not available'); } $this->setPidFromCurrentProcess(); $pid = pcntl_fork(); if ($pid === -1) { throw new ResqueRuntimeException(sprintf('Unable to fork child. %s', pcntl_strerror(pcntl_get_last_error()))); } if (0 === $pid) { // Forked and we're the child, so return nothing. return null; } $child = new self($pid); return $child; }
public function testWrite() { $path = '/tmp/tmp.log'; $count = 100; $workers = 10; if (is_file($path)) { unlink($path); } $message = ''; for ($i = 0; $i < 1000; ++$i) { $message .= uniqid(); } $message .= PHP_EOL; $pids = []; for ($w = 0; $w < $workers; ++$w) { $pid = pcntl_fork(); if (function_exists('pcntl_get_last_error')) { $error_number = pcntl_get_last_error(); $error = "[{$error_number}] " . pcntl_strerror($error_number); } else { $error = var_export(error_get_last(), true); } $this->assertNotSame(-1, $pid, "Can't fork: " . $error); if ($pid) { $pids[] = $pid; } else { $writer = new AppenderStream($path); $writer->setUseLock(true); $writer->setUseLockShortMessage(true); for ($i = 0; $i < $count; ++$i) { $writer->write(1, $message); } die; } } foreach ($pids as $p) { pcntl_waitpid($p, $status); } $this->assertSame(13001, strlen($message)); $c = str_pad('', $count * $workers * strlen($message), $message); $this->assertSame($c, file_get_contents($path)); if (is_file($path)) { unlink($path); } }
/** * @param callable $callable * @param array $arguments * * @throws \Brick\Process\ProcessException */ public function __construct(callable $callable, array $arguments = []) { self::checkExtensions(); $pid = @pcntl_fork(); if ($pid < 0) { $errorCode = pcntl_get_last_error(); $errorMessage = pcntl_strerror($errorCode); throw ProcessException::forkingError($errorCode, $errorMessage); } if ($pid) { // Parent $this->pid = $pid; } else { // Child call_user_func_array($callable, $arguments); exit(0); } }
public function run() { if ($this->stopped) { return 0; } $pid = pcntl_fork(); if ($pid < 0) { $errno = pcntl_get_last_error(); throw new \RuntimeException("Cannot fork process, error = " . pcntl_strerror($errno)); } elseif ($pid == 0) { // in child process $now = time(); if ($now < $this->nextRun) { if ($this->traceEnabled) { mdebug("Will wait %d seconds for next run of %s", $this->nextRun - $now, $this->name); } sleep($this->nextRun - $now); } // run using application $this->application->setAutoExit(false); // we will handle exit on our own $this->application->setCatchExceptions(false); // we will catch on our own try { $ret = $this->application->run($this->input, $this->output); } catch (\Exception $e) { mtrace($e, "Exception while running command {$this->name}", "error"); $ret = AbstractDaemonSentinelCommand::EXIT_CODE_COMMON_ERROR; } // Check if we should alert if ($ret != AbstractDaemonSentinelCommand::EXIT_CODE_OK && $this->alert) { // alert in child process is better, because we can get more trace here malert("Daemon command %s failed with exit code = %d", $this->name, $ret); exit(AbstractDaemonSentinelCommand::EXIT_CODE_OK); // exit OK because alert is already sent } else { exit($ret); } } else { $this->lastRun = $this->nextRun; return $pid; } }
/** * @param callable $callback * @param array $args * @throws Exception * @return int Child process PID */ public static function run(callable $callback, $args = []) { if (!function_exists('pcntl_fork')) { throw new Exception('No pcntl is installed'); } $pid = pcntl_fork(); if (-1 == $pid) { throw new Exception('pcntl_fork() error: ' . pcntl_strerror(pcntl_get_last_error())); } if (0 != $pid) { return $pid; } else { register_shutdown_function(function () { ob_end_clean(); posix_kill(getmypid(), SIGKILL); }); if (empty($args)) { call_user_func($callback); } else { call_user_func_array($callback, $args); } exit(0); } }
/** * Runs a pulling job, creating several threads to do so. * @param object $a The App instance. * @param array $pull_batch A batch of URL's to pull. * @param string $db_host DB host to connect to. * @param string $db_user DB user to connect with. * @param string $db_pass DB pass to connect with. * @param mixed $db_data Nobody knows. * @param mixed $install Maybe a boolean. * @return void */ function run_pulling_job($a, $pull_batch, $db_host, $db_user, $db_pass, $db_data, $install) { //We need the scraper. require_once 'include/submit.php'; //POSIX threads only. if (!function_exists('pcntl_fork')) { msg('Error: no pcntl_fork support. Are you running a different OS? Report an issue please.', true); } //Create the threads we need. $items = count($pull_batch); $threadc = min($a->config['syncing']['pulling_threads'], $items); //Don't need more threads than items. $threads = array(); msg("Creating {$threadc} pulling threads for {$items} profiles."); //Build the threads. for ($i = 0; $i < $threadc; $i++) { $pid = pcntl_fork(); if ($pid === -1) { msg('Error: something went wrong with the fork. ' . pcntl_strerror(), true); } //You're a child, go do some labor! if ($pid === 0) { pull_worker($i, $threadc, $pull_batch, $db_host, $db_user, $db_pass, $db_data, $install); exit; } //Store the list of PID's. if ($pid > 0) { $threads[] = $pid; } } //Wait for all child processes. $theading_problems = false; foreach ($threads as $pid) { pcntl_waitpid($pid, $status); if ($status !== 0) { $theading_problems = true; msg("Bad process return value {$pid}:{$status}"); } } //If we did not have any "threading" problems. if (!$theading_problems) { //Reconnect global $db; $db = new dba($db_host, $db_user, $db_pass, $db_data, $install); //Create a query for deleting this queue. $where = array(); foreach ($pull_batch as $item) { $where[] = dbesc($item['url']); } $where = "WHERE `url` IN ('" . implode("', '", $where) . "')"; //Remove the items from queue. q("DELETE FROM `sync-pull-queue` {$where} LIMIT %u", count($pull_batch)); msg('Removed items from pull queue.'); } }
/** * update the process status * @param bool $block */ public function updateStatus($block = false) { if (empty($this->pid)) { $message = "the process pid is null, so maybe the process is not started"; throw new \RuntimeException($message); } if ($this->running === false) { return; } if ($block) { $res = pcntl_waitpid($this->pid, $status); } else { $res = pcntl_waitpid($this->pid, $status, WNOHANG | WUNTRACED); } if ($res === -1) { $message = "pcntl_waitpid failed. the process maybe available"; throw new \RuntimeException($message); } elseif ($res === 0) { $this->running = true; } else { if (pcntl_wifsignaled($status)) { $this->term_signal = pcntl_wtermsig($status); } if (pcntl_wifstopped($status)) { $this->stop_signal = pcntl_wstopsig($status); } if (pcntl_wifexited($status)) { $this->exit_code = pcntl_wexitstatus($status); } else { $this->errno = pcntl_get_last_error(); $this->errmsg = pcntl_strerror($this->errno); } $this->running = false; } }
/** * Get system-specific signal handler * * @return callable */ protected function getSignalHandler() { return function (SignalHandler $handler, $signal) { if (SIGKILL != $signal) { //do not redefine SIGKILL, for consistency pcntl_sigprocmask(SIG_UNBLOCK, [$signal]); if (!pcntl_signal($signal, [$handler, 'handle'])) { $this->logger->error('Failed to register system signal ' . $signal . ': ' . pcntl_strerror(pcntl_get_last_error())); } else { $this->logger->info('Registered system signal: ' . $signal); } } }; }
$threadc = min($a->config['maintenance']['threads'], $items); //Don't need more threads than items. $threads = array(); //Debug... if ($verbose) { echo "Creating {$threadc} maintainer threads for {$items} profiles." . PHP_EOL; } logger("Creating {$threadc} maintainer threads for {$items} profiles."); for ($i = 0; $i < $threadc; $i++) { $pid = pcntl_fork(); if ($pid === -1) { if ($verbose) { echo 'Error: something went wrong with the fork. ' . pcntl_strerror(); } logger('Error: something went wrong with the fork. ' . pcntl_strerror()); die('Error: something went wrong with the fork. ' . pcntl_strerror()); } //You're a child, go do some labor! if ($pid === 0) { break; } //Store the list of PID's. if ($pid > 0) { $threads[] = $pid; } } //The work for child processes. if ($pid === 0) { //Lets be nice, we're only doing maintenance here... pcntl_setpriority(5); //Get personal DBA's.
/** * Str Error * * @param $errno Error Number * * @return string|bool Returns error description on success or FALSE on failure. */ public function strError($errno) { return pcntl_strerror($errno); }
/** * * Shutdown function is call if script terminates try to make a restart if needed * * Prepare the job for start * * @internal param int the signal that terminates the job */ public function shutdown() { $args = func_get_args(); //Put last error to log if one $lasterror = error_get_last(); if ($lasterror['type'] == E_ERROR or $lasterror['type'] == E_PARSE or $lasterror['type'] == E_CORE_ERROR or $lasterror['type'] == E_CORE_WARNING or $lasterror['type'] == E_COMPILE_ERROR or $lasterror['type'] == E_COMPILE_WARNING) { $this->log($lasterror['type'], $lasterror['message'], $lasterror['file'], $lasterror['line']); } //Put signals to log if (!empty($args[0])) { $signals = array('SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGBUS', 'SIGFPE', 'SIGKILL', 'SIGSEGV', 'SIGPIPE', 'SIGALRM', 'SIGTERM', 'SIGSTKFLT', 'SIGUSR1', 'SIGUSR2', 'SIGCHLD', 'SIGCONT', 'SIGSTOP', 'SIGTSTP', 'SIGTTIN', 'SIGTTOU', 'SIGURG', 'SIGXCPU', 'SIGXFSZ', 'SIGVTALRM', 'SIGPROF', 'SIGWINCH', 'SIGIO', 'SIGPWR', 'SIGSYS'); foreach ($signals as $signal) { if (defined($signal) && $args[0] === constant($signal)) { $this->log(sprintf(__('Signal "%s" is sent to script!', 'backwpup'), $signal), E_USER_ERROR); break; } } } if (function_exists('pcntl_get_last_error')) { $error = pcntl_get_last_error(); if (!empty($error)) { $error_msg = pcntl_strerror($error); if (!empty($error_msg)) { $error = '(' . $error . ') ' . $error_msg; } } if (!empty($error)) { $this->log(sprintf(__('System: %s', 'backwpup'), $error), E_USER_ERROR); } } if (function_exists('posix_get_last_error') && empty($error)) { $error = posix_get_last_error(); if (!empty($error)) { $error_msg = posix_strerror($error); if (!empty($error_msg)) { $error = '(' . $error . ') ' . $error_msg; } } if (!empty($error)) { $this->log(sprintf(__('System: %s', 'backwpup'), $error), E_USER_ERROR); } } $this->do_restart(TRUE); }
private function fork() { for ($i = 0; $i < 3; ++$i) { if (isset($pid)) { usleep(1000000); } $pid = pcntl_fork(); if ($pid >= 0) { return $pid; } $error_number = pcntl_get_last_error(); $error = "[{$error_number}] " . pcntl_strerror($error_number); Logger::getLogger('queue')->warn("Can`t fork, retryNumber={$i}, pid: '" . var_export($pid, true) . "', error: '{$error}'", new Exception()); } throw new RuntimeException('Can`t fork'); }
function signal_handler(int $sig) { if ($sig !== SIGCHLD) { return; } $ignore = NULL; while (($rc = pcntl_waitpid(-1, $ignore, WNOHANG)) > 0) { } if ($rc !== -1 || pcntl_get_last_error() === PCNTL_ECHILD) { return; } fwrite(STDERR, 'waitpid() failed: ' . pcntl_strerror(pcntl_get_last_error()) . "\n"); exit(1); }
/** * Retrieve the system error message associated with the given errorNo * * @param int $errorCode * * @return string */ public function getStringError(int $errorCode) { return pcntl_strerror($errorCode); }
protected function doFork(InputInterface $input, OutputInterface $output) { $pid = pcntl_fork(); if ($pid < 0) { $errno = pcntl_get_last_error(); throw new \RuntimeException("Cannot fork process, error = " . pcntl_strerror($errno)); } elseif ($pid == 0) { // in child process $ret = $this->doExecute($input, $output); exit(is_numeric($ret) ? $ret : 0); } else { return $pid; } }
/** * Error logging from pcntl* functions * * @param string $sMethodCalled */ private function _handlePcntlError($sMethodCalled) { $nError = pcntl_get_last_error(); if ($nError == 10) { return; // ignore: no child processes } $sErr = pcntl_strerror($nError); Logger::getInstance()->crit("ABORT: {$sMethodCalled} error: {$sErr}"); $this->bRunLoop = FALSE; }
/** * update the process status * * @param bool $block */ protected function updateStatus($block = false) { if ($this->running !== true) { return; } if ($block) { $res = pcntl_waitpid($this->pid, $status); } else { $res = pcntl_waitpid($this->pid, $status, WNOHANG | WUNTRACED); } if ($res === -1) { throw new \RuntimeException('pcntl_waitpid failed. the process maybe available'); } elseif ($res === 0) { $this->running = true; } else { if (pcntl_wifsignaled($status)) { $this->term_signal = pcntl_wtermsig($status); } if (pcntl_wifstopped($status)) { $this->stop_signal = pcntl_wstopsig($status); } if (pcntl_wifexited($status)) { $this->errno = pcntl_wexitstatus($status); $this->errmsg = pcntl_strerror($this->errno); } else { $this->errno = pcntl_get_last_error(); $this->errmsg = pcntl_strerror($this->errno); } if (pcntl_wifsignaled($status)) { $this->if_signal = true; } else { $this->if_signal = false; } $this->running = false; } }
protected function waitForBackgroundProcesses() { //$lastMemory = memory_get_usage(true); while (true) { //$memory = memory_get_usage(true); //if ($memory != $lastMemory) { // echo( // sprintf("memory change: %d, from %d to %d", $memory - $lastMemory, $lastMemory, $memory) // . PHP_EOL // ); //} //$lastMemory = $memory; pcntl_signal_dispatch(); $status = 0; $pid = pcntl_waitpid(-1, $status, WNOHANG); if ($pid == 0) { // no child process has quit usleep(200 * 1000); } else { if ($pid > 0) { // child process with pid = $pid exits $exitStatus = pcntl_wexitstatus($status); $runner = $this->runningProcesses[$pid]; if (!$runner instanceof CommandRunner) { throw new \LogicException("Cannot find command runner for process pid = %d", $pid); } unset($this->runningProcesses[$pid]); $runner->onProcessExit($exitStatus, $pid); $newPid = $runner->run(); if ($newPid > 0) { $this->runningProcesses[$newPid] = $runner; } } else { // error $errno = pcntl_get_last_error(); if ($errno == PCNTL_ECHILD) { // all children finished mdebug("No more BackgroundProcessRunner children, continue ..."); break; } else { // some other error throw new \RuntimeException("Error waiting for process, error = " . pcntl_strerror($errno)); } } } } }
public function fork() { $sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); $pid = pcntl_fork(); $this->isRunning = true; if ($pid <= -1) { $this->isRunning = false; throw new \RuntimeException("Unable to fork child: " . pcntl_strerror(pcntl_get_last_error())); } elseif ($pid === 0) { //child proc try { $this->isExternal = true; $this->childPid = posix_getpid(); set_error_handler(array(ThreadConfig::GetErrorHandler(), 'OnUncaughtErrorTriggered')); register_shutdown_function(function () { //catch fatal on some cases $fatalErrorCodes = array(E_ERROR, E_COMPILE_ERROR, E_PARSE); $error = error_get_last(); if (is_array($error) && isset($error['type']) && in_array($error['type'], $fatalErrorCodes)) { $exception = new SerializableFatalException($error['message'], $error['file'], $error['line'], $error['type']); if ($this->messageInProgress instanceof AsyncMessage) { //mark as error and send back to parent $this->messageInProgress->Error($exception); $this->connection->writeSync($this->messageInProgress); } else { $message = new AsyncMessage(array(), true); $message->setIsOneWay(true); $message->Error($exception); $this->connection->writeSync($message); } } }); $this->loop = $this->loop->afterForkChild(); fclose($sockets[1]); $this->socket = $sockets[0]; $this->connection = new ThreadConnection($this->loop, $this->socket); $this->connection->on('message', function ($connection, AsyncMessage $message) { $this->AttachMessage($message); }); $this->loop->nextTick(function () { $this->messageHandler->InitializeExternal($this); }); $this->loop->run(); } catch (\Exception $e) { $handler = ThreadConfig::GetErrorHandler(); $result = @$handler->OnUncaughtException($e); //redirect exception to client. if ($this->messageInProgress instanceof AsyncMessage) { //mark as error and send back to parent $this->messageInProgress->Error($result); $this->connection->writeSync($this->messageInProgress); } else { $message = new AsyncMessage(array(), true); $message->setIsOneWay(true); $message->Error($result); $this->connection->writeSync($message); } //no more chance to handle here :-( } exit; } else { // parent fclose($sockets[0]); $this->socket = $sockets[1]; $this->getLoop()->afterForkParent(); $this->connection = new ThreadConnection($this->loop, $this->socket); $this->connection->on('message', function ($connection, AsyncMessage $message) { $this->AttachMessage($message); }); //keep an eye on the thread $this->pidCollector = $this->getLoop()->addPeriodicTimer(5, function (TimerInterface $timer) { $this->waitForCompletion($timer); }); $this->childPid = $pid; } }
/** * Retrieve the system error message associated with the given errno * * @param int $errno * * @return string */ public function strerror(int $errno) : string { return pcntl_strerror($errno); }
/** * * Shutdown function is call if script terminates try to make a restart if needed * * Prepare the job for start * * @internal param int the signal that terminates the job */ public function shutdown() { //Put last error to log if one $lasterror = error_get_last(); if ($lasterror['type'] === E_ERROR || $lasterror['type'] === E_PARSE || $lasterror['type'] === E_CORE_ERROR || $lasterror['type'] === E_CORE_WARNING || $lasterror['type'] === E_COMPILE_ERROR || $lasterror['type'] === E_COMPILE_WARNING) { $this->log($lasterror['type'], $lasterror['message'], $lasterror['file'], $lasterror['line']); } $error = false; if (function_exists('pcntl_get_last_error')) { $error = pcntl_get_last_error(); if (!empty($error)) { $error_msg = pcntl_strerror($error); if (!empty($error_msg)) { $error = '(' . $error . ') ' . $error_msg; } } if (!empty($error)) { $this->log(sprintf(__('System: %s', 'backwpup'), $error), E_USER_ERROR); } } if (function_exists('posix_get_last_error') && !$error) { $error = posix_get_last_error(); if (!empty($error)) { $error_msg = posix_strerror($error); if (!empty($error_msg)) { $error = '(' . $error . ') ' . $error_msg; } } if (!empty($error)) { $this->log(sprintf(__('System: %s', 'backwpup'), $error), E_USER_ERROR); } } $this->do_restart(true); }
/** * Do child cleanup if child exited and mark for restart on error * * @param array $signalInfo Returned from pcntl_sigtimedwait() */ protected function cleanupChild(array $signalInfo, $forceFinished = false) { if ($signalInfo['signo'] !== SIGCHLD) { throw new \InvalidArgumentException('Can only cleanup children on SIGCHLD event.'); } if (!isset($this->processPIDs[$signalInfo['pid']])) { throw new \InvalidArgumentException('Received SIGCHLD signal from unknown process "' . $signalInfo['pid'] . '"'); } $name = $this->processPIDs[$signalInfo['pid']]; // Cleanup child process $childStatus = null; if (($childPid = \pcntl_waitpid($signalInfo['pid'], $childStatus)) === -1) { $errno = \pcntl_get_last_error(); $error = \pcntl_strerror($errno); throw new \RuntimeException('Failed to collect child "' . $signalInfo['pid'] . '": (' . $errno . ') ' . $error); } unset($this->processPIDs[$signalInfo['pid']]); $this->processStarted[$name] = 0; // Abort on normal exit (status: 0) if ($forceFinished || \pcntl_wifexited($childStatus) && \pcntl_wexitstatus($childStatus) === 0) { $this->processFinished[$name] = \microtime(true); unset($this->processRestart[$name]); } else { $this->processRestart[$name] = \microtime(true) + (isset($this->processTimeouts[$name]) ? $this->processTimeouts[$name] : 0); } }