/[drupal]/contributions/modules/daemon/daemon.module
ViewVC logotype

Contents of /contributions/modules/daemon/daemon.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.8 - (show annotations) (download) (as text)
Tue Feb 19 12:15:51 2008 UTC (21 months, 1 week ago) by aymerick
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +1 -1 lines
File MIME type: text/x-php
Fix daemon_is_daemon()
1 <?php
2 // $Id$
3
4 /**
5 * Create daemon instances of a drupal site.
6 *
7 * Coded by: Aymerick Jehanne <aymerick@newlc.com>
8 * Sponsored by NewLC: http://www.newlc.com
9 */
10
11 // Constants
12 define('DAEMON_DRUSH_CMD_RUN', 'daemon');
13 define('DAEMON_HTTP_ECHO_PORT', 41000);
14 define('DAEMON_HTTP_MENU_PORT', 41001);
15
16
17 /******************************************************************************
18 * Drupal Hooks
19 ******************************************************************************/
20
21 /**
22 * Implementation of hook_menu().
23 */
24 function daemon_menu($may_cache) {
25 $items = array();
26
27 if (!$may_cache) {
28 // only include admin file if we are viewing an admin page
29 if ((arg(0) == 'admin') && (arg(1) == 'build') && (arg(2) == 'daemon')) {
30 require_once('./'. drupal_get_path('module', 'daemon') .'/daemon.admin.inc');
31 }
32 }
33
34 if ($may_cache) {
35 // admin
36 $items[] = array(
37 'path' => 'admin/build/daemon',
38 'title' => t('Daemons'),
39 'callback' => 'daemon_admin_page',
40 'access' => user_access('administer daemons'),
41 'description' => t('Create and configure daemons.'),
42 'type' => MENU_NORMAL_ITEM);
43
44 $items[] = array(
45 'path' => 'admin/build/daemon/list',
46 'title' => t('Daemons'),
47 'access' => user_access('administer daemons'),
48 'type' => MENU_DEFAULT_LOCAL_TASK,
49 'weight' => -10);
50
51 $items[] = array(
52 'path' => 'admin/build/daemon/add',
53 'title' => t('Add daemon'),
54 'callback' => 'daemon_admin_add_page',
55 'access' => user_access('administer daemons'),
56 'type' => MENU_LOCAL_TASK);
57
58 $items[] = array(
59 'path' => 'admin/build/daemon/timers',
60 'title' => t('Timers'),
61 'callback' => 'daemon_admin_all_timers_page',
62 'access' => user_access('administer daemons'),
63 'type' => MENU_LOCAL_TASK);
64 }
65 else {
66 if (user_access('administer daemons') && (arg(0) == 'admin') && (arg(1) == 'build') && (arg(2) == 'daemon')) {
67 if ((arg(3) == 'del_timer') && arg(4) && ($timer = _daemon_load_timer(arg(4)))) {
68 $items[] = array(
69 'path' => 'admin/build/daemon/del_timer/'. arg(4),
70 'title' => t('Delete timer'),
71 'callback' => 'drupal_get_form',
72 'callback arguments' => array('daemon_admin_timer_delete_confirm', $timer),
73 'access' => user_access('administer daemons'),
74 'type' => MENU_CALLBACK);
75 }
76 else if (($daemon = _daemon_load_daemon(arg(3))) !== false) {
77 $items[] = array(
78 'path' => 'admin/build/daemon/'. arg(3),
79 'title' => $daemon->name,
80 'callback' => 'daemon_admin_edit_page',
81 'callback arguments' => array($daemon),
82 'access' => user_access('administer daemons'));
83
84 $items[] = array(
85 'path' => 'admin/build/daemon/'. arg(3) .'/edit',
86 'title' => t('Edit'),
87 'access' => user_access('administer daemons'),
88 'type' => MENU_DEFAULT_LOCAL_TASK,
89 'weight' => -10);
90
91 $items[] = array(
92 'path' => 'admin/build/daemon/'. arg(3) .'/timers',
93 'title' => t('Manage timers'),
94 'callback' => 'daemon_admin_timers_page',
95 'callback arguments' => array($daemon),
96 'access' => user_access('administer daemons'),
97 'type' => MENU_LOCAL_TASK,
98 'weight' => -5);
99
100 $items[] = array(
101 'path' => 'admin/build/daemon/'. arg(3) .'/add_timer',
102 'title' => t('Add timer'),
103 'callback' => 'daemon_admin_add_timer_page',
104 'callback arguments' => array($daemon),
105 'access' => user_access('administer daemons'),
106 'type' => MENU_LOCAL_TASK,
107 'weight' => 0);
108
109 $items[] = array(
110 'path' => 'admin/build/daemon/'. arg(3) .'/delete',
111 'title' => t('Delete daemon'),
112 'callback' => 'drupal_get_form',
113 'callback arguments' => array('daemon_admin_delete_confirm', $daemon),
114 'access' => user_access('administer daemons'),
115 'type' => MENU_CALLBACK);
116
117 if (arg(4) == 'timers' && arg(5) && ($timer = _daemon_get_timer($daemon, arg(5)))) {
118 $items[] = array(
119 'path' => 'admin/build/daemon/'. arg(3) .'/timers/'. arg(5),
120 'title' => $timer['name'],
121 'callback' => 'drupal_get_form',
122 'callback arguments' => array('daemon_admin_edit_timer_page', $daemon, $timer),
123 'access' => user_access('administer daemons'),
124 'type' => MENU_CALLBACK,
125 );
126 $items[] = array(
127 'path' => 'admin/build/daemon/'. arg(3) .'/timers/'. arg(5) .'/remove',
128 'title' => t('Remove timer'),
129 'callback' => 'drupal_get_form',
130 'callback arguments' => array('daemon_admin_timer_remove_confirm', $daemon, $timer),
131 'access' => user_access('administer daemons'),
132 'type' => MENU_CALLBACK,
133 );
134 }
135 }
136 }
137 }
138
139 return $items;
140 }
141
142 /**
143 * Implementation of hook_help().
144 */
145 function daemon_help($section = '') {
146 switch ($section) {
147 case 'drush:'. DAEMON_DRUSH_CMD_RUN:
148 $output = '';
149
150 $output .= t("Usage: drush [options] ". DAEMON_DRUSH_CMD_RUN ." <daemon name>\n\nStart the specified daemon. Available daemons:\n");
151
152 $daemons = _daemon_get_all_daemons();
153 foreach ($daemons as $daemon) {
154 $output .= " ". $daemon->name ." : ". $daemon->description ."\n";
155 }
156 $output .= "\n";
157
158 return $output;
159 }
160 return $output;
161 }
162
163 /**
164 * Implementation of hook_perm().
165 */
166 function daemon_perm() {
167 return array('administer daemons');
168 }
169
170
171 /******************************************************************************
172 * Daemon hooks
173 ******************************************************************************/
174
175 /**
176 * Implementation of hook_daemon_listeners().
177 */
178 function daemon_daemon_listeners() {
179 return array(
180 'http_echo' => array(
181 'name' => 'HTTP Echo',
182 'description' => t('A stupid HTTP server that only display requested URL.'),
183 'handler' => 'Daemon_HTTP_Echo_Connection_Handler',
184 'files' => array('handlers/http.echo.connection.handler.inc'),
185 ),
186 'http_menu' => array(
187 'name' => 'HTTP Menu',
188 'description' => t('A HTTP that fires the drupal menu system.'),
189 'handler' => 'Daemon_HTTP_Menu_Connection_Handler',
190 'files' => array('handlers/http.menu.connection.handler.inc'),
191 ),
192 );
193 }
194
195 /**
196 * Implementation of hook_daemon_default_daemons().
197 */
198 function daemon_daemon_default_daemons() {
199 $daemons = array();
200
201 // HTTP Echo
202 $daemon = new stdClass();
203 $daemon->name = 'http_echo';
204 $daemon->description = t('Daemon with a stupid HTTP server on port '. DAEMON_HTTP_ECHO_PORT .' that echo requested URL');
205 $daemon->listeners = array(
206 array(
207 'id' => 'http_echo',
208 'type' => 'tcp',
209 'port' => DAEMON_HTTP_ECHO_PORT,
210 ),
211 );
212
213 $daemons[$daemon->name] = $daemon;
214
215 // HTTP Menu
216 $daemon = new stdClass();
217 $daemon->name = 'http_menu';
218 $daemon->description = t('Daemon with a HTTP server on port '. DAEMON_HTTP_MENU_PORT .' that fires the drupal menu system and return the result (not "page" themed)');
219 $daemon->listeners = array(
220 array(
221 'id' => 'http_menu',
222 'type' => 'tcp',
223 'port' => DAEMON_HTTP_MENU_PORT,
224 ),
225 );
226
227 $daemons[$daemon->name] = $daemon;
228
229 return $daemons;
230 }
231
232 /**
233 * Implementation of hook_daemon_timeout().
234 *
235 * @param $tid Timer id
236 * @return If timer must be stopped, then return false
237 */
238 function daemon_daemon_timeout($tid) {
239 // load timer
240 $timer = _daemon_load_timer($tid);
241
242 // print message
243 daemon_print(t('Timeout: !timer_name', array('!timer_name' => $timer['name'])));
244
245 // execute custom PHP code
246 if (!empty($timer['php'])) {
247 $result = drupal_eval($timer['php']);
248 if (!empty($result)) {
249 // print output if any
250 daemon_print($result);
251 }
252 }
253 }
254
255
256 /******************************************************************************
257 * Drush hooks
258 ******************************************************************************/
259
260 /**
261 * Implementation of hook_drush_command().
262 */
263 function daemon_drush_command() {
264 $items[DAEMON_DRUSH_CMD_RUN] = array(
265 'callback' => 'daemon_run',
266 'description' => t('Start a daemon.'),
267 );
268 return $items;
269 }
270
271 /**
272 * Start daemon
273 */
274 function daemon_run($daemon_name) {
275 // check nanoserv
276 if (!file_exists('./'. drupal_get_path('module', 'daemon') .'/nanoserv/nanoserv.php')) {
277 return t("Failed to start daemon\nYou forgot to install nanoserv library, please read carefully README.txt file in daemon module directory.\n");
278 }
279
280 // get daemon
281 if (($daemon = daemon_get_daemon($daemon_name)) === false) {
282 return t("Daemon not found\n");
283 }
284
285 // include nanoserv
286 require_once './'. drupal_get_path('module', 'daemon') .'/nanoserv/nanoserv.php';
287
288 // setup listeners
289 $listeners = array();
290 foreach ($daemon->listeners as $listener) {
291 // include additional files
292 foreach ($listener['files'] as $file) {
293 require_once './'. drupal_get_path('module', $listener['module']) .'/'. $file;
294 }
295
296 // activate listener
297 $new_listener = Nanoserv::New_Listener($listener['type'] .'://0.0.0.0:'. $listener['port'], $listener['handler']);
298
299 $new_listener->Activate();
300
301 $listener_desc = $listener['type'] .':'. $listener['port'] .' ('. $listener['name'] .')';
302
303 daemon_print(t('Listener activated on !listener_desc', array('!listener_desc' => $listener_desc)));
304
305 $listeners[] = $new_listener;
306 }
307
308 // setup timers
309 foreach ($daemon->timers as $timer) {
310 // create daemon timer
311 $daemon_timer = new DaemonTimer($timer['tid'], $timer['timeout']);
312
313 // launch timer
314 Nanoserv::New_Timer($timer['timeout'], array($daemon_timer, "fired"));
315
316 daemon_print(t('Timer !timer_name launched (!timeout seconds)', array('!timer_name' => $timer['name'], '!timeout' => $timer['timeout'])));
317 }
318
319 daemon_print(t('Entering main loop...'));
320
321 // enter main loop
322 Nanoserv::Run();
323
324 daemon_print(t('Daemon stopped.'));
325
326 // cleanup
327 foreach ($listeners as $listener) {
328 Free_Listener($listener);
329 }
330 }
331
332
333 /******************************************************************************
334 * Daemons management
335 ******************************************************************************/
336
337 /**
338 * Get a daemon object
339 *
340 * @param $daemon_name Daemon name
341 * @return The daemon object, or false if not found
342 */
343 function daemon_get_daemon($daemon_name) {
344 $result = false;
345
346 // load daemon from database
347 if (($result = _daemon_load_daemon($daemon_name)) === false) {
348 // not found in database, check in default daemons
349 $default_daemons = _daemon_get_default_daemons();
350
351 if (isset($default_daemons[$daemon_name])) {
352 $result = $default_daemons[$daemon_name];
353 }
354 }
355
356 return $result;
357 }
358
359 /**
360 * Build default daemon information from all modules
361 */
362 function _daemon_get_default_daemons() {
363 static $daemons;
364
365 if (!isset($daemons)) {
366 // invoke hook_daemon_default_daemons()
367 $daemons = module_invoke_all('daemon_default_daemons');
368 foreach ($daemons as $name => $daemon) {
369 _daemon_fill_listeners($daemons[$name]);
370 $daemons[$name]->is_default = true;
371 }
372 }
373
374 return $daemons;
375 }
376
377 /**
378 * Get an empty daemon with basic defaults.
379 */
380 function _daemon_get_empty_daemon() {
381 $daemon = new stdClass();
382
383 $daemon->did = 0;
384 $daemon->name = '';
385 $daemon->description = '';
386 $daemon->listeners = array();
387
388 return $daemon;
389 }
390
391 /**
392 * Save a daemon
393 *
394 * @param $daemon Daemon to save
395 * @return Daemon id
396 */
397 function _daemon_save_daemon($daemon) {
398 if ($daemon->did) {
399 // update the daemon in the database
400 db_query("UPDATE {daemon_daemon} SET name = '%s', description = '%s' WHERE did = %d", $daemon->name, $daemon->description, $daemon->did);
401 db_query("DELETE from {daemon_listener} WHERE did = %d", $daemon->did);
402 }
403 else {
404 // insert new daemon in the database
405 $daemon->did = db_next_id('{daemon_daemon}_did');
406
407 db_query("INSERT INTO {daemon_daemon} (did, name, description) VALUES (%d, '%s', '%s')", $daemon->did, $daemon->name, $daemon->description);
408 }
409
410 // insert listeners associated to daemon
411 foreach ($daemon->listeners as $listener) {
412 if (!empty($listener['module']) && !empty($listener['id']) && !empty($listener['type']) && !empty($listener['port'])) {
413 db_query("INSERT INTO {daemon_listener} (did, module, id, type, port) VALUES (%d, '%s', '%s', '%s', %d)", $daemon->did, $listener['module'], $listener['id'], $listener['type'], $listener['port']);
414 }
415 }
416
417 return $daemon->did;
418 }
419
420 /**
421 * Load a daemon from database
422 *
423 * @param $arg Daemon id or name
424 * @return The daemon object, or false if not found
425 */
426 function _daemon_load_daemon($arg) {
427 static $cache = array();
428
429 if ($arg === false) {
430 return false;
431 }
432
433 // check in cache
434 $which = is_numeric($arg) ? 'did' : 'name';
435 if (isset($cache[$which][$arg])) {
436 return $cache[$which][$arg];
437 }
438
439 // load daemon
440 $where = (is_numeric($arg) ? "did = %d" : "name = '%s'");
441 $daemon = db_fetch_object(db_query("SELECT * FROM {daemon_daemon} WHERE $where", $arg));
442
443 if (!$daemon->name) {
444 return false;
445 }
446
447 // load daemon listeners
448 $result = db_query("SELECT * FROM {daemon_listener} WHERE did = %d", $daemon->did);
449
450 $daemon->listeners = array();
451 while ($listener = db_fetch_array($result)) {
452 $daemon->listeners[] = $listener;
453 }
454
455 _daemon_fill_listeners($daemon);
456
457 // load daemon timers
458 $daemon->timers = _daemon_timers($daemon->did);
459
460 // save in cache
461 $cache['did'][$daemon->did] = $daemon;
462 $cache['name'][$daemon->name] = $daemon;
463
464 return $daemon;
465 }
466
467 /**
468 * Delete a daemon from database
469 *
470 * @param $did Daemon id
471 */
472 function _daemon_delete_demon($did) {
473 db_query("DELETE FROM {daemon_daemon} WHERE did = %d", $did);
474 db_query("DELETE FROM {daemon_listener} WHERE did = %d", $did);
475 }
476
477 /**
478 * Get all daemons
479 */
480 function _daemon_get_all_daemons() {
481 $result = array();
482
483 $res = db_query("SELECT did FROM {daemon_daemon} ORDER BY name");
484 while ($daemon = db_fetch_object($res)) {
485 // get full daemon infos
486 $result[] = _daemon_load_daemon($daemon->did);
487 }
488
489 return $result + _daemon_get_default_daemons();
490 }
491
492 /**
493 * Fill listeners info of a daemon
494 *
495 * @param $daemon Daemon object to fill
496 */
497 function _daemon_fill_listeners(&$daemon) {
498 foreach ($daemon->listeners as $i => $listener) {
499 $listener_infos = _daemon_listeners('listener', $listener['id']);
500 if ($listener_infos !== false) {
501 $daemon->listeners[$i]['module'] = $listener_infos['module'];
502 $daemon->listeners[$i]['name'] = $listener_infos['name'];
503 $daemon->listeners[$i]['description'] = $listener_infos['description'];
504 $daemon->listeners[$i]['handler'] = $listener_infos['handler'];
505 $daemon->listeners[$i]['files'] = $listener_infos['files'];
506 }
507 }
508 }
509
510 /**
511 * Manage listeners
512 *
513 * @param $op Operation:
514 * - 'listeners': get all listeners
515 * - 'listener': get a listener by its id
516 * - 'names': get all listeners names
517 * @param $id Listener id, used when $op is 'listener'
518 * @return false or empty array if not found
519 */
520 function _daemon_listeners($op, $id = NULL) {
521 static $listeners = array();
522 static $listeners_names = array();
523
524 $result = false;
525
526 if (empty($listeners)) {
527 foreach (module_implements('daemon_listeners') as $module_name) {
528 $module_listeners = module_invoke($module_name, 'daemon_listeners');
529 foreach ($module_listeners as $listener_id => $module_listener) {
530 $module_listener['id'] = $listener_id;
531 $module_listener['module'] = $module_name;
532
533 $listeners[$listener_id] = $module_listener;
534 $listeners_names[$listener_id] = $module_listener['name'];
535 }
536 }
537 }
538
539 switch ($op) {
540 case 'listeners':
541 $result = $listeners;
542 break;
543
544 case 'listener':
545 if (($id == NULL) || !isset($listeners[$id])) {
546 $result = false;
547 }
548 else {
549 $result = $listeners[$id];
550 }
551 break;
552
553 case 'names':
554 $result = $listeners_names;
555 break;
556
557 default:
558 // nop
559 break;
560 }
561
562 return $result;
563 }
564
565 /**
566 * Get possible listeners types
567 */
568 function _daemon_listener_types() {
569 return array('tcp' => 'TCP', 'udp' => 'UDP');
570 }
571
572
573 /******************************************************************************
574 * Timers management
575 ******************************************************************************/
576
577 /**
578 * Save a timer
579 *
580 * @param $timer Timer to save
581 * @return Timer id
582 */
583 function _daemon_save_timer($timer) {
584 if ($timer->tid) {
585 // update the timer in the database
586 db_query("UPDATE {daemon_timer} SET name = '%s', description = '%s', timeout = %d, php = '%s' WHERE tid = %d",
587 $timer->name, $timer->description, $timer->timeout, $timer->php, $timer->tid);
588 }
589 else {
590 // insert new timer in the database
591 $timer->tid = db_next_id('{daemon_timer}_tid');
592
593 db_query("INSERT INTO {daemon_timer} (tid, name, description, timeout, php) VALUES (%d, '%s', '%s', %d, '%s')",
594 $timer->tid, $timer->name, $timer->description, $timer->timeout, $timer->php);
595 }
596
597 return $timer->tid;
598 }
599
600 /**
601 * Load a timer from database
602 *
603 * @param $arg Timer id or name
604 * @return The timer array, or false if not found
605 */
606 function _daemon_load_timer($arg) {
607 static $cache = array();
608
609 if ($arg === false) {
610 return false;
611 }
612
613 // check in cache
614 $which = is_numeric($arg) ? 'tid' : 'name';
615 if (isset($cache[$which][$arg])) {
616 return $cache[$which][$arg];
617 }
618
619 // load timer
620 $where = (is_numeric($arg) ? "tid = %d" : "name = '%s'");
621 $timer = db_fetch_array(db_query("SELECT * FROM {daemon_timer} WHERE $where", $arg));
622
623 if (!$timer['name']) {
624 return false;
625 }
626
627 // save in cache
628 $cache['tid'][$timer['tid']] = $timer;
629 $cache['name'][$timer['name']] = $timer;
630
631 return $timer;
632 }
633
634 /**
635 * Delete a timer from database
636 *
637 * @param $tid Timer id
638 */
639 function _daemon_delete_timer($tid) {
640 db_query("DELETE FROM {daemon_timer} WHERE tid = %d", $tid);
641 db_query("DELETE FROM {daemon_timer_daemon} WHERE tid = %d", $tid);
642 }
643
644 /**
645 * Associate a timer to a daemon
646 *
647 * @param $did Daemon id
648 * @param $tid Timer id
649 * @return true / false
650 */
651 function _daemon_associate_timer($did, $tid) {
652 if (db_result(db_query("SELECT COUNT(*) FROM {daemon_timer_daemon} WHERE did = %d AND tid = %d", $did, $tid)) == 0) {
653 db_query("INSERT INTO {daemon_timer_daemon} (did, tid) VALUES (%d, %d)", $did, $tid);
654 }
655
656 return true;
657 }
658
659 /**
660 * Disassociate a timer to a daemon
661 *
662 * @param $did Daemon id
663 * @param $tid Timer id
664 * @return true / false
665 */
666 function _daemon_disassociate_timer($did, $tid) {
667 db_query("DELETE FROM {daemon_timer_daemon} WHERE did = %d AND tid = %d", $did, $tid);
668 }
669
670 /**
671 * Get all defined timers
672 *
673 * @param $did Filter timers associated to this daemon id (if false: returns all timers)
674 * @return An array of timers
675 */
676 function _daemon_timers($did = false) {
677 $result = array();
678
679 if ($did !== false) {
680 $res = db_query("SELECT dt.* FROM {daemon_timer} dt LEFT JOIN {daemon_timer_daemon} dtd ON dtd.tid = dt.tid WHERE dtd.did = %d ORDER BY dt.name", $did);
681 }
682 else {
683 $res = db_query("SELECT * FROM {daemon_timer} ORDER BY name");
684 }
685
686 while ($timer = db_fetch_array($res)) {
687 $result[] = $timer;
688 }
689
690 return $result;
691 }
692
693 /**
694 * Check if given timer id is associated to given daemon
695 *
696 * @param $daemon Daemon object
697 * @param $tid Timer id
698 * @return The timer, or false if not found
699 */
700 function _daemon_get_timer($daemon, $tid) {
701 $result = false;
702
703 foreach ($daemon->timers as $timer) {
704 if ($timer['tid'] == $tid) {
705 $result = $timer;
706 break;
707 }
708 }
709
710 return $result;
711 }
712
713 /**
714 * Daemon Timer class
715 */
716 class DaemonTimer {
717
718 /** Timer id */
719 public $tid;
720
721 /** Timeout value in seconds */
722 public $timeout;
723
724 /**
725 * Daemon Timer constructor
726 *
727 * @param $tid Timer id
728 * @param $timeout Timeout
729 */
730 public function __construct($tid, $timeout) {
731 $this->tid = $tid;
732 $this->timeout = $timeout;
733 }
734
735 /**
736 * Method called on timer is fired
737 */
738 public function fired() {
739 $stop_timer = false;
740
741 // call hook_daemon_timeout()
742 foreach (module_implements('daemon_timeout') as $module_name) {
743 if (module_invoke($module_name, 'daemon_timeout', $this->tid) === false) {
744 // stop timer
745 $stop_timer = true;
746 }
747 }
748
749 if (!$stop_timer) {
750 // relaunch timer
751 Nanoserv::New_Timer($this->timeout, array($this, "fired"));
752 }
753 }
754 }
755
756
757 /******************************************************************************
758 * Misc
759 ******************************************************************************/
760
761 /**
762 * Are we running as a daemon ?
763 *
764 * @return true / false
765 */
766 function daemon_is_daemon() {
767 return (php_sapi_name() == 'cli');
768 }
769
770 /**
771 * Reinit drupal internals on new request
772 */
773 function daemon_reinit_request($url = false) {
774 // reset path
775 if ($url !== false) {
776 $_REQUEST['q'] = $_GET['q'] = trim($url, '/');
777 }
778 drupal_init_path();
779
780 // reset menu
781 global $_menu;
782 menu_rebuild();
783 unset($_menu['items']);
784 }
785
786 /**
787 * Print a message
788 */
789 function daemon_print($msg) {
790 drush_print('['. date('H:i:s') .'] '. $msg);
791 }

  ViewVC Help
Powered by ViewVC 1.1.2