diff --git a/lib/classes/module/console/schedulerStart.class.php b/lib/classes/module/console/schedulerStart.class.php index 52afd000be..8dd81c9ca4 100644 --- a/lib/classes/module/console/schedulerStart.class.php +++ b/lib/classes/module/console/schedulerStart.class.php @@ -29,6 +29,20 @@ class module_console_schedulerStart extends Command parent::__construct($name); $this->setDescription('Start the scheduler'); + $this->addOption( + 'nolog' + , NULL + , 1 | InputOption::VALUE_NONE + , 'do not log (scheduler) to logfile' + , NULL + ); + $this->addOption( + 'notasklog' + , NULL + , 1 | InputOption::VALUE_NONE + , 'do not log (tasks) to logfiles' + , NULL + ); $this->setHelp( "You should use launch the command and finish it with `&`" . " to return to the console\n\n" @@ -38,7 +52,7 @@ class module_console_schedulerStart extends Command return $this; } - public function execute(InputInterface $input, OutputInterface $output) + public function execute(InputInterface $zinput, OutputInterface $output) { if(!setup::is_installed()) { @@ -48,7 +62,7 @@ class module_console_schedulerStart extends Command require_once dirname(__FILE__) . '/../../../../lib/bootstrap.php'; $scheduler = new task_Scheduler(); - $scheduler->run($output, true); + $scheduler->run($zinput, $output); //, !$input->getOption('nolog'), !$input->getOption('notasklog')); return; } diff --git a/lib/classes/module/console/taskrun.class.php b/lib/classes/module/console/taskrun.class.php index fe7da52197..31714db34a 100644 --- a/lib/classes/module/console/taskrun.class.php +++ b/lib/classes/module/console/taskrun.class.php @@ -43,10 +43,27 @@ class module_console_taskrun extends Command , 'The name of the runner (manual, scheduler...)' , task_abstract::RUNNER_MANUAL ); + $this->addOption( + 'nolog' + , NULL + , 1 | InputOption::VALUE_NONE + , 'do not log to logfile' + , NULL + ); $this->setDescription('Run task'); return $this; } + + function sig_handler($signo) + { + if($this->task) + { + $this->task->log(sprintf("signal %s received", $signo)); + if($signo == SIGTERM) + $this->task->set_running(false); + } + } public function execute(InputInterface $input, OutputInterface $output) { @@ -81,9 +98,12 @@ class module_console_taskrun extends Command register_tick_function(array($this, 'tick_handler'), true); declare(ticks=1); + pcntl_signal(SIGTERM, array($this, 'sig_handler')); + + $this->task->run($runner, $input, $output); + +$this->task->log(sprintf("%s [%d] taskrun : returned from 'run()', get_status()=%s \n", __FILE__, __LINE__, $this->task->get_status())); - $this->task->run($runner); - printf("TASK QUIT\n"); return $this; } diff --git a/lib/classes/task/Scheduler.class.php b/lib/classes/task/Scheduler.class.php index deae331966..b70dfe0258 100644 --- a/lib/classes/task/Scheduler.class.php +++ b/lib/classes/task/Scheduler.class.php @@ -26,6 +26,7 @@ class task_Scheduler const METHOD_PROC_OPEN = 'METHOD_PROC_OPEN'; private $method; + private $input; protected $output; protected function log($message) @@ -33,18 +34,21 @@ class task_Scheduler $registry = registry::get_instance(); $logdir = $registry->get('GV_RootPath') . 'logs/'; - logs::rotate($logdir . "scheduler.log"); + logs::rotate($logdir . "scheduler_l.log"); + logs::rotate($logdir . "scheduler_o.log"); + logs::rotate($logdir . "scheduler_e.log"); $date_obj = new DateTime(); $message = sprintf("%s\t%s", $date_obj->format(DATE_ATOM), $message); if($this->output instanceof OutputInterface) { - $this->output->writeln($message); +// $this->output->writeln($message); } - // else +// $this->output->writeln($this->input->getOption('nolog')); + if($this->input && !($this->input->getOption('nolog'))) { - file_put_contents($logdir . "scheduler.log", $message."\n", FILE_APPEND); + file_put_contents($logdir . "scheduler_l.log", $message . "\n", FILE_APPEND); } return $this; } @@ -52,20 +56,32 @@ class task_Scheduler protected static function get_connection() { require dirname(__FILE__) . '/../../../config/connexion.inc'; - - return new connection_pdo('appbox', $hostname, $port, $user, $password, $dbname); + return(appbox::get_instance()->get_connection()); } - public function run(OutputInterface $output = null, $log_tasks = true) + public function run($input=null, OutputInterface $output = null) //, $log = true, $log_tasks = true) { $this->method = self::METHOD_FORK; require_once dirname(__FILE__) . '/../../bootstrap.php'; + $this->input = $input; $this->output = $output; $appbox = appbox::get_instance(); $registry = $appbox->get_registry(); + $nullfile = ''; $system = system_server::get_platform(); + switch($system) + { + case "WINDOWS": + $nullfile = 'NUL'; + break; + default: + case "DARWIN": + case "LINUX": + $nullfile = '/dev/null'; + break; + } $lockdir = $registry->get('GV_RootPath') . 'tmp/locks/'; @@ -98,16 +114,15 @@ class task_Scheduler } } } - + $this->log(sprintf("running scheduler with method %s", $this->method)); - if($this->method == self::METHOD_FORK) pcntl_signal(SIGCHLD, SIG_IGN); $logdir = $registry->get('GV_RootPath') . 'logs/'; - $conn = self::get_connection(); + $conn = appbox::get_instance()->get_connection(); $taskPoll = array(); // the poll of tasks @@ -146,15 +161,36 @@ class task_Scheduler while($schedstatus == 'started' || $runningtask > 0) { - while(!$conn->ping()) + while(1) { + try + { + assert(is_object($conn)); + $ping = @$conn->ping(); + } + catch(ErrorException $e) + { + $ping = false; + } + if($ping) + break; + unset($conn); if(!$connwaslost) { $this->log(sprintf("Warning : abox connection lost, restarting in 10 min.")); } - sleep(60 * 10); - $conn = self::get_connection(); + for($i = 0; $i < 60 * 10; $i++) + sleep(1); + try + { + $conn = appbox::get_instance()->get_connection(); + } + catch(ErrorException $e) + { + $ping = false; + } + $connwaslost = true; } if($connwaslost) @@ -168,13 +204,21 @@ class task_Scheduler $connwaslost = false; } - +// printf("%d \n", __LINE__); $schedstatus = ''; - $sql = "SELECT schedstatus FROM sitepreff"; - $stmt = $conn->prepare($sql); - $stmt->execute(); - $row = $stmt->fetch(PDO::FETCH_ASSOC); - $stmt->closeCursor(); + $row = NULL; + try + { + $sql = "SELECT schedstatus FROM sitepreff"; + $stmt = $conn->prepare($sql); + $stmt->execute(); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + } + catch(ErrorException $e) + { + continue; + } if($row) { @@ -193,14 +237,14 @@ class task_Scheduler $stmt = $conn->prepare($sql); $stmt->execute(); $stmt->closeCursor(); - $this->log("schedstatus == 'stopping', waiting tasks to end"); } - logs::rotate($logdir . "scheduler.log"); - logs::rotate($logdir . "scheduler.error.log"); - + logs::rotate($logdir . "scheduler_t.log"); + logs::rotate($logdir . "scheduler_o.log"); + logs::rotate($logdir . "scheduler_e.log"); +// printf("%d \n", __LINE__); // initialy, all tasks are supposed to be removed from the poll foreach($taskPoll as $tkey => $task) $taskPoll[$tkey]["todel"] = true; @@ -208,32 +252,41 @@ class task_Scheduler foreach($task_manager->get_tasks(true) as $task) { $tkey = "t_" . $task->get_task_id(); + $status = $task->get_status(); - logs::rotate($logdir . "task_$tkey.log"); - logs::rotate($logdir . "task_$tkey.error.log"); + logs::rotate($logdir . "task_t_" . $task->get_task_id() . ".log"); + logs::rotate($logdir . "task_o_" . $task->get_task_id() . ".log"); + logs::rotate($logdir . "task_e_" . $task->get_task_id() . ".log"); if(!isset($taskPoll[$tkey])) { // the task is not in the poll, add it $phpcli = $registry->get('GV_cli'); - switch($system) { + case "WINDOWS": + $cmd = $phpcli; + $args = array('-f', $registry->get('GV_RootPath') . 'bin/console', '--', '-q', 'task:run', $task->get_task_id(), '--runner=scheduler'); + if($this->input && ($this->input->getOption('notasklog'))) + $args[] = 'notasklog'; + break; default: case "DARWIN": - case "WINDOWS": case "LINUX": $cmd = $phpcli; - $args = array('-f', $registry->get('GV_RootPath') . 'bin/console', 'task:run', $task->get_task_id(), '--runner=scheduler'); + $args = array('-f', $registry->get('GV_RootPath') . 'bin/console', '--', '-q', 'task:run', $task->get_task_id(), '--runner=scheduler'); + if($this->input && ($this->input->getOption('notasklog'))) + $args[] = 'notasklog'; break; } $taskPoll[$tkey] = array( "task" => $task, - "current_status" => $task->get_status(), + "current_status" => $status, "cmd" => $cmd, "args" => $args, - "killat" => null + "killat" => null, + "sigterm_sent" => false ); if($this->method == self::METHOD_PROC_OPEN) { @@ -245,24 +298,24 @@ class task_Scheduler sprintf( "new Task %s, status=%s" , $taskPoll[$tkey]["task"]->get_task_id() - , $task->get_status() + , $status ) ); } else { // the task is already in the poll, update its status - if($taskPoll[$tkey]["current_status"] != $task->get_status()) + if($taskPoll[$tkey]["current_status"] != $status) { $this->log( sprintf( "Task %s, oldstatus=%s, newstatus=%s" , $taskPoll[$tkey]["task"]->get_task_id() , $taskPoll[$tkey]["current_status"] - , $task->get_status() + , $status ) ); - $taskPoll[$tkey]["current_status"] = $task->get_status(); + $taskPoll[$tkey]["current_status"] = $status; } // update the whole task object unset($taskPoll[$tkey]["task"]); @@ -284,18 +337,16 @@ class task_Scheduler } } - - /** - * Launch task that are not yet launched - */ + // Launch task that are not yet launched $runningtask = 0; foreach($taskPoll as $tkey => $tv) { - switch($tv['task']->get_status()) + $status = $tv['task']->get_status(); + switch($status) { default: - $this->log(sprintf('Unknow status `%s`', $tv['task']->get_status())); + $this->log(sprintf('Unknow status `%s`', $status)); break; case task_abstract::RETURNSTATUS_TORESTART: @@ -332,24 +383,8 @@ class task_Scheduler { if(!$taskPoll[$tkey]["process"]) { - $descriptors = array( - 1 => array("pipe", "w"), - 2 => array("pipe", "w") - ); - - if($log_tasks === true) - { - $descriptors[1] = array( - "file" - , $logdir . "task_$tkey.log" - , "a+" - ); - $descriptors[2] = array( - "file" - , $logdir . "task_$tkey.error.log" - , "a+" - ); - } + $descriptors[1] = array('file', $logdir . "task_o_" . $task->get_task_id() . ".log", 'a+'); + $descriptors[2] = array('file', $logdir . "task_e_" . $task->get_task_id() . ".log", 'a+'); $taskPoll[$tkey]["process"] = proc_open( $taskPoll[$tkey]["cmd"] . ' ' . implode(' ', $taskPoll[$tkey]["args"]) @@ -415,27 +450,25 @@ class task_Scheduler // child // printf("hello i am child pid=%d\n", getmypid()); // printf("%s %s \n", $taskPoll[$tkey]["cmd"], implode(' ', $taskPoll[$tkey]["args"])); - // ; umask(0); - openlog('MyLog', LOG_PID | LOG_PERROR, LOG_LOCAL0); if(posix_setsid() < 0) die("Forked process could not detach from terminal\n"); - //chdir(dirname(__FILE__)); + fclose(STDIN); fclose(STDOUT); fclose(STDERR); - $fdIN = fopen('/dev/null', 'r'); - $fdOUT = fopen($logdir . "task_$tkey.log", 'a+'); - $fdERR = fopen($logdir . "task_$tkey.error.log", 'a+'); + $fdIN = fopen($nullfile, 'r'); + $fdOUT = fopen($logdir . "task_o_" . $taskPoll[$tkey]["task"]->get_task_id() . ".log", 'a+'); + $fdERR = fopen($logdir . "task_e_" . $taskPoll[$tkey]["task"]->get_task_id() . ".log", 'a+'); + $this->log(sprintf("exec('%s %s')", $taskPoll[$tkey]["cmd"], implode(' ', $taskPoll[$tkey]["args"]))); pcntl_exec($taskPoll[$tkey]["cmd"], $taskPoll[$tkey]["args"]); - sleep(2); } else { // parent - // printf("hello i am parent pid=%d\n", getmypid()); + // sleep(2); } } break; @@ -506,33 +539,30 @@ class task_Scheduler if($taskPoll[$tkey]["killat"] === NULL) $taskPoll[$tkey]["killat"] = time() + self::TASKDELAYTOQUIT; - $tpid = $taskPoll[$tkey]['task']->get_pid(); - if($tpid) + $pid = $taskPoll[$tkey]['task']->get_pid(); + if($pid) { + if(!$taskPoll[$tkey]['sigterm_sent']) + { + posix_kill($pid, SIGTERM); + $this->log( + sprintf( + "SIGTERM sent to task %s (pid=%s)" + , $taskPoll[$tkey]["task"]->get_task_id() + , $pid + ) + ); + } + if(($dt = $taskPoll[$tkey]["killat"] - time()) < 0) { - if($this->method == self::METHOD_PROC_OPEN) - { - $pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid $tpid`); - foreach($pids as $pid) - { - if(is_numeric($pid)) - { - $this->log("Killing pid %d", $pid); - posix_kill($pid, 9); - } - } - } - elseif($this->method == self::METHOD_FORK) - { - posix_kill($tpid, 9); - } + posix_kill($pid, 9); $this->log( sprintf( "SIGKILL sent to task %s (pid=%s)" , $taskPoll[$tkey]["task"]->get_task_id() - , $tpid + , $pid ) ); @@ -569,6 +599,7 @@ class task_Scheduler , $taskPoll[$tkey]["task"]->get_task_id() ) ); + $taskPoll[$tkey]["task"]->set_status(task_abstract::RETURNSTATUS_STOPPED); } break; @@ -590,290 +621,8 @@ class task_Scheduler } } - - /* - - - $common_status = array( - task_abstract::STATUS_STARTED - , task_abstract::RETURNSTATUS_STOPPED - ); - - - foreach($taskPoll as $tkey => $tv) - { - // if (!in_array($taskPoll[$tkey]["task"]->get_status(), $common_status)) - // { - // $this->log( - // sprintf( - // 'task %s has status %s' - // , $taskPoll[$tkey]["task"]->get_task_id() - // , $taskPoll[$tkey]["task"]->get_status() - // ) - // ); - // } - switch($tv['task']->get_status()) - { - default: - $this->log(sprintf('Unknow status `%s`', $tv['task']->get_status())); - break; - - case task_abstract::RETURNSTATUS_TORESTART: - if(!$taskPoll[$tkey]['task']->get_pid()) - { - @fclose($taskPoll[$tkey]["pipes"][1]); - @fclose($taskPoll[$tkey]["pipes"][2]); - @proc_close($taskPoll[$tkey]["process"]); - - $taskPoll[$tkey]["process"] = null; - if($schedstatus == 'started') - { - $taskPoll[$tkey]["task"]->set_status(task_abstract::STATUS_TOSTART); - } - // trick to start the task immediatly : DON'T break if ending with 'tostart' - // so it will continue with 'tostart' case ! - } - else - { - break; - } - - case task_abstract::STATUS_TOSTART: - // if scheduler is 'tostop', don't launch a new task ! - if($schedstatus != 'started') - break; - - $taskPoll[$tkey]["killat"] = NULL; - if(!$taskPoll[$tkey]["process"]) - { - $descriptors = array( - 1 => array("pipe", "w"), - 2 => array("pipe", "w") - ); - - if($log_tasks === true) - { - $descriptors[1] = array( - "file" - , $logdir . "task_$tkey.log" - , "a+" - ); - $descriptors[2] = array( - "file" - , $logdir . "task_$tkey.error.log" - , "a+" - ); - } - - $taskPoll[$tkey]["process"] = proc_open( - $taskPoll[$tkey]["cmd"].' '.implode(' ', $taskPoll[$tkey]["args"]) - , $descriptors - , $taskPoll[$tkey]["pipes"] - , $registry->get('GV_RootPath') . "bin/" - , null - , array('bypass_shell' => true) - ); - - if(is_resource($taskPoll[$tkey]["process"])) - { - sleep(2); // let the process lock and write it's pid - } - - if(is_resource($taskPoll[$tkey]["process"]) && $taskPoll[$tkey]['task']->get_pid() !== null) - { - - // ************************************************ - // file_put_contents("/tmp/scheduler2.log", sprintf("%s [%d] : RUNNING ? : pid=%s \n", __FILE__, __LINE__, $taskPoll[$tkey]['task']->get_pid()), FILE_APPEND); - - $this->log( - sprintf( - "Task %s '%s' started (pid=%s)" - , $taskPoll[$tkey]['task']->get_task_id() - , $taskPoll[$tkey]["cmd"] - , $taskPoll[$tkey]['task']->get_pid() - ) - ); - $runningtask++; - } - else - { - // ************************************************ - // file_put_contents("/tmp/scheduler2.log", sprintf("%s [%d] : NOT RUNNING ? : pid=%s \n", __FILE__, __LINE__, $taskPoll[$tkey]['task']->get_pid()), FILE_APPEND); - $taskPoll[$tkey]["task"]->increment_crash_counter(); - - @fclose($taskPoll[$tkey]["pipes"][1]); - @fclose($taskPoll[$tkey]["pipes"][2]); - @proc_close($taskPoll[$tkey]["process"]); - $taskPoll[$tkey]["process"] = null; - - $this->log( - sprintf( - "Task %s '%s' failed to start %d times" - , $taskPoll[$tkey]["task"]->get_task_id() - , $taskPoll[$tkey]["cmd"] - , $taskPoll[$tkey]["task"]->get_crash_counter() - ) - ); - - if($taskPoll[$tkey]["task"]->get_crash_counter() > 5) - { - $taskPoll[$tkey]["task"]->set_status(task_abstract::RETURNSTATUS_STOPPED); - } - else - { - $taskPoll[$tkey]["task"]->set_status(task_abstract::STATUS_TOSTART); - } - } - } - break; - - case task_abstract::STATUS_STARTED: - $crashed = false; - // If no process, the task is probably manually ran - if($taskPoll[$tkey]["process"]) - { - $taskPoll[$tkey]["killat"] = NULL; - - // if(is_resource($taskPoll[$tkey]["process"])) - // { - // $proc_status = proc_get_status($taskPoll[$tkey]["process"]); - // if($proc_status['running']) - // $runningtask++; - // else - // $crashed = true; - // } - // else - // { - // $crashed = true; - // } - - if($taskPoll[$tkey]['task']->get_pid()) - $runningtask++; - else - $crashed = true; - } - - if($crashed === true && $taskPoll[$tkey]["task"]->get_status() === task_abstract::RETURNSTATUS_TORESTART) - { - $crashed = false; - } - if($crashed) - { - $taskPoll[$tkey]["task"]->increment_crash_counter(); - - @fclose($taskPoll[$tkey]["pipes"][1]); - @fclose($taskPoll[$tkey]["pipes"][2]); - @proc_close($taskPoll[$tkey]["process"]); - $taskPoll[$tkey]["process"] = null; - - $this->log( - sprintf( - "Task %s crashed %d times" - , $taskPoll[$tkey]["task"]->get_task_id() - , $taskPoll[$tkey]["task"]->get_crash_counter() - ) - ); - - - if($taskPoll[$tkey]["task"]->get_crash_counter() > 5) - { - $taskPoll[$tkey]["task"]->set_status(task_abstract::RETURNSTATUS_STOPPED); - } - else - { - $taskPoll[$tkey]["task"]->set_status(task_abstract::STATUS_TOSTART); - } - } - break; - - case task_abstract::STATUS_TOSTOP: - if($taskPoll[$tkey]["process"]) - { - if($taskPoll[$tkey]["killat"] === NULL) - $taskPoll[$tkey]["killat"] = time() + self::TASKDELAYTOQUIT; - - if(($dt = $taskPoll[$tkey]["killat"] - time()) < 0) - { - $ppid = $taskPoll[$tkey]['task']->get_pid(); - $pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid $ppid`); - foreach($pids as $pid) - { - if(is_numeric($pid)) - { - $this->log("Killing pid %d", $pid); - posix_kill($pid, 9); - } - } - - $this->log( - sprintf( - "SIGKILL sent to task %s (pid=%s)" - , $taskPoll[$tkey]["task"]->get_task_id() - , $taskPoll[$tkey]["task"]->get_pid() - ) - ); - - proc_terminate($taskPoll[$tkey]["process"], 9); - @fclose($taskPoll[$tkey]["pipes"][1]); - @fclose($taskPoll[$tkey]["pipes"][2]); - proc_close($taskPoll[$tkey]["process"]); - unlink($lockdir . 'task_' . $taskPoll[$tkey]['task']->get_task_id() . '.lock'); - - $taskPoll[$tkey]["task"]->increment_crash_counter(); - // $taskPoll[$tkey]["task"]->set_pid(null); - $taskPoll[$tkey]["task"]->set_status(task_abstract::RETURNSTATUS_STOPPED); - } - else - { - $this->log( - sprintf( - "waiting task %s to quit (kill in %d seconds)" - , $taskPoll[$tkey]["task"]->get_task_id() - , $dt - ) - ); - $runningtask++; - } - } - break; - - case task_abstract::RETURNSTATUS_STOPPED: - case task_abstract::RETURNSTATUS_TODELETE: - if($taskPoll[$tkey]["process"]) - { - @fclose($taskPoll[$tkey]["pipes"][1]); - @fclose($taskPoll[$tkey]["pipes"][2]); - @proc_close($taskPoll[$tkey]["process"]); - - $taskPoll[$tkey]["process"] = null; - } - break; - } - } - - */ - - - - - - - - - - - $to_reopen = false; - if($conn->ping()) - { - $conn->close(); - unset($conn); - $to_reopen = true; - } - sleep($sleeptime); - if($to_reopen) - { - $conn = self::get_connection(); - } + for($i=0; $i<$sleeptime; $i++) + sleep(1); } $sql = "UPDATE sitepreff SET schedstatus='stopped', schedpid='0'"; diff --git a/lib/classes/task/abstract.class.php b/lib/classes/task/abstract.class.php index 801696d992..8662d1d456 100644 --- a/lib/classes/task/abstract.class.php +++ b/lib/classes/task/abstract.class.php @@ -89,6 +89,9 @@ abstract class task_abstract */ protected $return_value; protected $runner; + + private $input; + private $output; /** * delay between two loops @@ -104,7 +107,15 @@ abstract class task_abstract public function get_status() { - return $this->task_status; + $conn = connection::getPDOConnection(); + $sql = 'SELECT status FROM task2 WHERE task_id = :taskid'; + $stmt = $conn->prepare($sql); + $stmt->execute(array(':taskid' => $this->taskid)); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + if(!$row) + throw new Exception('Unknown task id'); + return $row['status']; } public function printInterfaceHEAD() @@ -141,7 +152,7 @@ abstract class task_abstract $stmt = $conn->prepare($sql); $stmt->execute(array(':status' => $status, ':taskid' => $this->get_task_id())); $stmt->closeCursor(); - +$this->log(sprintf("task %d <- %s", $this->get_task_id(), $status)); $this->task_status = $status; return $this->task_status; @@ -265,6 +276,7 @@ abstract class task_abstract function __construct($taskid) { $this->taskid = $taskid; + phrasea::use_i18n(Session_Handler::get_locale()); $this->system = system_server::get_platform(); @@ -296,7 +308,7 @@ abstract class task_abstract $this->log($e->getMessage()); $this->log(("Warning : abox connection lost, restarting in 10 min.")); - for($t = 60 * 10; $t > 0; $t--) // DON'T do sleep(600) because it prevents ticks ! + for($t = 60 * 10; $this->running && $t > 0; $t--) // DON'T do sleep(600) because it prevents ticks ! sleep(1); $this->running = false; @@ -354,18 +366,18 @@ abstract class task_abstract public function delete() { - $conn = connection::getPDOConnection(); - $registry = registry::get_instance(); - $sql = "DELETE FROM task2 WHERE task_id = :task_id"; - $stmt = $conn->prepare($sql); - $stmt->execute(array(':task_id' => $this->get_task_id())); - $stmt->closeCursor(); + if(!$this->get_pid()) // do not delete a running task + { + $conn = connection::getPDOConnection(); + $registry = registry::get_instance(); + $sql = "DELETE FROM task2 WHERE task_id = :task_id"; + $stmt = $conn->prepare($sql); + $stmt->execute(array(':task_id' => $this->get_task_id())); + $stmt->closeCursor(); - $lock_file = $registry->get('GV_RootPath') . 'tmp/locks/task_' . $this->get_task_id() . '.lock'; - if(is_writable($lock_file)) - unlink($lock_file); - - return; + $lock_file = $registry->get('GV_RootPath') . 'tmp/locks/task_' . $this->get_task_id() . '.lock'; + @unlink($lock_file); + } } protected function check_memory_usage() @@ -520,6 +532,11 @@ abstract class task_abstract return $this; } + + public function set_running($stat) + { + $this->running = $stat; + } protected function pause($when_started=0) { @@ -532,15 +549,16 @@ abstract class task_abstract $conn = connection::getPDOConnection(); $conn->close(); unset($conn); -// sleep($this->period - $when_started); - for($t = $this->period - $when_started; $t > 0; $t--) // DON'T do sleep($this->period - $when_started) because it prevents ticks ! + for($t = $this->period - $when_started; $this->running && $t > 0; $t--) // DON'T do sleep($this->period - $when_started) because it prevents ticks ! sleep(1); } } } - final public function run($runner) + final public function run($runner, $input=null, $output = null) { + $this->input = $input; + $this->output = $output; // ************************************************ // file_put_contents("/tmp/scheduler2.log", sprintf("%s [%d] : LAUNCHING : tid=%s \n", __FILE__, __LINE__, $this->get_task_id()), FILE_APPEND); @@ -555,7 +573,7 @@ abstract class task_abstract { // ************************************************ // file_put_contents("/tmp/scheduler2.log", sprintf("%s [%d] : LAUNCH OPENED AND CANT LOCK : pid=%s \n", __FILE__, __LINE__, getmypid()), FILE_APPEND); - printf(("runtask::ERROR : task already running.\n"), $taskid); + $this->log("runtask::ERROR : task already running."); fclose($tasklock); return; @@ -574,8 +592,8 @@ abstract class task_abstract $this->set_status(self::STATUS_STARTED); $this->running = true; + $this->run2(); -// $this->set_pid(0); flock($tasklock, LOCK_UN | LOCK_NB); ftruncate($tasklock, 0); @@ -586,7 +604,6 @@ abstract class task_abstract $this->delete(); else $this->set_status($this->return_value); - return $this; } @@ -651,13 +668,31 @@ abstract class task_abstract // print($s); } - function log($msg = '') + public function log($message) { - // $d = debug_backtrace(false); - // printf("%s\t[%s]\t%s\r\n", date('r'), $d[0]['line'], $msg); - printf("%s\t%s\r\n", date('r'), $msg); - } + $registry = registry::get_instance(); + $logdir = $registry->get('GV_RootPath') . 'logs/'; + logs::rotate($logdir . 'task_l_'.$this->taskid.'.log'); + logs::rotate($logdir . 'task_o_'.$this->taskid.'.log'); + logs::rotate($logdir . 'task_e_'.$this->taskid.'.log'); + + $date_obj = new DateTime(); + $message = sprintf("%s\t%s", $date_obj->format(DATE_ATOM), $message); + + if($this->output) + { + $this->output->writeln($message); + } + if( $this->input && !($this->input->getOption('nolog')) ) + { + file_put_contents($logdir . 'task_l_'.$this->taskid.'.log', $message."\n", FILE_APPEND); + } + + return $this; + } + + public static function interfaceAvailable() { return true; diff --git a/lib/classes/task/databoxAbstract.class.php b/lib/classes/task/databoxAbstract.class.php index 0079a092a2..580bdeca41 100644 --- a/lib/classes/task/databoxAbstract.class.php +++ b/lib/classes/task/databoxAbstract.class.php @@ -33,27 +33,32 @@ abstract class task_databoxAbstract extends task_abstract protected function run2() { +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); while($this->running) { +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); try { $conn = connection::getPDOConnection(); } catch(Exception $e) { +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); $this->log($e->getMessage()); $this->log(("Warning : abox connection lost, restarting in 10 min.")); - for($t = 60 * 10; $t; $t--) // DON'T do sleep(600) because it prevents ticks ! + for($t = 60 * 10; $this->running && $t; $t--) // DON'T do sleep(600) because it prevents ticks ! sleep(1); $this->running = false; $this->return_value = self::RETURNSTATUS_TORESTART; +$this->log(sprintf("%s [%d] returning from 'run2()' rv=%s \n", __FILE__, __LINE__, $this->return_value)); return; } $this->set_last_exec_time(); +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); try { @@ -67,12 +72,14 @@ abstract class task_databoxAbstract extends task_abstract } catch(Exception $e) { +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); $this->task_status = self::STATUS_TOSTOP; $this->return_value = self::RETURNSTATUS_STOPPED; $rs = array(); } foreach($rs as $row) { +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); if(!$this->running) break; @@ -89,6 +96,7 @@ abstract class task_databoxAbstract extends task_abstract try { +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); $this->load_settings(simplexml_load_string($row['settings'])); } catch(Exception $e) @@ -98,14 +106,26 @@ abstract class task_databoxAbstract extends task_abstract } $this->current_state = self::STATE_OK; - $this->process_sbas() - ->check_current_state() - ->flush_records_sbas(); +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); + $this->process_sbas()->check_current_state(); +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); + $this->process_sbas()->flush_records_sbas(); +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); } +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); $this->increment_loops(); +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); $this->pause($duration); } +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); + $this->process_sbas()->check_current_state(); +$this->log(sprintf("%s [%d] rv=%s \n", __FILE__, __LINE__, $this->return_value)); + $this->process_sbas()->flush_records_sbas(); + + $this->set_status($this->return_value); + +$this->log(sprintf("%s [%d] returning from 'run2()' rv=%s \n", __FILE__, __LINE__, $this->return_value)); return; } diff --git a/lib/classes/task/manager.class.php b/lib/classes/task/manager.class.php index 8812c00783..9c428549db 100644 --- a/lib/classes/task/manager.class.php +++ b/lib/classes/task/manager.class.php @@ -167,13 +167,7 @@ class task_manager public function get_scheduler_state2() { - $sql = "SELECT UNIX_TIMESTAMP()-UNIX_TIMESTAMP(schedqtime) AS qdelay, schedstatus AS status FROM sitepreff"; - $stmt = $this->appbox->get_connection()->prepare($sql); - $stmt->execute(); - $ret = $stmt->fetch(PDO::FETCH_ASSOC); - $stmt->closeCursor(); - $ret['pid'] = NULL; - + $pid = NULL; $appbox = appbox::get_instance(); $lockdir = $appbox->get_registry()->get('GV_RootPath') . 'tmp/locks/'; if( ($schedlock = fopen( $lockdir . 'scheduler.lock', 'a+')) ) @@ -181,7 +175,7 @@ class task_manager if (flock($schedlock, LOCK_SH | LOCK_NB) === FALSE) { // already locked : running ! - $ret['pid'] = fgets($schedlock, 512); + $pid = trim(fgets($schedlock, 512)); } else { @@ -190,6 +184,20 @@ class task_manager } fclose($schedlock); } + + $sql = "SELECT UNIX_TIMESTAMP()-UNIX_TIMESTAMP(schedqtime) AS qdelay, schedstatus AS status FROM sitepreff"; + $stmt = $this->appbox->get_connection()->prepare($sql); + $stmt->execute(); + $ret = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + + if($pid === NULL && $ret['status'] !== 'stopped') + { + // auto fix + $this->appbox->get_connection()->exec('UPDATE sitepreff SET schedstatus=\'stopped\''); + $ret['status'] = 'stopped'; + } + $ret['pid'] = $pid; return($ret); } diff --git a/lib/classes/task/period/archive.class.php b/lib/classes/task/period/archive.class.php index 4299bbebd1..bac2aa8b3c 100644 --- a/lib/classes/task/period/archive.class.php +++ b/lib/classes/task/period/archive.class.php @@ -16,7 +16,6 @@ */ class task_period_archive extends task_abstract { - /** * command line args specifics * @@ -96,19 +95,19 @@ class task_period_archive extends task_abstract $dom = new DOMDocument(); $dom->formatOutput = true; $dom->preserveWhiteSpace = false; - if ($dom->loadXML($oldxml)) + if($dom->loadXML($oldxml)) { $xmlchanged = false; // foreach($parm2 as $pname=>$pvalue) - foreach (array("str:base_id", "str:hotfolder", "str:period", "boo:move_archived", "boo:move_error", "boo:delfolder", 'boo:copy_spe', 'str:cold') as $pname) + foreach(array("str:base_id", "str:hotfolder", "str:period", "boo:move_archived", "boo:move_error", "boo:delfolder", 'boo:copy_spe', 'str:cold') as $pname) { $ptype = substr($pname, 0, 3); $pname = substr($pname, 4); $pvalue = $parm2[$pname]; - if ($ns = $dom->getElementsByTagName($pname)->item(0)) + if($ns = $dom->getElementsByTagName($pname)->item(0)) { // le champ existait dans le xml, on supprime son ancienne valeur (tout le contenu) - while (($n = $ns->firstChild)) + while(($n = $ns->firstChild)) $ns->removeChild($n); } else @@ -117,7 +116,7 @@ class task_period_archive extends task_abstract $ns = $dom->documentElement->appendChild($dom->createElement($pname)); } // on fixe sa valeur - switch ($ptype) + switch($ptype) { case "str": $ns->appendChild($dom->createTextNode($pvalue)); @@ -142,16 +141,16 @@ class task_period_archive extends task_abstract */ public function xml2graphic($xml, $form) { - if (($sxml = simplexml_load_string($xml))) // in fact XML IS always valid here... + if(($sxml = simplexml_load_string($xml))) // in fact XML IS always valid here... { // ... but we could check for safe values (ex. 0 < period < 3600) - if ((int) ($sxml->period) < 10) + if((int) ($sxml->period) < 10) $sxml->period = 10; - elseif ((int) ($sxml->period) > 300) + elseif((int) ($sxml->period) > 300) $sxml->period = 300; - if ((int) ($sxml->cold) < 5) + if((int) ($sxml->cold) < 5) $sxml->cold = 5; - elseif ((int) ($sxml->cold) > 3600) + elseif((int) ($sxml->cold) > 3600) $sxml->cold = 3600; ?>