/** * Запуск сервиса. * * @access protected * @static */ protected static function cmdStart() { echo "Starting Pinger service... "; try { // проверка PID-файла Assets\PID::PreLock(); $ruid = posix_getpwnam(Assets\Config::$exec_user)['uid']; $rgid = posix_getgrnam(Assets\Config::$exec_group)['gid']; if (null === $ruid || null === $rgid) { printf(" [FAIL]\nRequired exec user:group [%s:%s] was not found.\n", Assets\Config::$exec_user, Assets\Config::$exec_group); exit(1); } // попытка запуска Мастер-процесса $pid = @pcntl_fork(); if ($pid == -1) { // ошибка ветвления throw new Assets\Exceptions\Master_Fork_Fail_Exception(); } elseif ($pid) { // // рутины родительского процесса - сценария SBINDIR/pinger // printf(" [OK]\nLooks like Master forked with PID=%d\n", $pid); exit(0); } else { // // рутины дочернего процесса - Мастер-процесса // // переключение группы-владельца процесса // выполняется ДО переключения пользователя-владельца if (!posix_setegid($rgid)) { printf("Failed to posix_setegid(%d) [%s].\n", $rgid, Assets\Config::$exec_group); exit(1); } // переключение пользователя-владельца процесса if (!posix_seteuid($ruid)) { printf("Failed to posix_seteuid(%d) [%s].\n", $ruid, Assets\Config::$exec_user); exit(1); } // открытие журналов TwinLog::init(PINGER_LOGDIR, 'Master'); // закрытие дескрипторов @fclose(STDIN); @fclose(STDOUT); @fclose(STDERR); // вход в Мастер-процесс Daemon\Master::main(); // закрытие журналов TwinLog::kill(); // успешный выход // exit(0) не используется для возможности перезапуска // с применением этого метода } } catch (Assets\Exceptions\PID_Open_Fail_Exception $e) { echo " [FAIL]\n PID-file exists but unreadable\n"; exit(1); } catch (Assets\Exceptions\PID_Lock_Fail_Exception $e) { printf("\n Daemon already running with PID=%d", Assets\PID::$pid); exit(1); } catch (Assets\Exceptions\PID_Read_Fail_Exception $e) { echo " [FAIL]\n PID-file exists and locked\n"; echo " PID-file reading failed!\n"; exit(1); } catch (Assets\Exceptions\PID_Unlink_Fail_Exception $e) { echo " [FAIL]\n PID-file exists but failed to unlink\n"; exit(1); } catch (Assets\Exceptions\Master_Fork_Fail_Exception $e) { echo " [FAIL]\n fork() failed\n"; exit(1); } catch (\Exception $e) { echo " [FAIL]\n Unexpected exception:\n"; var_export($e->getMessage()); var_export($e->getTraceAsString()); exit(1); } }
/** * Запуск Воркеров выбранного типа. * * @param string $type * @throws \Ganzal\Lulz\Pinger\Assets\Exceptions\Worker_Fork_Fail_Exception */ protected static function launchWorkers($type) { DEBUG && printf("Master::launchWorkers(%s)/master: begin\n", $type); // обновление слотов Воркеров static::recalcSlots(); // полное квалифицированное имя запускаемого метода Воркера $forkCallback = __NAMESPACE__ . '\\' . $type . 'Worker::main'; DEBUG && printf("Master::launchWorkers()/master: forkCallback = %s\n", $forkCallback); // запуск Воркеров во все доступные слоты while ($slot = array_shift(static::$pid_slots)) { ++static::$fork_num; DEBUG && printf("Master::launchWorkers()/master: slot #%d for fork #%d\n", $slot, static::$fork_num); // попытка запуска дочернего процесса $pid = @pcntl_fork(); if ($pid == -1) { // ошибка ветвления throw new Exceptions\Worker_Fork_Fail_Exception(); } elseif ($pid) { // // рутины родительского процесса // DEBUG && printf("Master::launchWorkers()/master: forked with PID %d to slot #%d\n", $pid, $slot); // регистрация дочернего процесса в списоке активных заданий static::$jobs[$pid] = $slot; DEBUG && printf("Master::launchWorkers()/master: jobs\n%s\n", var_export(static::$jobs, true)); // продолжаем цикл Мастера DEBUG && (print "Master::launchWorkers()/master: fill next slot\n"); continue; } // // рутины дочернего процесса // cli_set_process_title('PingerService/' . $type . 'Worker'); DEBUG && printf("Master::launchWorkers()/worker/%d: begin\n", static::$fork_num); // закрытие стандартных дескрипторов на случай, если // Мастер запускался напрямую, а не через SBINDIR/pinger. if ('resource' == gettype(STDIN)) { DEBUG && printf("Master::launchWorkers()/worker/%d: @fclose(STDIN);\n", static::$fork_num); @fclose(STDIN); } if ('resource' == gettype(STDOUT)) { DEBUG && printf("Master::launchWorkers()/worker/%d: @fclose(STDOUT);\n", static::$fork_num); @fclose(STDOUT); } if ('resource' == gettype(STDERR)) { DEBUG && printf("Master::launchWorkers()/worker/%d: @fclose(STDERR);\n", static::$fork_num); @fclose(STDERR); } // перемещение журнала DEBUG && printf("Master::launchWorkers()/worker/%d: TwinLog::rename(PINGER_LOGFILE, '%sWorker', %d)\n", static::$fork_num, $type, $slot); TwinLog::rename(PINGER_LOGDIR, $type . 'Worker', $slot); call_user_func($forkCallback); DEBUG && printf("Master::launchWorkers()/worker/%d: end\n\n", static::$fork_num); TwinLog::kill(true); // хорошо завершились exit(0); } // while (static::$master_loop) DEBUG && (print "Master::launchWorkers()/master: end\n\n"); }