PageRenderTime 64ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/antlion/antlion.php

https://code.google.com/p/overwatch/
PHP | 222 lines | 178 code | 7 blank | 37 comment | 19 complexity | 6b8e8e7a33bcc1f8b0bfab4567d68135 MD5 | raw file
  1. #!/usr/bin/env php
  2. <?php declare (ticks = 1);
  3. class App {
  4. const NAME = 'antlion';
  5. const VERSION = '0.%rev%';
  6. const MKSEC_SLEEP = 100000;
  7. protected static $JSON_ERRORS = array (
  8. JSON_ERROR_NONE => 'no errors',
  9. JSON_ERROR_DEPTH => 'stack overflow',
  10. JSON_ERROR_CTRL_CHAR => 'control character error, possibly incorrectly encoded',
  11. JSON_ERROR_SYNTAX => 'syntax error',
  12. );
  13. protected $Sockets = array ();
  14. protected $Config = FILE_CONFIG;
  15. protected $QCrash = FALSE;
  16. protected $QConfig = FALSE;
  17. public function __construct () {
  18. // *** PARSE OPTIONS ***
  19. $Opts ['c'] = new CommandLineOption (FILE_CONFIG, 'config file name');
  20. $Opts ['d'] = new CommandLineSwitch ('(overrides -l) set debug level to maximum');
  21. $Opts ['g'] = new CommandLineOption ('', 'group to change to');
  22. $Opts ['h'] = new CommandLineSwitch ('print this short help and exit');
  23. $Opts ['l'] = new CommandLineOptionLogLevel (LOG_WARNING, 'syslog level (integer or string)');
  24. $Opts ['n'] = new CommandLineSwitch ('do not fork, stay in foreground, log to stderr');
  25. $Opts ['p'] = new CommandLineOption (FILE_PID, 'pidfile name');
  26. $Opts ['u'] = new CommandLineOption ('', 'user to change to');
  27. $Opts ['v'] = new CommandLineSwitch ('show version and exit');
  28. // Construct getopt's argument
  29. $Temp = '';
  30. foreach ($Opts as $Letter => $Opt) {
  31. $Temp .= $Letter . $Opt->GetOpt;
  32. }
  33. // Parse the options from command line
  34. if (FALSE === ($Temp = getopt ($Temp))) {
  35. die (M__APP_ERR_GETOPT."\n");
  36. }
  37. try {
  38. foreach ($Temp as $Letter => $Value) {
  39. $Opts [$Letter]->Set ($Value);
  40. }
  41. } catch (X_CommandLineOptionLogLevel $X) {
  42. die (sprintf (M__CLO_ERR_SYSLOG_LEVEL."\n", $X->getMessage ()));
  43. }
  44. // Override -l with -d
  45. if ($Opts ['d']->Value) {
  46. $Opts ['l']->Set (LOG_DEBUG);
  47. }
  48. // Special modes (help and version)
  49. if ($Opts ['v']->Value) {
  50. printf (M__APP_VERSION."\n", self::NAME, self::VERSION);
  51. exit (0);
  52. }
  53. if ($Opts ['h']->Value) {
  54. printf (M__APP_USAGE_HEADER."\n", basename ($GLOBALS ['argv'][0]));
  55. foreach ($Opts as $Letter => $Opt) {
  56. printf (M__APP_USAGE_LINE."\n", $Letter, $Opt->Usage);
  57. }
  58. exit (0);
  59. }
  60. // Store config name
  61. $this->Config = $Opts ['c']->Value;
  62. // *** SUMMON A DAEMON ***
  63. Daemon::Summon ($Opts ['l']->Value, $Opts ['u']->Value, $Opts ['g']->Value);
  64. if (! $Opts ['n']->Value) {
  65. Daemon::Detach ();
  66. }
  67. // debug options table
  68. Daemon::Debug (M__APP_OPTIONS_DEBUG_HEADER);
  69. foreach ($Opts as $Letter => $Opt) {
  70. $Explicit = ($Opt->Explicit) ? '*' : ' ';
  71. if (is_bool ($Value = $Opt->Value)) {
  72. $Value = ($Value) ? 'yes' : 'no';
  73. } elseif (strlen ($Value)) {
  74. $Value = "\"$Value\"";
  75. } else {
  76. $Value = 'empty';
  77. }
  78. Daemon::Debug (M__APP_OPTIONS_DEBUG_LINE, $Letter, $Explicit, $Value);
  79. }
  80. // *** INSTALL SIGNAL HANDLERS ***
  81. foreach (array (SIGINT, SIGQUIT, SIGTERM) as $Sig) {
  82. pcntl_signal ($Sig, array ($this, 'sig_crash'));
  83. }
  84. pcntl_signal (SIGHUP, array ($this, 'sig_conf'));
  85. }
  86. protected function sig_crash ($Sig) {
  87. Daemon::Info (M__APP_SIG_CRASH, $Sig);
  88. $this->QCrash = TRUE;
  89. }
  90. protected function sig_conf ($Sig) {
  91. Daemon::Info (M__APP_SIG_CONF, $Sig);
  92. $this->QConfig = TRUE;
  93. }
  94. protected function Configure () {
  95. Daemon::Info (M__APP_CONF_INIT);
  96. $JSON = @file_get_contents ($this->Config);
  97. if (FALSE === $JSON) {
  98. Daemon::Warn (M__APP_CONF_ERR_READ, $this->Config);
  99. return (FALSE);
  100. }
  101. $JSON = json_decode ($JSON, TRUE);
  102. if (NULL === $JSON) {
  103. $Error = json_last_error ();
  104. Daemon::Warn (M__APP_CONF_ERR_JSON, $this->Config, self::$JSON_ERRORS [$Error], $Error);
  105. return (FALSE);
  106. }
  107. Daemon::Debug (M__APP_CONF_DEBUG_JSON);
  108. Daemon::Debug (print_r ($JSON, TRUE));
  109. // (re)create sockets
  110. if (array_key_exists ('Sockets', $JSON)) {
  111. if (! is_array ($JSON ['Sockets'])) {
  112. $JSON ['Sockets'] = array ($JSON ['Sockets']);
  113. }
  114. // close all opened sockets
  115. foreach ($this->Sockets as $Socket) {
  116. $Socket->Close ();
  117. }
  118. $this->Sockets = array ();
  119. // Extract single ports into address pairs
  120. $Sockets = array ();
  121. foreach ($JSON ['Sockets'] as $Address) {
  122. if (is_integer ($Address)) {
  123. array_push ($Sockets, ':::'.$Address);
  124. array_push ($Sockets, '0.0.0.0:'.$Address);
  125. } else {
  126. array_push ($Sockets, $Address);
  127. }
  128. }
  129. // Now try to create them
  130. foreach ($Sockets as $Address) {
  131. try {
  132. Daemon::Debug (M__APP_CONF_SOCKET_CREATE, $Address);
  133. $Socket = new Socket ($Address);
  134. array_push ($this->Sockets, $Socket);
  135. } catch (X_Socket $X) {
  136. Daemon::Warn (M__APP_CONF_SOCKET_X, $Address, $X->getMessage (), $X->getCode ());
  137. }
  138. }
  139. } else {
  140. Daemon::Warn (M__APP_CONF_NO_SOCKETS);
  141. }
  142. // (re)connect to DB
  143. /*
  144. if (! array_key_exists ('PDO', $JSON) or ! array_key_exists ('DSN', $JSON ['PDO'])) {
  145. Daemon::Warn (M__APP_CONF_ERR_PDO_NDEF, $this->Config);
  146. return (FALSE);
  147. }
  148. try {
  149. if (array_key_exists ('Options', $JSON ['PDO']) and array_key_exists ('User', $JSON ['PDO']) and array_key_exists ('Password', $JSON ['PDO'])) {
  150. $this->PDO = new PDO ($JSON ['PDO']['DSN'], $JSON ['PDO']['User'], $JSON ['PDO']['Password'], $JSON ['PDO']['Options']);
  151. } elseif (array_key_exists ('User', $JSON ['PDO']) and array_key_exists ('Password', $JSON ['PDO'])) {
  152. $this->PDO = new PDO ($JSON ['PDO']['DSN'], $JSON ['PDO']['User'], $JSON ['PDO']['Password']);
  153. } else {
  154. $this->PDO = new PDO ($JSON ['PDO']['DSN']);
  155. }
  156. } catch (PDOException $X) {
  157. var_dump ($X);
  158. var_dump ($this->PDO);
  159. die ();
  160. }
  161. */
  162. return (TRUE);
  163. }
  164. public function TakeOff () {
  165. if (! $this->Configure ()) {
  166. Daemon::Error (M__APP_CONF_ERR_FIRST, $this->Config);
  167. }
  168. Daemon::Info (M__APP_AIRBORNE);
  169. while (TRUE) {
  170. // if there are new connections on sockets, extract them into Connections
  171. foreach ($this->Sockets as $Socket) {
  172. $Socket->Accept ();
  173. }
  174. $Answers = array ();
  175. $Queries = Connections::Receive ();
  176. foreach ($Queries as $SID => $Query) {
  177. $Answers [$SID] = $Query;
  178. }
  179. if (count ($Answers)) {
  180. Connections::Transmit ($Answers);
  181. }
  182. if ($this->QCrash) {
  183. Daemon::Info (M__APP_CRASHING);
  184. // close all opened sockets
  185. foreach ($this->Sockets as $Socket) {
  186. $Socket->Close ();
  187. }
  188. Connections::Down ();
  189. Daemon::Banish ();
  190. exit (0);
  191. }
  192. if ($this->QConfig) {
  193. $this->Configure ();
  194. $this->QConfig = FALSE;
  195. }
  196. usleep (self::MKSEC_SLEEP);
  197. }
  198. }
  199. }
  200. // *** ENTRY POINT ***
  201. function __autoload ($Class) {
  202. include_once (DIR_CLASSES."/$Class.php");
  203. }
  204. ini_set ('display_errors', 1);
  205. ini_set ('log_errors', 0);
  206. error_reporting (E_ALL | E_STRICT);
  207. define ('DIR_CLASSES', 'classes');
  208. define ('DIR_MESSAGES', 'messages');
  209. define ('FILE_CONFIG', 'antlion.conf.js');
  210. define ('FILE_PID', '/var/run/overwatch/antlion.pid');
  211. setlocale (LC_ALL, 'C');
  212. require (DIR_MESSAGES.'/C.php'); // i18n may be implemented here
  213. $App = new App ();
  214. $App->TakeOff ();
  215. ?>