These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php namespace Comodojo\Extender; |
||
2 | |||
3 | use \Console_Color2; |
||
4 | use \Console_Table; |
||
5 | use \Comodojo\Extender\Scheduler\Scheduler; |
||
6 | use \Comodojo\Extender\Scheduler\Schedule; |
||
7 | use \Comodojo\Extender\Runner\JobsRunner; |
||
8 | use \Comodojo\Extender\Runner\JobsResult; |
||
9 | use \Comodojo\Extender\Job\Job; |
||
10 | use \Comodojo\Extender\Log\ExtenderLogger; |
||
11 | use \Comodojo\Extender\Events; |
||
12 | use \Comodojo\Extender\TasksTable; |
||
13 | use \Exception; |
||
14 | |||
15 | /** |
||
16 | * Extender main class |
||
17 | * |
||
18 | * @package Comodojo extender |
||
19 | * @author Marco Giovinazzi <[email protected]> |
||
20 | * @license GPL-3.0+ |
||
21 | * |
||
22 | * LICENSE: |
||
23 | * |
||
24 | * This program is free software: you can redistribute it and/or modify |
||
25 | * it under the terms of the GNU Affero General Public License as |
||
26 | * published by the Free Software Foundation, either version 3 of the |
||
27 | * License, or (at your option) any later version. |
||
28 | * |
||
29 | * This program is distributed in the hope that it will be useful, |
||
30 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
31 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
32 | * GNU Affero General Public License for more details. |
||
33 | * |
||
34 | * You should have received a copy of the GNU Affero General Public License |
||
35 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
36 | */ |
||
37 | |||
38 | class Extender { |
||
39 | |||
40 | // configurable things |
||
41 | |||
42 | /** |
||
43 | * Max result lenght (in bytes) retrieved from parent in miltithread mode |
||
44 | * |
||
45 | * @var int |
||
46 | */ |
||
47 | private $max_result_bytes_in_multithread = null; |
||
48 | |||
49 | /** |
||
50 | * Maximum time (in seconds) the parent will wait for child tasks to be completed (in miltithread mode) |
||
51 | * |
||
52 | * @var int |
||
53 | */ |
||
54 | private $max_childs_runtime = null; |
||
55 | |||
56 | /** |
||
57 | * Multithread mode |
||
58 | * |
||
59 | * @var bool |
||
60 | */ |
||
61 | private $multithread_mode = false; |
||
62 | |||
63 | /** |
||
64 | * Verbose mode, if requested via command line arg -v |
||
65 | * |
||
66 | * @var bool |
||
67 | */ |
||
68 | private $verbose_mode = false; |
||
69 | |||
70 | /** |
||
71 | * Debug mode, if requested via command line arg -V |
||
72 | * |
||
73 | * @var bool |
||
74 | */ |
||
75 | private $debug_mode = false; |
||
76 | |||
77 | /** |
||
78 | * Summary mode, if requested via command line arg -s |
||
79 | * |
||
80 | * @var bool |
||
81 | */ |
||
82 | private $summary_mode = false; |
||
83 | |||
84 | /** |
||
85 | * Daemon mode, if requested via command line arg -d |
||
86 | * |
||
87 | * @var bool |
||
88 | */ |
||
89 | private $daemon_mode = false; |
||
90 | |||
91 | /** |
||
92 | * Timestamp, relative, of current extend() cycle |
||
93 | * |
||
94 | * @var float |
||
95 | */ |
||
96 | private $timestamp = null; |
||
97 | |||
98 | /** |
||
99 | * Timestamp, absolute, sinnce extender was initiated |
||
100 | * |
||
101 | * @var float |
||
102 | */ |
||
103 | private $timestamp_absolute = null; |
||
104 | |||
105 | /** |
||
106 | * PID of the parent extender process |
||
107 | * |
||
108 | * @var int |
||
109 | */ |
||
110 | private $parent_pid = null; |
||
111 | |||
112 | /** |
||
113 | * Set exender in paused mode (no job will be processed) |
||
114 | * |
||
115 | * @var bool |
||
116 | */ |
||
117 | private $paused = false; |
||
118 | |||
119 | // Helper classes |
||
120 | |||
121 | /** |
||
122 | * Events manager instance |
||
123 | * |
||
124 | * @var \Comodojo\Extender\Events |
||
125 | */ |
||
126 | private $events = null; |
||
127 | |||
128 | /** |
||
129 | * Console_Color2 instance |
||
130 | * |
||
131 | * @var \Console_Color2 |
||
132 | */ |
||
133 | private $color = null; |
||
134 | |||
135 | /** |
||
136 | * Logger instance |
||
137 | * |
||
138 | * @var \Monolog\Logger |
||
139 | */ |
||
140 | private $logger = null; |
||
141 | |||
142 | /** |
||
143 | * JobsRunner instance |
||
144 | * |
||
145 | * @var \Comodojo\Extender\Runner\JobsRunner |
||
146 | */ |
||
147 | private $runner = null; |
||
148 | |||
149 | /** |
||
150 | * TasksTable instance |
||
151 | * |
||
152 | * @var \Comodojo\Extender\TasksTable |
||
153 | */ |
||
154 | private $tasks = null; |
||
155 | |||
156 | // checks and locks are static! |
||
157 | |||
158 | // local archives |
||
159 | |||
160 | /** |
||
161 | * Failed processes, refreshed each cycle (in daemon mode) |
||
162 | * |
||
163 | * @var int |
||
164 | */ |
||
165 | private $failed_processes = 0; |
||
166 | |||
167 | /** |
||
168 | * Completed processes |
||
169 | * |
||
170 | * @var int |
||
171 | */ |
||
172 | private $completed_processes = 0; |
||
173 | |||
174 | /** |
||
175 | * Constructor method |
||
176 | * |
||
177 | * Prepare extender environment, do checks and fire extender.ready event |
||
178 | */ |
||
179 | 12 | final public function __construct() { |
|
180 | |||
181 | // check if extender is running from cli |
||
182 | |||
183 | 12 | if ( Checks::cli() === false ) { |
|
184 | |||
185 | echo "Extender runs only in php-cli, exiting"; |
||
186 | |||
187 | self::end(1); |
||
188 | |||
189 | } |
||
190 | |||
191 | // setup default timezone (in daemon mode, timezone warning may break extender) |
||
192 | |||
193 | 12 | $default_timezone = ini_get('date.timezone'); |
|
194 | |||
195 | 12 | if ( empty($default_timezone) ) { |
|
196 | |||
197 | date_default_timezone_set(defined('EXTENDER_TIMEZONE') ? EXTENDER_TIMEZONE : 'Europe/Rome'); |
||
198 | |||
199 | } |
||
200 | |||
201 | 12 | $this->timestamp_absolute = microtime(true); |
|
202 | |||
203 | 12 | $this->color = new Console_Color2(); |
|
204 | |||
205 | // get command line options (vsdh) |
||
206 | |||
207 | 12 | list($this->verbose_mode, $this->debug_mode, $this->summary_mode, $this->daemon_mode, $help_mode) = self::getCommandlineOptions(); |
|
208 | |||
209 | 12 | if ( $help_mode ) { |
|
210 | |||
211 | self::showHelp($this->color); |
||
212 | |||
213 | self::end(0); |
||
214 | |||
215 | } |
||
216 | |||
217 | 12 | $this->logger = ExtenderLogger::create($this->verbose_mode, $this->debug_mode); |
|
218 | |||
219 | // do checks |
||
220 | |||
221 | 12 | $check_constants = Checks::constants(); |
|
222 | |||
223 | 12 | if ( $check_constants !== true ) { |
|
224 | |||
225 | $this->logger->critical($check_constants); |
||
226 | |||
227 | self::end(1); |
||
228 | |||
229 | } |
||
230 | |||
231 | 12 | if ( Checks::signals() === false AND $this->daemon_mode ) { |
|
232 | |||
233 | $this->logger->critical("Extender cannot run in daemon mode without PHP Process Control Extensions"); |
||
234 | |||
235 | self::end(1); |
||
236 | |||
237 | } |
||
238 | |||
239 | 12 | if ( Checks::database() === false ) { |
|
240 | |||
241 | $this->logger->critical("Extender database not available, exiting"); |
||
242 | |||
243 | self::end(1); |
||
244 | |||
245 | } |
||
246 | |||
247 | 12 | $this->tasks = TasksTable::load($this->logger); |
|
248 | |||
249 | 12 | $this->events = Events::load($this->logger); |
|
250 | |||
251 | // setup extender parameters |
||
252 | |||
253 | 12 | $this->max_result_bytes_in_multithread = defined('EXTENDER_MAX_RESULT_BYTES') ? filter_var(EXTENDER_MAX_RESULT_BYTES, FILTER_VALIDATE_INT) : 2048; |
|
254 | |||
255 | 12 | $this->max_childs_runtime = defined('EXTENDER_MAX_CHILDS_RUNTIME') ? filter_var(EXTENDER_MAX_CHILDS_RUNTIME, FILTER_VALIDATE_INT) : 600; |
|
256 | |||
257 | 12 | $this->multithread_mode = defined('EXTENDER_MULTITHREAD_ENABLED') ? filter_var(EXTENDER_MULTITHREAD_ENABLED, FILTER_VALIDATE_BOOLEAN) : false; |
|
258 | |||
259 | // if in daemon mode, remember parent pid, setup lock and register signal handlers |
||
260 | |||
261 | 12 | if ( $this->daemon_mode ) { |
|
262 | |||
263 | $this->parent_pid = posix_getpid(); |
||
264 | |||
265 | Lock::register($this->parent_pid); |
||
266 | |||
267 | $this->adjustNiceness(); |
||
268 | |||
269 | if ( Checks::signals() ) $this->registerSignals(); |
||
270 | |||
271 | } |
||
272 | |||
273 | // init the runner |
||
274 | |||
275 | 12 | $this->runner = new JobsRunner($this->logger, $this->getMultithreadMode(), $this->max_result_bytes_in_multithread, $this->max_childs_runtime); |
|
276 | |||
277 | 12 | $this->logger->notice("Extender ready"); |
|
278 | |||
279 | // store initial status and queue information |
||
280 | |||
281 | 12 | Status::dump($this->timestamp_absolute, $this->parent_pid, $this->completed_processes, $this->failed_processes, $this->paused); |
|
282 | |||
283 | 12 | Queue::dump(0, 0); |
|
284 | |||
285 | // we are ready to go! |
||
286 | |||
287 | 12 | } |
|
288 | |||
289 | /** |
||
290 | * Set max result length (in bytes) that should be read from child tasks |
||
291 | * |
||
292 | * @param int $bytes Maximum length (bytes) |
||
293 | * |
||
294 | * @return Extender $this |
||
295 | */ |
||
296 | 3 | final public function setMaxResultLength($bytes) { |
|
297 | |||
298 | 3 | $this->max_result_bytes_in_multithread = filter_var($bytes, FILTER_VALIDATE_INT, array("default" => 2048)); |
|
299 | |||
300 | 3 | return $this; |
|
301 | |||
302 | } |
||
303 | |||
304 | /** |
||
305 | * Get max result length (in bytes) |
||
306 | * |
||
307 | * @return int Bytes parent should read (max) |
||
308 | */ |
||
309 | 3 | final public function getMaxResultLength() { |
|
310 | |||
311 | 3 | return $this->max_result_bytes_in_multithread; |
|
312 | |||
313 | } |
||
314 | |||
315 | /** |
||
316 | * Set maximum time (in seconds) the parent will wait for child tasks to be completed (in miltithread mode) |
||
317 | * |
||
318 | * After $time seconds, parent will start killing tasks |
||
319 | * |
||
320 | * @param int $time Maximum time (seconds) |
||
321 | * |
||
322 | * @return Extender $this |
||
323 | */ |
||
324 | 3 | final public function setMaxChildsRuntime($time) { |
|
325 | |||
326 | 3 | $this->max_childs_runtime = filter_var($time, FILTER_VALIDATE_INT, array("min_range" => 1, "default" => 300)); |
|
327 | |||
328 | 3 | return $this; |
|
329 | |||
330 | } |
||
331 | |||
332 | /** |
||
333 | * Get maximum time (in seconds) the parent will wait for child tasks to be completed (in miltithread mode) |
||
334 | * |
||
335 | * @return int Time parent will wait for childs to be completed |
||
336 | */ |
||
337 | 3 | final public function getMaxChildsRuntime() { |
|
338 | |||
339 | 3 | return $this->max_childs_runtime; |
|
340 | |||
341 | } |
||
342 | |||
343 | /** |
||
344 | * Set working mode (single or multithread) |
||
345 | * |
||
346 | * If multithread enabled, extender will use pcntl to fork child tasks |
||
347 | * |
||
348 | * @param bool $mode Enable/disable multithread |
||
349 | * |
||
350 | * @return Extender $this |
||
351 | */ |
||
352 | 3 | final public function setMultithreadMode($mode) { |
|
353 | |||
354 | 3 | $this->multithread_mode = filter_var($mode, FILTER_VALIDATE_BOOLEAN); |
|
355 | |||
356 | 3 | return $this; |
|
357 | |||
358 | } |
||
359 | |||
360 | /** |
||
361 | * Get multithread mode status |
||
362 | * |
||
363 | * @return bool True if enabled, false if disabled |
||
364 | */ |
||
365 | 12 | final public function getMultithreadMode() { |
|
366 | |||
367 | 12 | return ($this->multithread_mode AND Checks::multithread()) ? true : false; |
|
368 | |||
369 | } |
||
370 | |||
371 | /** |
||
372 | * Get daemon mode status |
||
373 | * |
||
374 | * @return bool True if enabled, false if disabled |
||
375 | */ |
||
376 | 6 | final public function getDaemonMode() { |
|
377 | |||
378 | 6 | return $this->daemon_mode; |
|
379 | |||
380 | } |
||
381 | |||
382 | /** |
||
383 | * Get the number of completed processes |
||
384 | * |
||
385 | * @return int |
||
386 | */ |
||
387 | 3 | final public function getCompletedProcesses() { |
|
388 | |||
389 | 3 | return $this->completed_processes; |
|
390 | |||
391 | } |
||
392 | |||
393 | /** |
||
394 | * Get the number of failed processes |
||
395 | * |
||
396 | * @return int |
||
397 | */ |
||
398 | 3 | final public function getFailedProcesses() { |
|
399 | |||
400 | 3 | return $this->failed_processes; |
|
401 | |||
402 | } |
||
403 | |||
404 | /** |
||
405 | * Get current version |
||
406 | * |
||
407 | * @return string |
||
408 | */ |
||
409 | 3 | final public function getVersion() { |
|
410 | |||
411 | 3 | return Version::getVersion(); |
|
412 | |||
413 | } |
||
414 | |||
415 | /** |
||
416 | * Get events manager |
||
417 | * |
||
418 | * @return \Comodojo\Extender\Events |
||
419 | */ |
||
420 | 6 | final public function events() { |
|
421 | |||
422 | 6 | return $this->events; |
|
423 | |||
424 | } |
||
425 | |||
426 | /** |
||
427 | * Get console color instance |
||
428 | * |
||
429 | * @return \Console_Color2 |
||
430 | */ |
||
431 | 3 | final public function color() { |
|
432 | |||
433 | 3 | return $this->color; |
|
434 | |||
435 | } |
||
436 | |||
437 | /** |
||
438 | * Get internal logger |
||
439 | * |
||
440 | * @return \Monolog\Logger |
||
441 | */ |
||
442 | 3 | final public function logger() { |
|
443 | |||
444 | 3 | return $this->logger; |
|
445 | |||
446 | } |
||
447 | |||
448 | /** |
||
449 | * Get jobs' runner |
||
450 | * |
||
451 | * @return JobsRunner |
||
452 | */ |
||
453 | 3 | final public function runner() { |
|
454 | |||
455 | 3 | return $this->runner; |
|
456 | |||
457 | } |
||
458 | |||
459 | /** |
||
460 | * Get the tasks' table |
||
461 | * |
||
462 | * @return TasksTable |
||
463 | */ |
||
464 | 6 | final public function tasks() { |
|
465 | |||
466 | 6 | return $this->tasks; |
|
467 | |||
468 | } |
||
469 | |||
470 | /** |
||
471 | * Do extend! |
||
472 | * |
||
473 | */ |
||
474 | 3 | public function extend() { |
|
475 | |||
476 | 3 | if ( $this->getDaemonMode() ) { |
|
477 | |||
478 | $this->logger->notice("Executing extender in daemon mode"); |
||
479 | |||
480 | $idle_time = defined("EXTENDER_IDLE_TIME") ? filter_var(EXTENDER_IDLE_TIME, FILTER_VALIDATE_INT, array( |
||
481 | 'options' => array( |
||
482 | 'default' => 1, |
||
483 | 'min_range' => 1 |
||
484 | ) |
||
485 | )) : 1; |
||
486 | |||
487 | while (true) { |
||
488 | |||
489 | $this->cycle(); |
||
490 | |||
491 | sleep($idle_time); |
||
492 | |||
493 | } |
||
494 | |||
495 | } else { |
||
496 | |||
497 | 3 | $this->logger->notice("Executing extender in single mode"); |
|
498 | |||
499 | 3 | $this->cycle(); |
|
500 | |||
501 | } |
||
502 | |||
503 | 3 | } |
|
504 | |||
505 | /** |
||
506 | * Change parent process priority according to EXTENDER_NICENESS |
||
507 | * |
||
508 | */ |
||
509 | final public function adjustNiceness() { |
||
510 | |||
511 | if ( defined("EXTENDER_PARENT_NICENESS") ) { |
||
512 | |||
513 | $niceness = proc_nice(EXTENDER_PARENT_NICENESS); |
||
514 | |||
515 | if ( $niceness == false ) $this->logger->warning("Unable to set parent process niceness to ".EXTENDER_PARENT_NICENESS); |
||
516 | |||
517 | } |
||
518 | |||
519 | } |
||
520 | |||
521 | /** |
||
522 | * Register signals |
||
523 | * |
||
524 | */ |
||
525 | final public function registerSignals() { |
||
526 | |||
527 | $pluggable_signals = array( |
||
528 | SIGHUP, SIGCHLD, SIGUSR2, SIGILL, SIGTRAP, SIGABRT, SIGIOT, SIGBUS, SIGFPE, |
||
529 | SIGSEGV, SIGPIPE, SIGALRM, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, |
||
530 | SIGVTALRM, SIGPROF, SIGWINCH, SIGIO, SIGSYS, SIGBABY |
||
531 | ); |
||
532 | |||
533 | if ( defined('SIGPOLL') ) $pluggable_signals[] = SIGPOLL; |
||
534 | if ( defined('SIGPWR') ) $pluggable_signals[] = SIGPWR; |
||
535 | if ( defined('SIGSTKFLT') ) $pluggable_signals[] = SIGSTKFLT; |
||
536 | |||
537 | // register supported signals |
||
538 | |||
539 | pcntl_signal(SIGTERM, array($this, 'sigTermHandler')); |
||
540 | |||
541 | pcntl_signal(SIGINT, array($this, 'sigTermHandler')); |
||
542 | |||
543 | pcntl_signal(SIGTSTP, array($this, 'sigStopHandler')); |
||
544 | |||
545 | pcntl_signal(SIGCONT, array($this, 'sigContHandler')); |
||
546 | |||
547 | //pcntl_signal(SIGUSR1, array($this,'sigUsr1Handler')); |
||
548 | |||
549 | // register pluggable signals |
||
550 | |||
551 | foreach ( $pluggable_signals as $signal ) { |
||
552 | |||
553 | pcntl_signal($signal, array($this, 'genericSignalHandler')); |
||
554 | |||
555 | } |
||
556 | |||
557 | // register shutdown function |
||
558 | |||
559 | register_shutdown_function(array($this, 'shutdown')); |
||
560 | |||
561 | } |
||
562 | |||
563 | /** |
||
564 | * Delete all status file after exit() called |
||
565 | * |
||
566 | */ |
||
567 | 3 | final public function shutdown($force = false) { |
|
568 | |||
569 | 3 | if ( $this->parent_pid == posix_getpid() ) { |
|
570 | |||
571 | $this->logger->info("Shutdown in progress, cleaning environment"); |
||
572 | |||
573 | Lock::release(); |
||
574 | |||
575 | Status::release(); |
||
576 | |||
577 | Queue::release(); |
||
578 | |||
579 | Planner::release(); |
||
580 | |||
581 | } |
||
582 | |||
583 | 3 | if ( $force === true ) { |
|
584 | |||
585 | 3 | $this->logger->info("Shutdown in progress, cleaning environment"); |
|
586 | |||
587 | 3 | Status::release(); |
|
588 | |||
589 | 3 | Queue::release(); |
|
590 | |||
591 | 3 | Planner::release(); |
|
592 | |||
593 | 3 | } |
|
594 | |||
595 | 3 | } |
|
596 | |||
597 | /** |
||
598 | * The sigTerm handler. |
||
599 | * |
||
600 | * It kills everything and then exit with status 1 |
||
601 | */ |
||
602 | final public function sigTermHandler() { |
||
603 | |||
604 | if ( $this->parent_pid == posix_getpid() ) { |
||
605 | |||
606 | $this->logger->info("Received TERM signal, shutting down extender gracefully"); |
||
607 | |||
608 | $this->runner->killAll($this->parent_pid); |
||
609 | |||
610 | self::end(1); |
||
611 | |||
612 | } |
||
613 | |||
614 | } |
||
615 | |||
616 | /** |
||
617 | * The sigStop handler. |
||
618 | * |
||
619 | * It just pauses extender execution |
||
620 | */ |
||
621 | final public function sigStopHandler() { |
||
622 | |||
623 | if ( $this->parent_pid == posix_getpid() ) { |
||
624 | |||
625 | $this->logger->info("Received STOP signal, pausing extender"); |
||
626 | |||
627 | $this->paused = true; |
||
628 | |||
629 | } |
||
630 | |||
631 | } |
||
632 | |||
633 | /** |
||
634 | * The sigCont handler. |
||
635 | * |
||
636 | * It just resume extender execution |
||
637 | */ |
||
638 | final public function sigContHandler() { |
||
639 | |||
640 | if ( $this->parent_pid == posix_getpid() ) { |
||
641 | |||
642 | $this->logger->info("Received CONT signal, resuming extender"); |
||
643 | |||
644 | $this->paused = false; |
||
645 | |||
646 | } |
||
647 | |||
648 | } |
||
649 | |||
650 | /** |
||
651 | * The generig signal handler. |
||
652 | * |
||
653 | * It can be used to handle custom signals |
||
654 | */ |
||
655 | final public function genericSignalHandler($signal) { |
||
656 | |||
657 | if ( $this->parent_pid == posix_getpid() ) { |
||
658 | |||
659 | $this->logger->info("Received ".$signal." signal, firing associated event(s)"); |
||
660 | |||
661 | $this->events->fire("extender.signal.".$signal, "VOID", $this); |
||
662 | |||
663 | } |
||
664 | |||
665 | } |
||
666 | |||
667 | 3 | private function cycle() { |
|
668 | |||
669 | // fire extender ready event |
||
670 | |||
671 | 3 | $this->events->fire("extender", "VOID", $this); |
|
672 | |||
673 | // dispatch signals (if multithread active) |
||
674 | |||
675 | 3 | if ( $this->getMultithreadMode() ) pcntl_signal_dispatch(); |
|
676 | |||
677 | // if extender is paused (SIGINT), skip to extend |
||
678 | |||
679 | 3 | if ( $this->paused ) return; |
|
680 | |||
681 | // fix relative timestamp |
||
682 | |||
683 | 3 | $this->timestamp = microtime(true); |
|
684 | |||
685 | // fire tasktable event |
||
686 | |||
687 | 3 | $this->tasks = $this->events->fire("extender.tasks", "TASKSTABLE", $this->tasks); |
|
688 | |||
689 | // get the next planned activity interval |
||
690 | |||
691 | 3 | $plans = Planner::get(); |
|
692 | |||
693 | 3 | if ( !is_null($plans) AND $this->timestamp < $plans ) { |
|
694 | |||
695 | // nothing to do right now, still waiting if in daemon mode |
||
696 | |||
697 | $this->logger->info("Next planned job: ".date('c', $plans)); |
||
698 | |||
699 | $this->logger->notice("Extender completed\n"); |
||
700 | |||
701 | if ( $this->getDaemonMode() === false ) { |
||
702 | |||
703 | $this->shutdown(true); |
||
704 | |||
705 | self::end(0); |
||
706 | |||
707 | } |
||
708 | |||
709 | return; |
||
710 | |||
711 | } |
||
712 | |||
713 | // if no plan is retrieved, try to retrieve it from scheduler |
||
714 | |||
715 | try { |
||
716 | |||
717 | // get schedules and dispatch schedule event |
||
718 | |||
719 | 3 | list($schedules, $planned) = Scheduler::getSchedules($this->logger, $this->timestamp); |
|
720 | |||
721 | // write next planned activity interval |
||
722 | |||
723 | 3 | if ( !is_null($planned) AND $planned != 0 ) Planner::set($planned); |
|
724 | |||
725 | 3 | $scheduled = new Schedule(); |
|
726 | |||
727 | 3 | $scheduled->setSchedules($schedules); |
|
728 | |||
729 | // expose the current shcedule via events |
||
730 | |||
731 | 3 | $scheduled = $this->events->fire("extender.schedule", "SCHEDULE", $scheduled); |
|
732 | |||
733 | // if no jobs in queue, exit gracefully |
||
734 | |||
735 | 3 | if ( $scheduled->howMany() == 0 ) { |
|
736 | |||
737 | 3 | $this->logger->info("No jobs to process right now, exiting"); |
|
738 | |||
739 | 3 | $this->logger->notice("Extender completed\n"); |
|
740 | |||
741 | 3 | if ( $this->getDaemonMode() === false ) { |
|
742 | |||
743 | 3 | $this->shutdown(true); |
|
744 | |||
745 | 3 | self::end(0); |
|
746 | |||
747 | 3 | } |
|
748 | |||
749 | 3 | return; |
|
750 | |||
751 | } |
||
752 | |||
753 | // compose jobs |
||
754 | |||
755 | foreach ( $scheduled->getSchedules() as $schedule ) { |
||
756 | |||
757 | if ( $this->tasks->isRegistered($schedule['task']) ) { |
||
758 | |||
759 | $job = new Job(); |
||
760 | |||
761 | $job->setName($schedule['name']) |
||
762 | ->setId($schedule['id']) |
||
763 | ->setParameters(unserialize($schedule['params'])) |
||
764 | ->setTask($schedule['task']) |
||
765 | ->setClass($this->tasks->getClass($schedule['task'])); |
||
766 | |||
767 | $this->runner->addJob($job); |
||
768 | |||
769 | } else { |
||
770 | |||
771 | $this->logger->warning("Skipping job due to unknown task", array( |
||
772 | "ID" => $schedule['id'], |
||
773 | "NAME" => $schedule['name'], |
||
774 | "TASK" => $schedule['task'] |
||
775 | )); |
||
776 | |||
777 | } |
||
778 | |||
779 | } |
||
780 | |||
781 | // lauch runner |
||
782 | |||
783 | $result = $this->runner->run(); |
||
784 | |||
785 | // free runner for next cycle |
||
786 | |||
787 | $this->runner->free(); |
||
788 | |||
789 | // compose results |
||
790 | |||
791 | $results = new JobsResult($result); |
||
792 | |||
793 | // update schedules |
||
794 | |||
795 | Scheduler::updateSchedules($this->logger, $result); |
||
796 | |||
797 | // increment counters |
||
798 | |||
799 | foreach ( $result as $r ) { |
||
800 | |||
801 | if ( $r[2] ) $this->completed_processes++; |
||
802 | |||
803 | else $this->failed_processes++; |
||
804 | |||
805 | } |
||
806 | |||
807 | } catch (Exception $e) { |
||
808 | |||
809 | $this->logger->error($e->getMessage()); |
||
810 | |||
811 | if ( $this->getDaemonMode() === false ) { |
||
812 | |||
813 | self::end(1); |
||
814 | |||
815 | } |
||
816 | |||
817 | } |
||
818 | |||
819 | // fire result event |
||
820 | |||
821 | $this->events->fire("extender.result", "VOID", $results); |
||
822 | |||
823 | $this->logger->notice("Extender completed\n"); |
||
824 | |||
825 | // show summary (if -s) |
||
826 | |||
827 | if ( $this->summary_mode ) self::showSummary($this->timestamp, $result, $this->color); |
||
828 | |||
829 | Status::dump($this->timestamp_absolute, $this->parent_pid, $this->completed_processes, $this->failed_processes, $this->paused); |
||
830 | |||
831 | if ( $this->getDaemonMode() === false ) { |
||
832 | |||
833 | $this->shutdown(true); |
||
834 | |||
835 | self::end(0); |
||
836 | |||
837 | } |
||
838 | |||
839 | } |
||
840 | |||
841 | private static function showHelp($color) { |
||
842 | |||
843 | echo Version::getDescription(); |
||
844 | |||
845 | echo "\nVersion: ".$color->convert("%g".Version::getVersion()."%n"); |
||
846 | |||
847 | echo "\n\nAvailable options:"; |
||
848 | |||
849 | echo "\n------------------"; |
||
850 | |||
851 | echo "\n".$color->convert("%g -v %n").": verbose mode"; |
||
852 | |||
853 | echo "\n".$color->convert("%g -V %n").": debug mode"; |
||
854 | |||
855 | echo "\n".$color->convert("%g -s %n").": show summary of executed jobs (if any)"; |
||
856 | |||
857 | echo "\n".$color->convert("%g -d %n").": run extender in daemon mode"; |
||
858 | |||
859 | echo "\n".$color->convert("%g -h %n").": show this help"; |
||
860 | |||
861 | echo "\n\n"; |
||
862 | |||
863 | } |
||
864 | |||
865 | 12 | private static function getCommandlineOptions() { |
|
866 | |||
867 | 12 | $options = getopt("svdVh"); |
|
868 | |||
869 | return array( |
||
870 | 12 | array_key_exists('v', $options) ? true : false, |
|
871 | 12 | array_key_exists('V', $options) ? true : false, |
|
872 | 12 | array_key_exists('s', $options) ? true : false, |
|
873 | 12 | array_key_exists('d', $options) ? true : false, |
|
874 | 12 | array_key_exists('h', $options) ? true : false |
|
875 | 12 | ); |
|
876 | |||
877 | } |
||
878 | |||
879 | /** |
||
880 | * @param double $timestamp |
||
881 | */ |
||
882 | private static function showSummary($timestamp, $completed_processes, $color) { |
||
883 | |||
884 | $header_string = "\n\n --- Comodojo Extender Summary --- ".date('c', $timestamp)."\n\n"; |
||
885 | |||
886 | $tbl = new Console_Table(CONSOLE_TABLE_ALIGN_LEFT, CONSOLE_TABLE_BORDER_ASCII, 1, null, true); |
||
887 | |||
888 | $tbl->setHeaders(array( |
||
889 | 'Pid', |
||
890 | 'Name', |
||
891 | 'Log Id', |
||
892 | 'Success', |
||
893 | 'Result (truncated)', |
||
894 | 'Time elapsed' |
||
895 | )); |
||
896 | |||
897 | foreach ( $completed_processes as $key => $completed_process ) { |
||
898 | |||
899 | $pid = $completed_process[0]; |
||
900 | |||
901 | $name = $completed_process[1]; |
||
902 | |||
903 | $success = $color->convert($completed_process[2] ? "%gYES%n" : "%rNO%n"); |
||
904 | |||
905 | $result = str_replace(array("\r", "\n"), " ", $completed_process[5]); |
||
906 | |||
907 | $result = strlen($result) >= 80 ? substr($result, 0, 80)."..." : $result; |
||
908 | |||
909 | $elapsed = empty($completed_process[4]) ? "--" : ($completed_process[4] - $completed_process[3]); |
||
910 | |||
911 | $worklog_id = $completed_process[7]; |
||
912 | |||
913 | $tbl->addRow(array( |
||
914 | $pid, |
||
915 | $name, |
||
916 | $worklog_id, |
||
917 | $success, |
||
918 | $result, |
||
919 | $elapsed |
||
920 | )); |
||
921 | |||
922 | } |
||
923 | |||
924 | $footer_string = "\n\nTotal script runtime: ".(microtime(true) - $timestamp)." seconds\r\n\n"; |
||
925 | |||
926 | print $header_string.$tbl->getTable().$footer_string; |
||
927 | |||
928 | } |
||
929 | |||
930 | /** |
||
931 | * @param integer $returnCode |
||
932 | */ |
||
933 | 3 | View Code Duplication | private static function end($returnCode) { |
0 ignored issues
–
show
|
|||
934 | |||
935 | 3 | if ( defined('COMODOJO_PHPUNIT_TEST') && @constant('COMODOJO_PHPUNIT_TEST') === true ) { |
|
936 | |||
937 | 3 | if ( $returnCode === 1 ) throw new Exception("PHPUnit Test Exception"); |
|
938 | |||
939 | 3 | else return $returnCode; |
|
940 | |||
941 | } else { |
||
942 | |||
943 | exit($returnCode); |
||
944 | |||
945 | } |
||
946 | |||
947 | } |
||
948 | |||
949 | } |
||
950 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.