/antlion/antlion.php
PHP | 222 lines | 178 code | 7 blank | 37 comment | 19 complexity | 6b8e8e7a33bcc1f8b0bfab4567d68135 MD5 | raw file
- #!/usr/bin/env php
- <?php declare (ticks = 1);
- class App {
- const NAME = 'antlion';
- const VERSION = '0.%rev%';
- const MKSEC_SLEEP = 100000;
- protected static $JSON_ERRORS = array (
- JSON_ERROR_NONE => 'no errors',
- JSON_ERROR_DEPTH => 'stack overflow',
- JSON_ERROR_CTRL_CHAR => 'control character error, possibly incorrectly encoded',
- JSON_ERROR_SYNTAX => 'syntax error',
- );
- protected $Sockets = array ();
- protected $Config = FILE_CONFIG;
- protected $QCrash = FALSE;
- protected $QConfig = FALSE;
- public function __construct () {
- // *** PARSE OPTIONS ***
- $Opts ['c'] = new CommandLineOption (FILE_CONFIG, 'config file name');
- $Opts ['d'] = new CommandLineSwitch ('(overrides -l) set debug level to maximum');
- $Opts ['g'] = new CommandLineOption ('', 'group to change to');
- $Opts ['h'] = new CommandLineSwitch ('print this short help and exit');
- $Opts ['l'] = new CommandLineOptionLogLevel (LOG_WARNING, 'syslog level (integer or string)');
- $Opts ['n'] = new CommandLineSwitch ('do not fork, stay in foreground, log to stderr');
- $Opts ['p'] = new CommandLineOption (FILE_PID, 'pidfile name');
- $Opts ['u'] = new CommandLineOption ('', 'user to change to');
- $Opts ['v'] = new CommandLineSwitch ('show version and exit');
- // Construct getopt's argument
- $Temp = '';
- foreach ($Opts as $Letter => $Opt) {
- $Temp .= $Letter . $Opt->GetOpt;
- }
- // Parse the options from command line
- if (FALSE === ($Temp = getopt ($Temp))) {
- die (M__APP_ERR_GETOPT."\n");
- }
- try {
- foreach ($Temp as $Letter => $Value) {
- $Opts [$Letter]->Set ($Value);
- }
- } catch (X_CommandLineOptionLogLevel $X) {
- die (sprintf (M__CLO_ERR_SYSLOG_LEVEL."\n", $X->getMessage ()));
- }
- // Override -l with -d
- if ($Opts ['d']->Value) {
- $Opts ['l']->Set (LOG_DEBUG);
- }
- // Special modes (help and version)
- if ($Opts ['v']->Value) {
- printf (M__APP_VERSION."\n", self::NAME, self::VERSION);
- exit (0);
- }
- if ($Opts ['h']->Value) {
- printf (M__APP_USAGE_HEADER."\n", basename ($GLOBALS ['argv'][0]));
- foreach ($Opts as $Letter => $Opt) {
- printf (M__APP_USAGE_LINE."\n", $Letter, $Opt->Usage);
- }
- exit (0);
- }
- // Store config name
- $this->Config = $Opts ['c']->Value;
- // *** SUMMON A DAEMON ***
- Daemon::Summon ($Opts ['l']->Value, $Opts ['u']->Value, $Opts ['g']->Value);
- if (! $Opts ['n']->Value) {
- Daemon::Detach ();
- }
- // debug options table
- Daemon::Debug (M__APP_OPTIONS_DEBUG_HEADER);
- foreach ($Opts as $Letter => $Opt) {
- $Explicit = ($Opt->Explicit) ? '*' : ' ';
- if (is_bool ($Value = $Opt->Value)) {
- $Value = ($Value) ? 'yes' : 'no';
- } elseif (strlen ($Value)) {
- $Value = "\"$Value\"";
- } else {
- $Value = 'empty';
- }
- Daemon::Debug (M__APP_OPTIONS_DEBUG_LINE, $Letter, $Explicit, $Value);
- }
- // *** INSTALL SIGNAL HANDLERS ***
- foreach (array (SIGINT, SIGQUIT, SIGTERM) as $Sig) {
- pcntl_signal ($Sig, array ($this, 'sig_crash'));
- }
- pcntl_signal (SIGHUP, array ($this, 'sig_conf'));
- }
- protected function sig_crash ($Sig) {
- Daemon::Info (M__APP_SIG_CRASH, $Sig);
- $this->QCrash = TRUE;
- }
- protected function sig_conf ($Sig) {
- Daemon::Info (M__APP_SIG_CONF, $Sig);
- $this->QConfig = TRUE;
- }
- protected function Configure () {
- Daemon::Info (M__APP_CONF_INIT);
- $JSON = @file_get_contents ($this->Config);
- if (FALSE === $JSON) {
- Daemon::Warn (M__APP_CONF_ERR_READ, $this->Config);
- return (FALSE);
- }
- $JSON = json_decode ($JSON, TRUE);
- if (NULL === $JSON) {
- $Error = json_last_error ();
- Daemon::Warn (M__APP_CONF_ERR_JSON, $this->Config, self::$JSON_ERRORS [$Error], $Error);
- return (FALSE);
- }
- Daemon::Debug (M__APP_CONF_DEBUG_JSON);
- Daemon::Debug (print_r ($JSON, TRUE));
- // (re)create sockets
- if (array_key_exists ('Sockets', $JSON)) {
- if (! is_array ($JSON ['Sockets'])) {
- $JSON ['Sockets'] = array ($JSON ['Sockets']);
- }
- // close all opened sockets
- foreach ($this->Sockets as $Socket) {
- $Socket->Close ();
- }
- $this->Sockets = array ();
- // Extract single ports into address pairs
- $Sockets = array ();
- foreach ($JSON ['Sockets'] as $Address) {
- if (is_integer ($Address)) {
- array_push ($Sockets, ':::'.$Address);
- array_push ($Sockets, '0.0.0.0:'.$Address);
- } else {
- array_push ($Sockets, $Address);
- }
- }
- // Now try to create them
- foreach ($Sockets as $Address) {
- try {
- Daemon::Debug (M__APP_CONF_SOCKET_CREATE, $Address);
- $Socket = new Socket ($Address);
- array_push ($this->Sockets, $Socket);
- } catch (X_Socket $X) {
- Daemon::Warn (M__APP_CONF_SOCKET_X, $Address, $X->getMessage (), $X->getCode ());
- }
- }
- } else {
- Daemon::Warn (M__APP_CONF_NO_SOCKETS);
- }
- // (re)connect to DB
- /*
- if (! array_key_exists ('PDO', $JSON) or ! array_key_exists ('DSN', $JSON ['PDO'])) {
- Daemon::Warn (M__APP_CONF_ERR_PDO_NDEF, $this->Config);
- return (FALSE);
- }
- try {
- if (array_key_exists ('Options', $JSON ['PDO']) and array_key_exists ('User', $JSON ['PDO']) and array_key_exists ('Password', $JSON ['PDO'])) {
- $this->PDO = new PDO ($JSON ['PDO']['DSN'], $JSON ['PDO']['User'], $JSON ['PDO']['Password'], $JSON ['PDO']['Options']);
- } elseif (array_key_exists ('User', $JSON ['PDO']) and array_key_exists ('Password', $JSON ['PDO'])) {
- $this->PDO = new PDO ($JSON ['PDO']['DSN'], $JSON ['PDO']['User'], $JSON ['PDO']['Password']);
- } else {
- $this->PDO = new PDO ($JSON ['PDO']['DSN']);
- }
- } catch (PDOException $X) {
- var_dump ($X);
- var_dump ($this->PDO);
- die ();
- }
- */
- return (TRUE);
- }
- public function TakeOff () {
- if (! $this->Configure ()) {
- Daemon::Error (M__APP_CONF_ERR_FIRST, $this->Config);
- }
- Daemon::Info (M__APP_AIRBORNE);
- while (TRUE) {
- // if there are new connections on sockets, extract them into Connections
- foreach ($this->Sockets as $Socket) {
- $Socket->Accept ();
- }
- $Answers = array ();
- $Queries = Connections::Receive ();
- foreach ($Queries as $SID => $Query) {
- $Answers [$SID] = $Query;
- }
- if (count ($Answers)) {
- Connections::Transmit ($Answers);
- }
- if ($this->QCrash) {
- Daemon::Info (M__APP_CRASHING);
- // close all opened sockets
- foreach ($this->Sockets as $Socket) {
- $Socket->Close ();
- }
- Connections::Down ();
- Daemon::Banish ();
- exit (0);
- }
- if ($this->QConfig) {
- $this->Configure ();
- $this->QConfig = FALSE;
- }
- usleep (self::MKSEC_SLEEP);
- }
- }
- }
- // *** ENTRY POINT ***
- function __autoload ($Class) {
- include_once (DIR_CLASSES."/$Class.php");
- }
- ini_set ('display_errors', 1);
- ini_set ('log_errors', 0);
- error_reporting (E_ALL | E_STRICT);
- define ('DIR_CLASSES', 'classes');
- define ('DIR_MESSAGES', 'messages');
- define ('FILE_CONFIG', 'antlion.conf.js');
- define ('FILE_PID', '/var/run/overwatch/antlion.pid');
- setlocale (LC_ALL, 'C');
- require (DIR_MESSAGES.'/C.php'); // i18n may be implemented here
- $App = new App ();
- $App->TakeOff ();
- ?>