PageRenderTime 61ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/dragoond.php

https://github.com/OwlManAtt/dragoond
PHP | 411 lines | 279 code | 75 blank | 57 comment | 31 complexity | 60e96f2c74bf98f1b1470c739275a338 MD5 | raw file
  1. <?php
  2. /**
  3. * The core of the Dragoon service.
  4. *
  5. * @package Dragoond
  6. * @author OwlManAtt <owlmanatt@gmail.com>
  7. * @copyright 2007, Yasashii Syndicate
  8. * @version 0.0.1 dev
  9. **/
  10. /**
  11. * External libraries.
  12. **/
  13. require_once('DB.php');
  14. require_once('Log.php');
  15. require_once('aphp/aphp.php');
  16. require_once('spyc/spyc.php');
  17. /**
  18. * Dragoond libs.
  19. **/
  20. require_once('lib/daemonize.class.php');
  21. // Load core classes.
  22. foreach(glob('lib/core/*.class.php') as $filename)
  23. {
  24. require($filename);
  25. }
  26. /**
  27. *
  28. * @package Dragoond
  29. * @author OwlManAtt <owlmanatt@gmail.com>
  30. * @copyright 2007, Yasashii Syndicate
  31. * @version: Release: @package_version@
  32. **/
  33. class Dragoond extends Daemonize
  34. {
  35. protected $db_config = array();
  36. protected $db = null;
  37. protected $dragoon_name = 'Uninitialized Dragoon';
  38. protected $debug_level = 2;
  39. protected $log = null;
  40. protected $loaded_modules = array();
  41. protected $module_load_queue = array();
  42. protected $module_unload_queue = array();
  43. private $log_dir = '/tmp/dragoond/log';
  44. private $module_dir = null;
  45. public function __construct($config)
  46. {
  47. parent::__construct();
  48. // This is a temporary logger that will be used to log to the console until we can fork
  49. // off the real daemon. This is used for reporting configuration problems that prevent
  50. // correct initialization of the Dragoon.
  51. $this->log = Log::singleton('console','',$this->dragoon_name,array('timeFormat' => '%Y-%m-%d %H:%M:%S'));
  52. $this->logMessage("Reading configuration '$config'...",'debug');
  53. if($this->configure($config) == false)
  54. {
  55. $this->logMessage("Could not configure dragoon. Please check config.",'emergency');
  56. }
  57. else
  58. {
  59. $this->logMessage("{$this->dragoon_name} configured.",'debug');
  60. }
  61. } // end __construct
  62. public function start()
  63. {
  64. $composite_log = Log::singleton('composite');
  65. $console_log = Log::singleton('console','',$this->dragoon_name,array('timeFormat' => '%Y-%m-%d %H:%M:%S'));
  66. $file_log = Log::singleton('file',"{$this->log_dir}/dragoon.log",$this->dragoon_name,array('timeFormat' => '%Y-%m-%d %H:%M:%S'));
  67. if($this->debug == 0)
  68. {
  69. $file_log->setMask((PEAR_LOG_ALL ^ Log::MASK(PEAR_LOG_INFO)));
  70. }
  71. elseif($this->debug == 1)
  72. {
  73. $file_log->setMask((PEAR_LOG_ALL ^ Log::MASK(PEAR_LOG_DEBUG)));
  74. }
  75. elseif($this->debug >= 2)
  76. {
  77. $file_log->setMask(PEAR_LOG_ALL);
  78. }
  79. // The console logger will just write out emergency messages.
  80. $console_log->setMask(PEAR_LOG_NONE ^ Log::MASK(PEAR_LOG_EMERG));
  81. $composite_log->addChild($console_log);
  82. $composite_log->addChild($file_log);
  83. $this->log = $composite_log;
  84. $this->logMessage("Advanced logging set up; moving to initalize daemon.",'debug');
  85. return parent::start();
  86. } // end start
  87. public function prepareDaemon()
  88. {
  89. try
  90. {
  91. $db = $this->dbconnect($this->db_config);
  92. $this->db = $db;
  93. }
  94. catch(SQLError $e)
  95. {
  96. $this->logMessage('Could not establish database connection.','critical');
  97. $this->logMessage($e->__toString(),'debug');
  98. return false;
  99. } // end db connection failure
  100. } // end prepareDaemon
  101. public function stop()
  102. {
  103. $this->logMessage('Dragoon received stop command.','notice');
  104. // Move module names to unload queue, fire the processor off, and then
  105. // do the teardown.
  106. $this->logMessage('Clearing module load queue & queing all loaded modules for unload...');
  107. $this->module_load_queue = array(); // Clear any pending modules out.
  108. $this->module_unload_queue = array_keys($this->loaded_modules);
  109. $this->handleModuleQueues();
  110. $this->logMessage('All modules should have been unloaded.','info');
  111. $this->logMessage('Dropping database connection...','debug');
  112. $this->db->disconnect();
  113. $this->logMessage('Escalating shutdown to daemonizer.','debug');
  114. parent::stop();
  115. } // end stop
  116. protected function configure($file)
  117. {
  118. $return = true;
  119. $config = Spyc::YAMLLoad($file);
  120. $this->db_config = $config['database_dsn'];
  121. // Daemon settings.
  122. $this->userID = $config['uid'];
  123. $this->groupID = $config['gid'];
  124. // Dragoon settings.
  125. $this->dragoon_name = $config['name'];
  126. $this->debug_level = $config['debug'];
  127. $this->homePath = $config['home_dir'];
  128. $this->log_dir = $config['log_dir'];
  129. $this->module_dir = $config['module_dir'];
  130. if(is_array($config['default_modules']))
  131. {
  132. $this->module_load_queue = $config['default_modules'];
  133. }
  134. return $return;
  135. } // end configure
  136. /**
  137. * Get the Dragoon's name.
  138. *
  139. * @return string
  140. **/
  141. public function getDragoonName()
  142. {
  143. return $this->dragoon_name;
  144. } // end getDragoonName
  145. /**
  146. * A wrapper for creating a PEAR::DB connection instance.
  147. *
  148. * @param array PEAR::DB DSN
  149. * @throws SQLError
  150. * @return object PEAR::DB connection
  151. **/
  152. protected function dbconnect($DSN)
  153. {
  154. $db = DB::connect($DSN,array('debug' => 2,'portability' => DB_PORTABILITY_ALL));
  155. if(PEAR::isError($db))
  156. {
  157. throw new SQLError($db->getDebugInfo(),null);
  158. } // end iserror
  159. $db->setFetchMode(DB_FETCHMODE_ASSOC);
  160. return $db;
  161. } // end dbconnect
  162. // Magic goes here.
  163. protected function doTask()
  164. {
  165. // Deal with loading/unloading modules.
  166. $this->handleModuleQueues();
  167. // Deal with the scheduled methods.
  168. foreach($this->loaded_modules as $module_name => $MODULE)
  169. {
  170. foreach($MODULE['registered_run_methods'] as $method)
  171. {
  172. if(is_object($MODULE['instance']) == false)
  173. {
  174. $this->logMessage("Module $module_name has had its instance corrupted! '{$MODULE['instance']}' '{$method['method']}'",'emergency');
  175. }
  176. call_user_func_array(array(&$MODULE['instance'],$method['method']),$method['args']);
  177. } // end method loop
  178. } // end module loop
  179. // Rest to give the CPU a break.
  180. sleep(2);
  181. } // end doTask
  182. public function getModule($module_name)
  183. {
  184. if(array_key_exists($module_name,$this->loaded_modules) == true)
  185. {
  186. return $this->loaded_modules[$module_name]['instance'];
  187. }
  188. return false;
  189. } // end getModule
  190. public function queueModuleLoad($module_name)
  191. {
  192. $this->module_load_queue[] = $module_name;
  193. } // end queueModuleLoad
  194. public function queueModuleUnload($module_name)
  195. {
  196. $this->module_unload_queue[] = $module_name;
  197. } // end queueModuleUnload
  198. public function queueModuleReload($module_name)
  199. {
  200. $this->queueModuleUnload($module_name);
  201. $this->queueModuleLoad($module_name);
  202. } // end queueModuleReload
  203. private function handleModuleQueues()
  204. {
  205. if(sizeof($this->module_unload_queue) > 0)
  206. {
  207. $this->module_unload_queue = array_unique($this->module_unload_queue);
  208. foreach($this->module_unload_queue as $index => $module)
  209. {
  210. if(array_key_exists($module,$this->loaded_modules) == false)
  211. {
  212. $this->logMessage("Module '$module' not loaded - cannot unload.",'info');
  213. continue;
  214. }
  215. $this->loaded_modules[$module]['instance']->unload();
  216. unset($this->loaded_modules[$module]);
  217. unset($this->module_unload_queue[$index]);
  218. } // end unload loop
  219. } // end unload
  220. if(sizeof($this->module_load_queue) > 0)
  221. {
  222. foreach($this->module_load_queue as $index => $module)
  223. {
  224. $this->module_load_queue = array_unique($this->module_load_queue);
  225. if(array_key_exists($module,$this->loaded_modules) == true)
  226. {
  227. $this->logMessage("Could not load '$module' - it is already loaded.",'info');
  228. continue;
  229. } // end module is loaded
  230. $module_path = "{$this->module_dir}/$module/$module.class.php";
  231. $this->logMessage("Module $module_path being loaded...",'debug');
  232. // Hack module loader - dynamically rename the class definition
  233. // so it's unique and eval it in. Ideally, this would use runkit - see below.
  234. $class = str_replace('_',null,$module);
  235. $class_with_suffix = $class.'__'.time();
  236. $file = '';
  237. $file = trim(`cat $module_path`);
  238. // Rename the class, strip shit tags.
  239. $file = preg_replace("/class $class extends/i","class $class_with_suffix extends",$file);
  240. $file = preg_replace('/^<\?php/i',null,$file);
  241. $file = preg_replace('/\?>$/',null,$file);
  242. // *gulp*
  243. eval($file);
  244. unset($file);
  245. // Runkit does not function properly. This is a desired way to load modules,
  246. // but it won't work. Please se PECL bug #11656 for details.
  247. // runkit_import($module_path,RUNKIT_IMPORT_CLASSES | RUNKIT_IMPORT_OVERRIDE);
  248. $this->logMessage("Loading class $class...");
  249. $php = '$module_instance = new '.$class_with_suffix.'(&$this,&$this->db);';
  250. eval($php);
  251. $this->loaded_modules[$module] = $module_instance->getModuleInfo();
  252. $this->loaded_modules[$module]['registered_run_methods'] = array();
  253. $this->loaded_modules[$module]['instance'] = $module_instance;
  254. // If it's just a helper-type thing (ie, not a true module), don't load it.
  255. if(is_subclass_of($this->loaded_modules[$module]['instance'],'DragoonModule'))
  256. {
  257. if($this->loaded_modules[$module]['instance']->load() == false)
  258. {
  259. $this->logMessage("$module could not be successfully loaded.",'notice');
  260. unset($this->loaded_modules[$module]);
  261. }
  262. } // end is module
  263. unset($this->module_load_queue[$index]);
  264. } // end load loop
  265. } // end load
  266. } // end handleModuleQueues
  267. public function registerRunMethod($module,$method_name,$args=array())
  268. {
  269. $this->loaded_modules[$module]['registered_run_methods'][] = array(
  270. 'method' => $method_name,
  271. 'args' => $args,
  272. );
  273. return true;
  274. } // end registerRunMethod
  275. public function unregisterRunMethod($module,$method_name,$args=array())
  276. {
  277. if(array_key_exists($module,$this->loaded_modules) == false)
  278. {
  279. return false;
  280. }
  281. foreach($this->loaded_modules[$module]['registered_run_methods'] as $i => $method)
  282. {
  283. if($method['method'] == $method_name)
  284. {
  285. unset($this->loaded_modules[$module]['registered_run_methods'][$i]);
  286. // Unset collapses things?
  287. if(is_array($this->loaded_modules[$module]['registered_run_methods']) == false)
  288. {
  289. $this->loaded_modules[$module]['registered_run_methods'] = array();
  290. }
  291. return true;
  292. }
  293. } // end module loop
  294. return false;
  295. } // end unregisterRunMethod
  296. // Route a datasource's fatched data to a handler module.
  297. public function handleDatasource($cached_file_path,$datasource_handler_module)
  298. {
  299. if(array_key_exists($datasource_handler_module,$this->loaded_modules) == false)
  300. {
  301. $this->logMessage("Handler $datasource_handler_module is not loaded. Cannot process.",'notice');
  302. return false;
  303. } // module not loaded
  304. if(method_exists($this->loaded_modules[$datasource_handler_module]['instance'],'handle') == false)
  305. {
  306. $this->logMessage("Handler $datasource_handler_module does not have #handle() method defined. Cannot proceed.",'notice');
  307. return false;
  308. } // datahandler method undefined
  309. return $this->loaded_modules[$datasource_handler_module]['instance']->handle($cached_file_path);
  310. } // end handleDatasource
  311. protected function logMessage($msg,$level='notice')
  312. {
  313. // Friendly names are friendly~
  314. $PEAR_LEVELS = array(
  315. 'debug' => PEAR_LOG_DEBUG,
  316. 'info' => PEAR_LOG_INFO,
  317. 'notice' => PEAR_LOG_NOTICE,
  318. 'warning' => PEAR_LOG_WARNING,
  319. 'error' => PEAR_LOG_ERR,
  320. 'critical' => PEAR_LOG_CRIT,
  321. 'alert' => PEAR_LOG_ALERT,
  322. 'emergency' => PEAR_LOG_EMERG,
  323. ); // pear levels
  324. if(array_key_exists($level,$PEAR_LEVELS))
  325. {
  326. $level = $PEAR_LEVELS[$level];
  327. }
  328. else
  329. {
  330. $level = $PEAR_LEVELS['info'];
  331. }
  332. $this->log->log($msg,$level);
  333. return null;
  334. } // end logMessage
  335. } // end Dragoond
  336. ?>