function minecraft_command($signal)
{
    global $_CONFIG, $_STATE;
    switch ($signal) {
        case SIGHUP:
            logmsg("SIGHUP - reload");
            $command = 'reload';
            break;
        case SIGUSR1:
            logmsg("SIGUSR1 - save-on");
            $command = 'save-on';
            break;
        case SIGUSR2:
            logmsg("SIGUSR2 - save-off");
            $command = 'save-off';
            break;
        case SIGALRM:
            logmsg("SIGALRM - save-all");
            $command = 'save-all';
            if (isset($_CONFIG['AlarmInterval'])) {
                pcntl_alarm($_CONFIG['AlarmInterval']);
            }
            break;
    }
    fwrite($_STATE['Descriptors'][0], $command . PHP_EOL);
    fflush($_STATE['Descriptors'][0]);
}
示例#2
0
 private function sleepUntilThereIsSomethingInteresting($timeLimit, $child)
 {
     pcntl_signal(SIGALRM, [$this, "alarm"], true);
     pcntl_alarm($timeLimit);
     pcntl_waitpid($child, $status);
     //pcntl_signal_dispatch();
 }
示例#3
0
 public function beat()
 {
     if (false === $this->stopped) {
         pcntl_alarm((int) 1);
         $this->tick();
     }
 }
 /**
  * Signal handler
  * 
  * @param  int $signalNumber
  * @return void
  */
 public function signalHandler($signalNumber)
 {
     echo 'Handling signal: #' . $signalNumber . PHP_EOL;
     global $consumer;
     switch ($signalNumber) {
         case SIGTERM:
             // 15 : supervisor default stop
         // 15 : supervisor default stop
         case SIGQUIT:
             // 3  : kill -s QUIT
             $consumer->stopHard();
             break;
         case SIGINT:
             // 2  : ctrl+c
             $consumer->stop();
             break;
         case SIGHUP:
             // 1  : kill -s HUP
             $consumer->restart();
             break;
         case SIGUSR1:
             // 10 : kill -s USR1
             // send an alarm in 1 second
             pcntl_alarm(1);
             break;
         case SIGUSR2:
             // 12 : kill -s USR2
             // send an alarm in 10 seconds
             pcntl_alarm(10);
             break;
         default:
             break;
     }
     return;
 }
示例#5
0
 protected function setSigAlarm()
 {
     pcntl_signal(SIGALRM, array(&$this, 'sigAlarmCallback'));
     $sec = $this->getSigAlarmTimeout();
     if ($sec > -1) {
         pcntl_alarm($this->getSigAlarmTimeout());
     }
 }
示例#6
0
 /**
  * Loads to configuration from the daemon options and installs signal
  * handlers.
  *
  * @param DaemonOptions $daemonOptions
  */
 private function setupDaemon(DaemonOptions $daemonOptions)
 {
     $this->requestCount = 0;
     $this->requestLimit = $daemonOptions->getOption(DaemonOptions::REQUEST_LIMIT);
     $this->memoryLimit = $daemonOptions->getOption(DaemonOptions::MEMORY_LIMIT);
     $timeLimit = $daemonOptions->getOption(DaemonOptions::TIME_LIMIT);
     if (DaemonOptions::NO_LIMIT !== $timeLimit) {
         pcntl_alarm($timeLimit);
     }
     $this->installSignalHandlers();
 }
 /**
  * Test stream_select signal interrupt doesn't trigger \RunTime exception.
  */
 public function testStreamSelectSignalInterrupt()
 {
     $address = 'tcp://localhost:7000';
     $serverSocket = stream_socket_server($address);
     $connectionPool = new StreamSocketConnectionPool($serverSocket);
     $alarmCalled = false;
     declare (ticks=1);
     pcntl_signal(SIGALRM, function () use(&$alarmCalled) {
         $alarmCalled = true;
     });
     pcntl_alarm(1);
     $connectionPool->getReadableConnections(2);
     $this->assertTrue($alarmCalled);
 }
示例#8
0
 public function setUp()
 {
     // Make this process a session leader so we can send signals
     // to this job as a whole (including any subprocesses such as spawned by Symfony).
     posix_setsid();
     if (function_exists('pcntl_alarm') && function_exists('pcntl_signal')) {
         if (!empty($this->args['sigFile'])) {
             echo sprintf('[-] Signal file requested, polling "%s".' . PHP_EOL, $this->args['sigFile']);
             declare (ticks=1);
             pcntl_signal(SIGALRM, [$this, 'alarmHandler']);
             pcntl_alarm(1);
         }
     }
     $this->updateStatus(DNDeployment::TR_DEPLOY);
     chdir(BASE_PATH);
 }
function pleac_Timing_Out_an_Operation()
{
    declare (ticks=1);
    $aborted = false;
    function handle_alarm($signal)
    {
        global $aborted;
        $aborted = true;
    }
    pcntl_signal(SIGALRM, 'handle_alarm');
    pcntl_alarm(3600);
    // long-time operations here
    pcntl_alarm(0);
    if ($aborted) {
        // timed out - do what you will here
    }
}
示例#10
0
 /**
  * Invokes a callable and raises an exception when the execution does not
  * finish before the specified timeout.
  *
  * @param  callable                 $callable
  * @param  array                    $arguments
  * @param  int                      $timeout   in seconds
  * @return mixed
  * @throws InvalidArgumentException
  */
 public function invoke($callable, array $arguments, $timeout)
 {
     if (!is_callable($callable)) {
         throw new InvalidArgumentException();
     }
     if (!is_integer($timeout)) {
         throw new InvalidArgumentException();
     }
     pcntl_signal(SIGALRM, array($this, 'callback'), TRUE);
     pcntl_alarm($timeout);
     $this->timeout = $timeout;
     try {
         $result = call_user_func_array($callable, $arguments);
     } catch (Exception $e) {
         pcntl_alarm(0);
         throw $e;
     }
     pcntl_alarm(0);
     return $result;
 }
示例#11
0
文件: Code.php 项目: crysalead/code
 /**
  * Executes a callable until a timeout is reached or the callable returns `true`.
  *
  * @param  Callable $callable The callable to execute.
  * @param  integer  $timeout  The timeout value.
  * @return mixed
  */
 public static function run($callable, $timeout = 0)
 {
     if (!is_callable($callable)) {
         throw new InvalidArgumentException();
     }
     $timeout = (int) $timeout;
     if (!function_exists('pcntl_signal')) {
         throw new Exception("PCNTL threading is not supported on your OS.");
     }
     pcntl_signal(SIGALRM, function ($signal) use($timeout) {
         throw new TimeoutException("Timeout reached, execution aborted after {$timeout} second(s).");
     }, true);
     pcntl_alarm($timeout);
     $result = null;
     try {
         $result = $callable();
     } catch (Exception $e) {
         throw $e;
     } finally {
         pcntl_alarm(0);
     }
     return $result;
 }
示例#12
0
 /**
  * 
  * 添加一个任务
  * 
  * @param int $time_long 多长时间运行一次 单位秒
  * @param callback $func 任务运行的函数或方法
  * @param mix $args 任务运行的函数或方法使用的参数
  * @return void
  */
 public static function add($time_long, $func, $args = array(), $persistent = true)
 {
     if ($time_long <= 0) {
         return false;
     }
     if (!is_callable($func)) {
         if (class_exists('\\Man\\Core\\Lib\\Log')) {
             \Man\Core\Lib\Log::add(var_export($func, true) . "not callable\n");
         }
         return false;
     }
     // 有任务时才出发计时器
     if (empty(self::$tasks)) {
         pcntl_alarm(1);
     }
     $time_now = time();
     $run_time = $time_now + $time_long;
     if (!isset(self::$tasks[$run_time])) {
         self::$tasks[$run_time] = array();
     }
     self::$tasks[$run_time][] = array($func, $args, $persistent, $time_long);
     return true;
 }
示例#13
0
 /**
  * 运行守护进程,监控有变化的日志文件。
  * 
  * @see ZtChart_Model_Monitor_Abstract::daemon()
  */
 public function run()
 {
     if (0 == ($pid = pcntl_fork())) {
         // 子进程负责处理当前日志
         try {
             $this->tail($this->_console->getLogPaths());
         } catch (ZtChart_Model_Monitor_Exception $e) {
             $this->_logger->err($e->getMessage());
             exit(1);
         }
     } else {
         if (0 < $pid) {
             pcntl_setpriority(-1);
             // 父进程负责把子进产生的数据写入数据库
             if (pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD, SIGALRM, SIGINT, SIGTERM))) {
                 $interval = 10;
                 // 10秒写一次
                 pcntl_alarm($interval);
                 while ($signo = pcntl_sigwaitinfo(array(SIGCHLD, SIGALRM, SIGINT, SIGTERM))) {
                     if (SIGALRM == $signo) {
                         pcntl_alarm($interval);
                     }
                     try {
                         $this->extract($interval);
                     } catch (ZtChart_Model_Monitor_Exception $e) {
                         posix_kill($pid, 9);
                         exit(1);
                     }
                     if (SIGCHLD == $signo || SIGINT == $signo || SIGTERM == $signo) {
                         break;
                     }
                 }
             }
         }
     }
     exit(0);
 }
示例#14
0
 /**
  * Remove all timers.
  * @return void
  */
 public static function delAll()
 {
     self::$_tasks = array();
     pcntl_alarm(0);
     if (self::$_event) {
         self::$_event->clearAllTimer();
     }
 }
示例#15
0
 /**
  * 处理内部通讯收到的数据
  * @param event_buffer $event_buffer
  * @param int $fd
  * @return void
  */
 public function recvInnerTcp($connection, $flag, $fd = null)
 {
     $this->currentDealFd = $fd;
     $buffer = stream_socket_recvfrom($connection, $this->recvBuffers[$fd]['remain_len']);
     // 出错了
     if ('' == $buffer && '' == ($buffer = fread($connection, $this->recvBuffers[$fd]['remain_len']))) {
         // 判断是否是链接断开
         if (!feof($connection)) {
             return;
         }
         // 如果该链接对应的buffer有数据,说明发生错误
         if (!empty($this->recvBuffers[$fd]['buf'])) {
             $this->statusInfo['send_fail']++;
         }
         // 关闭链接
         $this->closeInnerClient($fd);
         $this->notice("CLIENT:" . $this->getRemoteIp() . " CLOSE INNER_CONNECTION\n");
         if ($this->workerStatus == self::STATUS_SHUTDOWN) {
             $this->stop();
         }
         return;
     }
     $this->recvBuffers[$fd]['buf'] .= $buffer;
     $remain_len = $this->dealInnerInput($this->recvBuffers[$fd]['buf']);
     // 包接收完毕
     if (0 === $remain_len) {
         // 内部通讯业务处理
         $this->innerDealProcess($this->recvBuffers[$fd]['buf']);
         $this->recvBuffers[$fd] = array('buf' => '', 'remain_len' => GatewayProtocol::HEAD_LEN);
     } else {
         if (false === $remain_len) {
             // 出错
             $this->statusInfo['packet_err']++;
             $this->notice("INNER_PACKET_ERROR and CLOSE_INNER_CONNECTION\nCLIENT_IP:" . $this->getRemoteIp() . "\nBUFFER:[" . bin2hex($this->recvBuffers[$fd]['buf']) . "]\n");
             $this->closeInnerClient($fd);
         } else {
             $this->recvBuffers[$fd]['remain_len'] = $remain_len;
         }
     }
     // 检查是否是关闭状态或者是否到达请求上限
     if ($this->workerStatus == self::STATUS_SHUTDOWN) {
         // 停止服务
         $this->stop();
         // EXIT_WAIT_TIME秒后退出进程
         pcntl_alarm(self::EXIT_WAIT_TIME);
     }
 }
 /**
  * Test seeding
  *
  * @return void
  */
 public function testSeeding()
 {
     $this->torrent_file = $this->createTorrentFile();
     $this->download_destination = $this->createDownloadDestination();
     $this->seed_server_pid = $this->startSeedServer();
     $this->torrent_client_pid = $this->startTorrentClient();
     // We don't want to wait forever.
     $self = $this;
     pcntl_signal(SIGALRM, function () use($self) {
         $self->fail('Test timed out.');
     });
     pcntl_alarm(self::TEST_TIMEOUT);
     $pid_exit = pcntl_wait($status);
     switch ($pid_exit) {
         case -1:
             $this->fail('Error in child processes.');
             break;
         case $this->seed_server_pid:
             unset($this->seed_server_pid);
             $this->fail('Seed server exited.');
             break;
         case $this->torrent_client_pid:
             unset($this->torrent_client_pid);
             break;
     }
     $download_path = $this->download_destination . '/' . self::FILE_TO_DOWNLOAD;
     $this->assertFileExists($download_path);
     $downloaded_hash = sha1_file($download_path);
     $expected_hash = sha1_file(dirname(__FILE__) . '/../Fixtures/' . self::FILE_TO_DOWNLOAD);
     $this->assertEquals($expected_hash, $downloaded_hash);
 }
示例#17
0
 public function forExactly($duration)
 {
     // what are we doing?
     $log = usingLog()->startAction("run for exactly '{$duration}'");
     // remember the duration
     //
     // the $action callback can then make use of it
     $this->duration = $duration;
     // convert the duration into seconds
     $interval = new DateInterval($duration);
     $seconds = $interval->getTotalSeconds();
     // set the alarm
     pcntl_signal(SIGALRM, array($this, "handleSigAlarm"), FALSE);
     $log->addStep("setting SIGALRM for '{$seconds}' seconds", function () use($seconds) {
         pcntl_alarm($seconds);
     });
     declare (ticks=1);
     $callback = $this->action;
     $returnVal = $callback($this);
     // all done
     $log->endAction();
     return $returnVal;
 }
示例#18
0
 /**
  * Обработка сигналов Мастером.
  * 
  * @param int $signo Код сигнала
  * @param mixed $pid Идентификатор процесса для обработки
  * @param mixed $status Статус завершения процесса
  * @throws \Ganzal\Lulz\Pinger\Assets\Exceptions\Master_Bad_Signal_Exception
  * @return void
  * @access protected
  * @static
  */
 protected static function masterSignalHandler($signo, $pid = null, $status = null)
 {
     DEBUG && printf("Master::masterSignalHandler(%s, %s, %s): begin\n", var_export($signo, true), var_export($pid, true), var_export($status, true));
     switch ($signo) {
         case SIGHUP:
         case SIGINT:
         case SIGQUIT:
         case SIGTERM:
         case SIGABRT:
             DEBUG && (print "Master::masterSignalHandler(): master_loop = false\n");
             static::$master_loop = false;
             pcntl_signal_dispatch();
             break;
         case SIGALRM:
         case SIGCHLD:
         case SIGCLD:
             // Пид не указан - сигнал от системы. Уточняем кто умер.
             if (!$pid) {
                 $pid = pcntl_waitpid(-1, $status, WNOHANG);
             }
             // Дожидаемся окончания очереди умерших.
             while ($pid > 0) {
                 if ($pid && isset(static::$jobs[$pid])) {
                     DEBUG && (print "Master::masterSignalHandler(): ((**))\n");
                     $exitCode = pcntl_wexitstatus($status);
                     if ($exitCode != 0) {
                         DEBUG && printf("Master::masterSignalHandler(): %d exited with status %d\n", $pid, $exitCode);
                     }
                     // этот демон не требует возврата слотов в пул
                     //array_push(static::$pid_slots, static::$jobs[$pid]);
                     // удаление завершенного процесса из списка
                     unset(static::$jobs[$pid]);
                 } elseif ($pid) {
                     DEBUG && (print "Master::masterSignalHandler(): (())\n");
                     // пид указан, но не в нашем списке детишек!
                     // запишем в очередь сигналов, вдруг чо
                     DEBUG && printf("Master::masterSignalHandler(): adding %d to the signal queue \n", $pid);
                     static::$sig_queue[$pid] = $status;
                 }
                 $pid = pcntl_waitpid(-1, $status, WNOHANG);
             }
             pcntl_alarm(10);
             break;
         default:
             throw new Exceptions\Master_Bad_Signal_Exception($signo);
     }
     // switch($signo)
     DEBUG && (print "Master::masterSignalHandler(): end\n\n");
 }
示例#19
0
 /**
  * Register the worker timeout handler (PHP 7.1+).
  *
  * @param  \Illuminate\Contracts\Queue\Job|null  $job
  * @param  WorkerOptions  $options
  * @return void
  */
 protected function registerTimeoutHandler($job, WorkerOptions $options)
 {
     if (version_compare(PHP_VERSION, '7.1.0') < 0 || !extension_loaded('pcntl')) {
         return;
     }
     $timeout = $job && !is_null($job->timeout()) ? $job->timeout() : $options->timeout;
     pcntl_async_signals(true);
     pcntl_signal(SIGALRM, function () {
         $this->exceptions->report(new TimeoutException('A queue worker timed out while processing a job.'));
         exit(1);
     });
     pcntl_alarm($timeout + $options->sleep);
 }
示例#20
0
 /**
  * 处理受到的数据
  * @param event_buffer $event_buffer
  * @param int $fd
  * @return void
  */
 public function dealInputBase($connection, $length, $buffer, $fd = null)
 {
     $this->currentDealFd = $fd;
     // 出错了
     if ($length == 0) {
         if (feof($connection)) {
             // 客户端提前断开链接
             $this->statusInfo['client_close']++;
         } else {
             // 超时了
             $this->statusInfo['recv_timeout']++;
         }
         $this->closeClient($fd);
         if ($this->workerStatus == self::STATUS_SHUTDOWN) {
             $this->stopServe();
         }
         return;
     }
     if (isset($this->recvBuffers[$fd])) {
         $buffer = $this->recvBuffers[$fd] . $buffer;
     }
     $remain_len = $this->dealInput($buffer);
     // 包接收完毕
     if (0 === $remain_len) {
         // 逻辑超时处理,逻辑只能执行xxs,xxs后超时放弃当前请求处理下一个请求
         pcntl_alarm(ceil($this->processTimeout / 1000));
         // 执行处理
         try {
             declare (ticks=1);
             // 业务处理
             $this->dealProcess($buffer);
             // 关闭闹钟
             pcntl_alarm(0);
         } catch (Exception $e) {
             // 关闭闹钟
             pcntl_alarm(0);
             if ($e->getCode() != self::CODE_PROCESS_TIMEOUT) {
                 $this->notice($e->getMessage() . ":\n" . $e->getTraceAsString());
                 $this->statusInfo['throw_exception']++;
                 $this->sendToClient($e->getMessage());
             }
         }
         // 是否是长连接
         if ($this->isPersistentConnection) {
             // 清空缓冲buffer
             unset($this->recvBuffers[$fd]);
         } else {
             // 关闭链接
             $this->closeClient($fd);
         }
     } else {
         if (false === $remain_len) {
             // 出错
             $this->statusInfo['packet_err']++;
             $this->sendToClient('packet_err:' . $buffer);
             $this->notice('packet_err:' . $buffer);
             $this->closeClient($fd);
         } else {
             $this->recvBuffers[$fd] = $buffer;
         }
     }
     // 检查是否到达请求上限或者服务是否是关闭状态
     if ($this->statusInfo['total_request'] >= $this->maxRequests || $this->workerStatus == self::STATUS_SHUTDOWN) {
         // 停止服务
         $this->stopServe();
         // 5秒后退出进程
         pcntl_alarm(self::EXIT_WAIT_TIME);
     }
 }
 static function setAlarm($f = SIG_IGN, $seconds = 0)
 {
     pcntl_signal(SIGALRM, $f);
     return pcntl_alarm($seconds);
 }
示例#22
0
 /**
  * 设置server信号处理函数
  * @param null $null
  * @param int $signal
  */
 public function signalHandler($signal, $null = null, $null = null)
 {
     switch ($signal) {
         // 时钟处理函数
         case SIGALRM:
             // 停止服务后EXIT_WAIT_TIME秒还没退出则强制退出
             if ($this->workerStatus == self::STATUS_SHUTDOWN) {
                 exit(0);
             }
             break;
             // 停止该进程
         // 停止该进程
         case SIGINT:
             $this->stop();
             // EXIT_WAIT_TIME秒后退出进程
             pcntl_alarm(self::EXIT_WAIT_TIME);
             break;
             // 平滑重启
         // 平滑重启
         case SIGHUP:
             $this->onReload();
             // 如果配置了no_reload则不重启该进程
             if (\Man\Core\Lib\Config::get($this->workerName . '.no_reload')) {
                 return;
             }
             $this->stop();
             // EXIT_WAIT_TIME秒后退出进程
             pcntl_alarm(self::EXIT_WAIT_TIME);
             break;
             // 报告进程状态
         // 报告进程状态
         case SIGUSR1:
             $this->writeStatusToQueue();
             break;
             // 报告进程使用的php文件
         // 报告进程使用的php文件
         case SIGUSR2:
             $this->writeFilesListToQueue();
             break;
             // FileMonitor检测到终端已经关闭,向此进程发送SIGTTOU信号,关闭此进程的标准输入输出
         // FileMonitor检测到终端已经关闭,向此进程发送SIGTTOU信号,关闭此进程的标准输入输出
         case SIGTTOU:
             $this->resetFd();
             break;
     }
 }
示例#23
0
function commandStart()
{
    global $_CONFIG, $_STATE;
    // Set up the descriptors for the process
    $descriptors = array(0 => array('pipe', 'r'), 1 => array('file', $_CONFIG['Stdout'], 'a'), 2 => array('file', $_CONFIG['Stderr'], 'a'));
    // Set the current working directory
    $cwd = $_CONFIG['WorkingDirectory'];
    // Set up the environment variables
    $env = $_CONFIG['Environment'];
    // Set the effective uid/gid so we spawn the process as the correct user.
    posix_setegid($_CONFIG['GID']);
    posix_seteuid($_CONFIG['UID']);
    $_STATE['ProcessHandle'] = proc_open($_CONFIG['Command'], $descriptors, $_STATE['Descriptors'], $cwd, $env);
    if (!isset($_STATE['ProcessHandle']) || !is_resource($_STATE['ProcessHandle'])) {
        throw new Exception("Could not start command.");
    }
    // Reset the effective uid/gid
    posix_setegid(0);
    posix_seteuid(0);
    $_STATE['Status'] = proc_get_status($_STATE['ProcessHandle']);
    if (isset($_CONFIG['Command_Pidfile'])) {
        file_put_contents($_CONFIG['Command_Pidfile'], $_STATE['Status']['pid']);
    }
    if (isset($_CONFIG['AlarmInterval'])) {
        pcntl_alarm($_CONFIG['AlarmInterval']);
    }
}
示例#24
0
 /**
  * @return PromiseInterface
  */
 protected function startup()
 {
     pcntl_alarm(30);
     $this->registerSigHandlers();
     $this->pruneDeadWorkers();
     return $this->registerWorker();
 }
示例#25
0
文件: Worker.php 项目: bezk/workers
 /**
  * Выполняет работу воркера
  *
  * @return void
  */
 private function worker()
 {
     $this->iPID = getmypid();
     $this->aMeta['time']['start'] = round(microtime(true), 2);
     // ключ для вызова в функции пользовательской
     $this->mInstanceData = (bool) $this->aWorkersData ? $this->aWorkersData[$this->iNumberWorker - 1] : null;
     if (function_exists('cli_set_process_title') && isset($_SERVER['TERM_PROGRAM']) && $_SERVER['TERM_PROGRAM'] !== 'Apple_Terminal') {
         cli_set_process_title($this->sFilesBasename . '.' . $this->sInstanceName);
     }
     $this->setStreamsIO($this->sInstanceName);
     if ($this->bVerbose) {
         $this->writeInLog($this->rVerboseStream, 'PID: ' . $this->iPID);
     }
     // установка приоритета на процесс
     if ($this->iWorkerPriority) {
         if ($this->bVerbose) {
             $this->writeInLog($this->rVerboseStream, 'Set process priority equal ' . $this->iWorkerPriority);
         }
         $this->setPriority($this->iWorkerPriority);
     }
     // лимит на время исполнения дочернего процесса
     if ($this->iMaxExecutionTimeInSeconds) {
         if ($this->bVerbose) {
             $this->writeInLog($this->rVerboseStream, 'Set process lifetime ' . $this->iMaxExecutionTimeInSeconds . ' seconds');
         }
         // считает время работы скрипта
         set_time_limit($this->iMaxExecutionTimeInSeconds);
         // считает время исполнения вне скрипта (потоки, коннекты, sleep, etc)
         pcntl_alarm($this->iMaxExecutionTimeInSeconds);
     }
     if ($this->bVerbose) {
         $this->writeInLog($this->rVerboseStream, 'Started');
     }
     // перехватываем вывод
     ob_start();
     // запускаем функцию
     if ($this->cExecutingFunction) {
         call_user_func($this->cExecutingFunction, $this->mInstanceData);
         // успешно выходим
         exit(0);
     }
 }
示例#26
0
文件: CLI.php 项目: erebot/erebot
 /**
  * Entry-point for Erebot.
  *
  * \return
  *      This method never returns.
  *      Instead, the program exits with an appropriate
  *      return code when Erebot is stopped.
  */
 public static function run()
 {
     // Apply patches.
     \Erebot\Patches::patch();
     // Load the configuration for the Dependency Injection Container.
     $dic = new \Symfony\Component\DependencyInjection\ContainerBuilder();
     $dic->setParameter('Erebot.src_dir', __DIR__);
     $loader = new \Symfony\Component\DependencyInjection\Loader\XmlFileLoader($dic, new \Symfony\Component\Config\FileLocator(getcwd()));
     $dicConfig = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'defaults.xml';
     $dicCwdConfig = getcwd() . DIRECTORY_SEPARATOR . 'defaults.xml';
     if (!strncasecmp(__FILE__, 'phar://', 7)) {
         if (!file_exists($dicCwdConfig)) {
             copy($dicConfig, $dicCwdConfig);
         }
         $dicConfig = $dicCwdConfig;
     } elseif (file_exists($dicCwdConfig)) {
         $dicConfig = $dicCwdConfig;
     }
     $loader->load($dicConfig);
     // Determine availability of PHP extensions
     // needed by some of the command-line options.
     $hasPosix = in_array('posix', get_loaded_extensions());
     $hasPcntl = in_array('pcntl', get_loaded_extensions());
     $logger = $dic->get('logging');
     $localeGetter = $dic->getParameter('i18n.default_getter');
     $coreTranslatorCls = $dic->getParameter('core.classes.i18n');
     $translator = new $coreTranslatorCls("Erebot\\Core");
     $categories = array('LC_MESSAGES', 'LC_MONETARY', 'LC_TIME', 'LC_NUMERIC');
     foreach ($categories as $category) {
         $locales = call_user_func($localeGetter);
         $locales = empty($locales) ? array() : array($locales);
         $localeSources = array('LANGUAGE' => true, 'LC_ALL' => false, $category => false, 'LANG' => false);
         foreach ($localeSources as $source => $multiple) {
             if (!isset($_SERVER[$source])) {
                 continue;
             }
             if ($multiple) {
                 $locales = explode(':', $_SERVER[$source]);
             } else {
                 $locales = array($_SERVER[$source]);
             }
             break;
         }
         $translator->setLocale($translator->nameToCategory($category), $locales);
     }
     // Also, include some information about the version
     // of currently loaded PHAR modules, if any.
     $version = 'dev-master';
     if (!strncmp(__FILE__, 'phar://', 7)) {
         $phar = new \Phar(\Phar::running(true));
         $md = $phar->getMetadata();
         $version = $md['version'];
     }
     if (defined('Erebot_PHARS')) {
         $phars = unserialize(Erebot_PHARS);
         ksort($phars);
         foreach ($phars as $module => $metadata) {
             if (strncasecmp($module, 'Erebot_Module_', 14)) {
                 continue;
             }
             $version .= "\n  with {$module} version {$metadata['version']}";
         }
     }
     \Console_CommandLine::registerAction('StoreProxy', '\\Erebot\\Console\\StoreProxyAction');
     $parser = new \Console_CommandLine(array('name' => 'Erebot', 'description' => $translator->gettext('A modular IRC bot written in PHP'), 'version' => $version, 'add_help_option' => true, 'add_version_option' => true, 'force_posix' => false));
     $parser->accept(new \Erebot\Console\MessageProvider());
     $parser->renderer->options_on_different_lines = true;
     $defaultConfigFile = getcwd() . DIRECTORY_SEPARATOR . 'Erebot.xml';
     $parser->addOption('config', array('short_name' => '-c', 'long_name' => '--config', 'description' => $translator->gettext('Path to the configuration file to use instead ' . 'of "Erebot.xml", relative to the current ' . 'directory.'), 'help_name' => 'FILE', 'action' => 'StoreString', 'default' => $defaultConfigFile));
     $parser->addOption('daemon', array('short_name' => '-d', 'long_name' => '--daemon', 'description' => $translator->gettext('Run the bot in the background (daemon).' . ' [requires the POSIX and pcntl extensions]'), 'action' => 'StoreTrue'));
     $noDaemon = new \Erebot\Console\ParallelOption('no_daemon', array('short_name' => '-n', 'long_name' => '--no-daemon', 'description' => $translator->gettext('Do not run the bot in the background. ' . 'This is the default, unless the -d option ' . 'is used or the bot is configured otherwise.'), 'action' => 'StoreProxy', 'action_params' => array('option' => 'daemon')));
     $parser->addOption($noDaemon);
     $parser->addOption('pidfile', array('short_name' => '-p', 'long_name' => '--pidfile', 'description' => $translator->gettext("Store the bot's PID in this file."), 'help_name' => 'FILE', 'action' => 'StoreString', 'default' => null));
     $parser->addOption('group', array('short_name' => '-g', 'long_name' => '--group', 'description' => $translator->gettext('Set group identity to this GID/group during ' . 'startup. The default is to NOT change group ' . 'identity, unless configured otherwise.' . ' [requires the POSIX extension]'), 'help_name' => 'GROUP/GID', 'action' => 'StoreString', 'default' => null));
     $parser->addOption('user', array('short_name' => '-u', 'long_name' => '--user', 'description' => $translator->gettext('Set user identity to this UID/username during ' . 'startup. The default is to NOT change user ' . 'identity, unless configured otherwise.' . ' [requires the POSIX extension]'), 'help_name' => 'USER/UID', 'action' => 'StoreString', 'default' => null));
     try {
         $parsed = $parser->parse();
     } catch (\Exception $exc) {
         $parser->displayError($exc->getMessage());
         exit(1);
     }
     // Parse the configuration file.
     $config = new \Erebot\Config\Main($parsed->options['config'], \Erebot\Config\Main::LOAD_FROM_FILE, $translator);
     $coreCls = $dic->getParameter('core.classes.core');
     $bot = new $coreCls($config, $translator);
     $dic->set('bot', $bot);
     // Use values from the XML configuration file
     // if there is no override from the command line.
     $overrides = array('daemon' => 'mustDaemonize', 'group' => 'getGroupIdentity', 'user' => 'getUserIdentity', 'pidfile' => 'getPidfile');
     foreach ($overrides as $option => $func) {
         if ($parsed->options[$option] === null) {
             $parsed->options[$option] = $config->{$func}();
         }
     }
     /* Handle daemonization.
      * See also:
      * - http://www.itp.uzh.ch/~dpotter/howto/daemonize
      * - http://andytson.com/blog/2010/05/daemonising-a-php-cli-script
      */
     if ($parsed->options['daemon']) {
         if (!$hasPosix) {
             $logger->error($translator->gettext('The posix extension is required in order ' . 'to start the bot in the background'));
             exit(1);
         }
         if (!$hasPcntl) {
             $logger->error($translator->gettext('The pcntl extension is required in order ' . 'to start the bot in the background'));
             exit(1);
         }
         foreach (array('SIGCHLD', 'SIGUSR1', 'SIGALRM') as $signal) {
             if (defined($signal)) {
                 pcntl_signal(constant($signal), array(__CLASS__, 'startupSighandler'));
             }
         }
         $logger->info($translator->gettext('Starting the bot in the background...'));
         $pid = pcntl_fork();
         if ($pid < 0) {
             $logger->error($translator->gettext('Could not start in the background (unable to fork)'));
             exit(1);
         }
         if ($pid > 0) {
             pcntl_wait($dummy, WUNTRACED);
             pcntl_alarm(2);
             pcntl_signal_dispatch();
             exit(1);
         }
         $parent = posix_getppid();
         // Ignore some of the signals.
         foreach (array('SIGTSTP', 'SIGTOU', 'SIGTIN', 'SIGHUP') as $signal) {
             if (defined($signal)) {
                 pcntl_signal(constant($signal), SIG_IGN);
             }
         }
         // Restore the signal handlers we messed with.
         foreach (array('SIGCHLD', 'SIGUSR1', 'SIGALRM') as $signal) {
             if (defined($signal)) {
                 pcntl_signal(constant($signal), SIG_DFL);
             }
         }
         umask(0);
         if (umask() != 0) {
             $logger->warning($translator->gettext('Could not change umask'));
         }
         if (posix_setsid() == -1) {
             $logger->error($translator->gettext('Could not start in the background (unable to setsid)'));
             exit(1);
         }
         // Prevent the child from ever acquiring a controlling terminal.
         // Not required under Linux, but required by at least System V.
         $pid = pcntl_fork();
         if ($pid < 0) {
             $logger->error($translator->gettext('Could not start in the background (unable to fork)'));
             exit(1);
         }
         if ($pid > 0) {
             exit(0);
         }
         // Avoid locking up the current directory.
         if (!chdir(DIRECTORY_SEPARATOR)) {
             $logger->error($translator->gettext('Could not chdir to "%(path)s"'), array('path' => DIRECTORY_SEPARATOR));
         }
         // Explicitly close the magic stream-constants (just in case).
         foreach (array('STDIN', 'STDOUT', 'STDERR') as $stream) {
             if (defined($stream)) {
                 fclose(constant($stream));
             }
         }
         // Re-open them with the system's blackhole.
         /**
          * \todo
          *      should be made portable, but the requirement on the POSIX
          *      extension prevents this, so this is okay for now.
          */
         $stdin = fopen('/dev/null', 'r');
         $stdout = fopen('/dev/null', 'w');
         $stderr = fopen('/dev/null', 'w');
         if (defined('SIGUSR1')) {
             posix_kill($parent, SIGUSR1);
         }
         $logger->info($translator->gettext('Successfully started in the background'));
     }
     try {
         /// @TODO: Check the interface or something like that.
         $identd = $dic->get('identd');
     } catch (\InvalidArgumentException $e) {
         $identd = null;
     }
     try {
         /// @TODO: Check the interface or something like that.
         $prompt = $dic->get('prompt');
     } catch (\InvalidArgumentException $e) {
         $prompt = null;
     }
     // Change group identity if necessary.
     if ($parsed->options['group'] !== null && $parsed->options['group'] != '') {
         if (!$hasPosix) {
             $logger->warning($translator->gettext('The posix extension is needed in order ' . 'to change group identity.'));
         } elseif (posix_getuid() !== 0) {
             $logger->warning($translator->gettext('Only root can change group identity! ' . 'Your current UID is %(uid)d'), array('uid' => posix_getuid()));
         } else {
             if (ctype_digit($parsed->options['group'])) {
                 $info = posix_getgrgid((int) $parsed->options['group']);
             } else {
                 $info = posix_getgrnam($parsed->options['group']);
             }
             if ($info === false) {
                 $logger->error($translator->gettext('No such group "%(group)s"'), array('group' => $parsed->options['group']));
                 exit(1);
             }
             if (!posix_setgid($info['gid'])) {
                 $logger->error($translator->gettext('Could not set group identity ' . 'to "%(name)s" (%(id)d)'), array('id' => $info['gid'], 'name' => $info['name']));
                 exit(1);
             }
             $logger->debug($translator->gettext('Successfully changed group identity ' . 'to "%(name)s" (%(id)d)'), array('name' => $info['name'], 'id' => $info['gid']));
         }
     }
     // Change user identity if necessary.
     if ($parsed->options['user'] !== null || $parsed->options['user'] != '') {
         if (!$hasPosix) {
             $logger->warning($translator->gettext('The posix extension is needed in order ' . 'to change user identity.'));
         } elseif (posix_getuid() !== 0) {
             $logger->warning($translator->gettext('Only root can change user identity! ' . 'Your current UID is %(uid)d'), array('uid' => posix_getuid()));
         } else {
             if (ctype_digit($parsed->options['user'])) {
                 $info = posix_getpwuid((int) $parsed->options['user']);
             } else {
                 $info = posix_getpwnam($parsed->options['user']);
             }
             if ($info === false) {
                 $logger->error($translator->gettext('No such user "%(user)s"'), array('user' => $parsed->options['user']));
                 exit(1);
             }
             if (!posix_setuid($info['uid'])) {
                 $logger->error($translator->gettext('Could not set user identity ' . 'to "%(name)s" (%(id)d)'), array('name' => $info['name'], 'id' => $info['uid']));
                 exit(1);
             }
             $logger->debug($translator->gettext('Successfully changed user identity ' . 'to "%(name)s" (%(id)d)'), array('name' => $info['name'], 'id' => $info['uid']));
         }
     }
     // Write new pidfile.
     if ($parsed->options['pidfile'] !== null && $parsed->options['pidfile'] != '') {
         $pid = @file_get_contents($parsed->options['pidfile']);
         // If the file already existed, the bot may already be started
         // or it may contain data not related to Erebot at all.
         if ($pid !== false) {
             $pid = (int) rtrim($pid);
             if (!$pid) {
                 $logger->error($translator->gettext('The pidfile (%(pidfile)s) contained garbage. ' . 'Exiting'), array('pidfile' => $parsed->options['pidfile']));
                 exit(1);
             } else {
                 posix_kill($pid, 0);
                 $res = posix_errno();
                 switch ($res) {
                     case 0:
                         // No error.
                         $logger->error($translator->gettext('Erebot is already running ' . 'with PID %(pid)d'), array('pid' => $pid));
                         exit(1);
                     case 3:
                         // ESRCH.
                         $logger->warning($translator->gettext('Found stalled PID %(pid)d in pidfile ' . '"%(pidfile)s". Removing it'), array('pidfile' => $parsed->options['pidfile'], 'pid' => $pid));
                         @unlink($parsed->options['pidfile']);
                         break;
                     case 1:
                         // EPERM.
                         $logger->error($translator->gettext('Found another program\'s PID %(pid)d in ' . 'pidfile "%(pidfile)s". Exiting'), array('pidfile' => $parsed->options['pidfile'], 'pid' => $pid));
                         exit(1);
                     default:
                         $logger->error($translator->gettext('Unknown error while checking for ' . 'the existence of another running ' . 'instance of Erebot (%(error)s)'), array('error' => posix_get_last_error()));
                         exit(1);
                 }
             }
         }
         $pidfile = fopen($parsed->options['pidfile'], 'wt');
         flock($pidfile, LOCK_EX | LOCK_NB, $wouldBlock);
         if ($wouldBlock) {
             $logger->error($translator->gettext('Could not lock pidfile (%(pidfile)s). ' . 'Is the bot already running?'), array('pidfile' => $parsed->options['pidfile']));
             exit(1);
         }
         $pid = sprintf("%u\n", getmypid());
         $res = fwrite($pidfile, $pid);
         if ($res !== strlen($pid)) {
             $logger->error($translator->gettext('Unable to write PID to pidfile (%(pidfile)s)'), array('pidfile' => $parsed->options['pidfile']));
             exit(1);
         }
         $logger->debug($translator->gettext('PID (%(pid)d) written into %(pidfile)s'), array('pidfile' => $parsed->options['pidfile'], 'pid' => getmypid()));
         // Register a callback to remove the pidfile upon exit.
         register_shutdown_function(array(__CLASS__, 'cleanupPidfile'), $pidfile, $parsed->options['pidfile']);
     }
     // Display a desperate warning when run as user root.
     if ($hasPosix && posix_getuid() === 0) {
         $logger->warning($translator->gettext('You SHOULD NOT run Erebot as root!'));
     }
     if ($identd !== null) {
         $identd->connect();
     }
     if ($prompt !== null) {
         $prompt->connect();
     }
     // This doesn't return until we purposely
     // make the bot drop all active connections.
     $bot->start($dic->get('factory.connection'));
     exit(0);
 }
示例#27
0
文件: Pcntl.php 项目: dantudor/pcntl
 /**
  * Alarm
  *
  * @param int $seconds The number of seconds to wait. If seconds is zero, no new alarm is created.
  *
  * @return int The time in seconds that any previously scheduled alarm had remaining before it was to be delivered, or 0 if there was no previously scheduled alarm.
  */
 public function alarm($seconds)
 {
     return pcntl_alarm($seconds);
 }
示例#28
0
/**
 * Update a feed batch.
 * Used by daemons to update n feeds by run.
 * Only update feed needing a update, and not being processed
 * by another process.
 * 
 * @param mixed $link Database link
 * @param integer $limit Maximum number of feeds in update batch. Default to DAEMON_FEED_LIMIT.
 * @param boolean $from_http Set to true if you call this function from http to disable cli specific code.
 * @param boolean $debug Set to false to disable debug output. Default to true.
 * @return void
 */
function update_daemon_common($link, $limit = DAEMON_FEED_LIMIT, $from_http = false, $debug = true)
{
    // Process all other feeds using last_updated and interval parameters
    // Test if the user has loggued in recently. If not, it does not update its feeds.
    if (DAEMON_UPDATE_LOGIN_LIMIT > 0) {
        if (DB_TYPE == "pgsql") {
            $login_thresh_qpart = "AND ttrss_users.last_login >= NOW() - INTERVAL '" . DAEMON_UPDATE_LOGIN_LIMIT . " days'";
        } else {
            $login_thresh_qpart = "AND ttrss_users.last_login >= DATE_SUB(NOW(), INTERVAL " . DAEMON_UPDATE_LOGIN_LIMIT . " DAY)";
        }
    } else {
        $login_thresh_qpart = "";
    }
    // Test if the feed need a update (update interval exceded).
    if (DB_TYPE == "pgsql") {
        $update_limit_qpart = "AND ((\n\t\t\t\t\tttrss_feeds.update_interval = 0\n\t\t\t\t\tAND ttrss_feeds.last_updated < NOW() - CAST((ttrss_user_prefs.value || ' minutes') AS INTERVAL)\n\t\t\t\t) OR (\n\t\t\t\t\tttrss_feeds.update_interval > 0\n\t\t\t\t\tAND ttrss_feeds.last_updated < NOW() - CAST((ttrss_feeds.update_interval || ' minutes') AS INTERVAL)\n\t\t\t\t) OR ttrss_feeds.last_updated IS NULL)";
    } else {
        $update_limit_qpart = "AND ((\n\t\t\t\t\tttrss_feeds.update_interval = 0\n\t\t\t\t\tAND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(ttrss_user_prefs.value, SIGNED INTEGER) MINUTE)\n\t\t\t\t) OR (\n\t\t\t\t\tttrss_feeds.update_interval > 0\n\t\t\t\t\tAND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL ttrss_feeds.update_interval MINUTE)\n\t\t\t\t) OR ttrss_feeds.last_updated IS NULL)";
    }
    // Test if feed is currently being updated by another process.
    if (DB_TYPE == "pgsql") {
        $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < NOW() - INTERVAL '120 seconds')";
    } else {
        $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < DATE_SUB(NOW(), INTERVAL 120 SECOND))";
    }
    // Test if there is a limit to number of updated feeds
    $query_limit = "";
    if ($limit) {
        $query_limit = sprintf("LIMIT %d", $limit);
    }
    $random_qpart = sql_random_function();
    // We search for feed needing update.
    $result = db_query($link, "SELECT ttrss_feeds.feed_url,ttrss_feeds.id, ttrss_feeds.owner_uid,\n\t\t\t\t" . SUBSTRING_FOR_DATE . "(ttrss_feeds.last_updated,1,19) AS last_updated,\n\t\t\t\tttrss_feeds.update_interval \n\t\t\tFROM \n\t\t\t\tttrss_feeds, ttrss_users, ttrss_user_prefs\n\t\t\tWHERE\n\t\t\t\tttrss_feeds.owner_uid = ttrss_users.id\n\t\t\t\tAND ttrss_users.id = ttrss_user_prefs.owner_uid\n\t\t\t\tAND ttrss_user_prefs.pref_name = 'DEFAULT_UPDATE_INTERVAL'\n\t\t\t\t{$login_thresh_qpart} {$update_limit_qpart}\n\t\t\t {$updstart_thresh_qpart}\n\t\t\tORDER BY {$random_qpart} {$query_limit}");
    $user_prefs_cache = array();
    if ($debug) {
        _debug(sprintf("Scheduled %d feeds to update...\n", db_num_rows($result)));
    }
    // Here is a little cache magic in order to minimize risk of double feed updates.
    $feeds_to_update = array();
    while ($line = db_fetch_assoc($result)) {
        $feeds_to_update[$line['id']] = $line;
    }
    // We update the feed last update started date before anything else.
    // There is no lag due to feed contents downloads
    // It prevent an other process to update the same feed.
    $feed_ids = array_keys($feeds_to_update);
    if ($feed_ids) {
        db_query($link, sprintf("UPDATE ttrss_feeds SET last_update_started = NOW()\n\t\t\t\tWHERE id IN (%s)", implode(',', $feed_ids)));
    }
    // For each feed, we call the feed update function.
    while ($line = array_pop($feeds_to_update)) {
        if ($debug) {
            _debug("Feed: " . $line["feed_url"] . ", " . $line["last_updated"]);
        }
        // We setup a alarm to alert if the feed take more than 300s to update.
        // => HANG alarm.
        if (!$from_http && function_exists('pcntl_alarm')) {
            pcntl_alarm(300);
        }
        update_rss_feed($link, $line["id"], true);
        // Cancel the alarm (the update went well)
        if (!$from_http && function_exists('pcntl_alarm')) {
            pcntl_alarm(0);
        }
        sleep(1);
        // prevent flood (FIXME make this an option?)
    }
    // Send feed digests by email if needed.
    if (DAEMON_SENDS_DIGESTS) {
        send_headlines_digests($link);
    }
    purge_orphans($link);
}
示例#29
0
 /**
  * 设置server信号处理函数
  * @param null $null
  * @param int $signal
  */
 public function signalHandler($signal, $null = null, $null = null)
 {
     switch ($signal) {
         // 时钟处理函数
         case SIGALRM:
             // 停止服务后EXIT_WAIT_TIME秒还没退出则强制退出
             if ($this->workerStatus == self::STATUS_SHUTDOWN) {
                 exit(0);
             }
             break;
             // 停止该进程
         // 停止该进程
         case SIGINT:
             // 平滑重启
         // 平滑重启
         case SIGHUP:
             $this->stop();
             // EXIT_WAIT_TIME秒后退出进程
             pcntl_alarm(self::EXIT_WAIT_TIME);
             break;
             // 报告进程状态
         // 报告进程状态
         case SIGUSR1:
             $this->writeStatusToQueue();
             break;
             // 报告进程使用的php文件
         // 报告进程使用的php文件
         case SIGUSR2:
             $this->writeFilesListToQueue();
             break;
     }
 }
 /**
  * 当gateway转发来数据时
  * @param TcpConnection $connection
  * @param mixed $data
  */
 public function onGatewayMessage($connection, $data)
 {
     // 上下文数据
     Context::$client_ip = $data['client_ip'];
     Context::$client_port = $data['client_port'];
     Context::$local_ip = $data['local_ip'];
     Context::$local_port = $data['local_port'];
     Context::$connection_id = $data['connection_id'];
     Context::$client_id = Context::addressToClientId($data['local_ip'], $data['local_port'], $data['connection_id']);
     // $_SERVER变量
     $_SERVER = array('REMOTE_ADDR' => long2ip($data['client_ip']), 'REMOTE_PORT' => $data['client_port'], 'GATEWAY_ADDR' => long2ip($data['local_ip']), 'GATEWAY_PORT' => $data['gateway_port'], 'GATEWAY_CLIENT_ID' => Context::$client_id);
     // 尝试解析session
     if ($data['ext_data'] != '') {
         $_SESSION = Context::sessionDecode($data['ext_data']);
     } else {
         $_SESSION = null;
     }
     // 备份一次$data['ext_data'],请求处理完毕后判断session是否和备份相等,不相等就更新session
     $session_str_copy = $data['ext_data'];
     $cmd = $data['cmd'];
     if ($this->processTimeout) {
         pcntl_alarm($this->processTimeout);
     }
     // 尝试执行Event::onConnection、Event::onMessage、Event::onClose
     switch ($cmd) {
         case GatewayProtocol::CMD_ON_CONNECTION:
             if ($this->_eventOnConnect) {
                 call_user_func($this->_eventOnConnect, Context::$client_id);
             }
             break;
         case GatewayProtocol::CMD_ON_MESSAGE:
             if ($this->_eventOnMessage) {
                 call_user_func($this->_eventOnMessage, Context::$client_id, $data['body']);
             }
             break;
         case GatewayProtocol::CMD_ON_CLOSE:
             if ($this->_eventOnClose) {
                 call_user_func($this->_eventOnClose, Context::$client_id);
             }
             break;
     }
     if ($this->processTimeout) {
         pcntl_alarm(0);
     }
     // 判断session是否被更改
     $session_str_now = $_SESSION !== null ? Context::sessionEncode($_SESSION) : '';
     if ($session_str_copy != $session_str_now) {
         \GatewayWorker\Lib\Gateway::updateSocketSession(Context::$client_id, $session_str_now);
     }
     Context::clear();
 }