/** * 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 $callable * @param PromiseInterface $promise * @return mixed The return value of the thread * * Method where all the implementation logic is to run the callable asynchronously. * * It must call $promise's setPromisedData when finished. */ protected function runCallable(callable $callable, PromiseInterface $promise) { $this->callableToRun = $callable; fprintf(STDOUT, "Starting runCallable\n"); if (null !== $this->currentChildPid) { // The deamon is already started, change the callable to execute $this->setTerminated(false); $this->hasToBeRun = true; return; } // Start the deamon. $this->currentChildPid = pcntl_fork(); if (-1 === $this->currentChildPid) { throw new PcntlForkException(sprintf("The process could not be forked. Error message: %s", pcntl_get_last_error())); } elseif (0 === $this->currentChildPid) { fprintf(STDOUT, "Starting deamon\n"); fprintf(STDOUT, "Has to be run: %b\n", $this->hasToBeRun); while (true) { if ($this->hasToBeRun) { $this->currentChildPid = posix_getpid(); // Here is the child space try { $childValue = call_user_func($this->callableToRun); $succeed = true; } catch (\Throwable $e) { $childValue = $e; $succeed = false; } fprintf(STDOUT, "Got output: %s\n", $childValue); $promise->setPromisedData($childValue, $succeed); $this->setTerminated(); fprintf(STDOUT, "Terminated: %b\n", $this->isTerminated()); $this->hasToBeRun = false; } } } }
/** * @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); } }
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)); } } } } }
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; } }
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'); }
/** * 监控worker进程状态,退出重启 * @param resource $channel * @param int $flag * @param int $pid 退出的进程id * @return mixed */ public static function checkWorkerExit() { // 由于SIGCHLD信号可能重叠导致信号丢失,所以这里要循环获取所有退出的进程id while (($pid = pcntl_waitpid(-1, $status, WUNTRACED | WNOHANG)) != 0) { // 如果是重启的进程,则继续重启进程 if (isset(self::$workerToRestart[$pid]) && self::$serverStatus != self::STATUS_SHUTDOWN) { unset(self::$workerToRestart[$pid]); self::restartWorkers(); } // 出错 if ($pid == -1) { // 没有子进程了,可能是出现Fatal Err 了 if (pcntl_get_last_error() == 10) { self::notice('Server has no workers now'); } return -1; } // 查找子进程对应的woker_name $pid_workname_map = self::getPidWorkerNameMap(); $worker_name = isset($pid_workname_map[$pid]) ? $pid_workname_map[$pid] : ''; // 没找到worker_name说明出错了 哪里来的野孩子? if (empty($worker_name)) { self::notice("child exist but not found worker_name pid:{$pid}"); break; } // 进程退出状态不是0,说明有问题了 if ($status !== 0) { self::notice("worker exit status {$status} pid:{$pid} worker:{$worker_name}"); } // 记录进程退出状态 self::$serverStatusInfo['worker_exit_code'][$worker_name][$status] = isset(self::$serverStatusInfo['worker_exit_code'][$worker_name][$status]) ? self::$serverStatusInfo['worker_exit_code'][$worker_name][$status] + 1 : 1; // 更新状态到共享内存 self::updateStatusToShm(); // 清理这个进程的数据 self::clearWorker($worker_name, $pid); // 如果服务是不是关闭中 if (self::$serverStatus != self::STATUS_SHUTDOWN) { // 重新创建worker self::createWorkers(); } else { $all_worker_pid = self::getPidWorkerNameMap(); if (empty($all_worker_pid)) { // 删除共享内存 self::removeShmAndQueue(); // 发送提示 self::notice("Server stoped"); // 删除pid文件 @unlink(WORKERMAN_PID_FILE); exit(0); } } //end if } //end while }
/** * 监控worker进程状态,退出重启 * @param resource $channel * @param int $flag * @param int $pid 退出的进程id * @return mixed */ public static function checkWorkerExit() { // 由于SIGCHLD信号可能重叠导致信号丢失,所以这里要循环获取所有退出的进程id while (($pid = pcntl_waitpid(-1, $status, WUNTRACED | WNOHANG)) != 0) { // 如果是重启的进程,则继续重启进程 if (isset(self::$pidsToRestart[$pid]) && self::$serviceStatus != self::STATUS_SHUTDOWN) { unset(self::$pidsToRestart[$pid]); self::restartPids(); } // 出错 if ($pid < 0) { self::notice('pcntl_waitpid return ' . $pid . ' and pcntl_get_last_error = ' . pcntl_get_last_error()); return $pid; } // 查找子进程对应的woker_name $pid_workname_map = self::getPidWorkerNameMap(); $worker_name = isset($pid_workname_map[$pid]) ? $pid_workname_map[$pid] : ''; // 没找到worker_name说明出错了 if (empty($worker_name)) { self::notice("child exist but not found worker_name pid:{$pid}"); break; } // 进程退出状态不是0,说明有问题了 if ($status !== 0) { self::notice("worker[{$pid}:{$worker_name}] exit with status {$status}"); } // 记录进程退出状态 self::$serviceStatusInfo['worker_exit_code'][$worker_name][$status] = isset(self::$serviceStatusInfo['worker_exit_code'][$worker_name][$status]) ? self::$serviceStatusInfo['worker_exit_code'][$worker_name][$status] + 1 : 1; // 更新状态到共享内存 self::updateStatusToShm(); // 清理这个进程的数据 self::clearWorker($worker_name, $pid); // 如果服务是不是关闭中 if (self::$serviceStatus != self::STATUS_SHUTDOWN) { // 重新创建worker self::spawnWorkers(); } else { $all_worker_pid = self::getPidWorkerNameMap(); if (empty($all_worker_pid)) { // 删除共享内存 self::removeShmAndQueue(); // 发送提示 self::notice("Workerman stoped"); // 删除pid文件 @unlink(WORKERMAN_PID_FILE); exit(0); } } //end if } //end while }
/** * Get Last Error * * @return int Returns error code. */ public function getLastError() { return pcntl_get_last_error(); }
/** * * 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); }
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); }
/** * 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; } }
/** * 监控worker进程状态,退出重启 * @param resource $channel * @param int $flag * @param int $pid 退出的进程id */ public static function monitorWorkers($wait_pid = -1) { // 由于SIGCHLD信号可能重叠导致信号丢失,所以这里要循环获取所有退出的进程id while (($pid = pcntl_waitpid($wait_pid, $status, WUNTRACED | WNOHANG)) != 0) { // 如果是重启的进程,则继续重启进程 if (isset(self::$workerToRestart[$pid]) && self::$serverStatus != self::STATUS_SHUTDOWN) { Telnet::sendToAllClient("."); unset(self::$workerToRestart[$pid]); self::restartWorkers(); } // 出错 if ($pid == -1) { // 没有子进程了,可能是出现Fatal Err 了 if (pcntl_get_last_error() == 10) { self::notice('Server has no workers now'); } return -1; } // 查找子进程对应的woker_name $pid_workname_map = self::getPidWorkerNameMap(); $worker_name = isset($pid_workname_map[$pid]) ? $pid_workname_map[$pid] : ''; // 没找到worker_name说明出错了 哪里来的野孩子? if (empty($worker_name)) { self::notice("child exist but not found worker_name pid:{$pid}"); break; } // 进程退出状态不是0,说明有问题了 if ($status !== 0 && self::$serverStatus != self::STATUS_SHUTDOWN) { self::notice("worker exit status {$status} pid:{$pid} worker:{$worker_name}"); } // 记录进程退出状态 self::$serverStatusInfo['err_info'][$worker_name][$status] = isset(self::$serverStatusInfo['err_info'][$worker_name][$status]) ? self::$serverStatusInfo['err_info'][$worker_name][$status] + 1 : 1; // 清理这个进程的数据 self::clearWorker($worker_name, $pid); // 如果服务是不是关闭中 if (self::$serverStatus != self::STATUS_SHUTDOWN) { // 重新创建worker self::createWorkers(); // 开发环境需要上报监控文件给FileMonitor if ($worker_name == 'FileMonitor') { Reporter::reportIncludedFiles(self::$filesToInotify); } } else { $all_worker_pid = self::getPidWorkerNameMap(); if (empty($all_worker_pid)) { // 发送提示 self::notice("Server stoped"); // 关闭管理员socket Telnet::close(); // 删除pid文件 @unlink(PID_FILE); exit(0); } } //end if } //end while }
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; } }
/** * 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); } } }; }
/** * * 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); }
/** * 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; } }
<?php var_dump(pcntl_get_last_error()); $pid = pcntl_wait($status); var_dump($pid); var_dump(pcntl_get_last_error() == PCNTL_ECHILD);
/** * 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); } }