diff --git a/bin/console b/bin/console
index dad1ccc91e..3916429b92 100755
--- a/bin/console
+++ b/bin/console
@@ -66,6 +66,7 @@ try {
$app->add(new module_console_taskrun('task:run'));
$app->add(new module_console_tasklist('task:list'));
+ $app->add(new module_console_taskState('task:state'));
$app->add(new module_console_schedulerState('scheduler:state'));
$app->add(new module_console_schedulerStop('scheduler:stop'));
$app->add(new module_console_schedulerStart('scheduler:start'));
diff --git a/lib/classes/module/console/schedulerState.class.php b/lib/classes/module/console/schedulerState.class.php
index 4cb8bd755d..fcd22e3cc3 100644
--- a/lib/classes/module/console/schedulerState.class.php
+++ b/lib/classes/module/console/schedulerState.class.php
@@ -24,6 +24,15 @@ use Symfony\Component\Console\Command\Command;
class module_console_schedulerState extends Command
{
+ const EXITCODE_SETUP_ERROR = 1;
+ const EXITCODE_STATE_UNKNOWN = 21;
+
+ private $stateToExitCode = array(
+ \task_manager::STATE_TOSTOP => 13,
+ \task_manager::STATE_STARTED => 10,
+ \task_manager::STATE_STOPPING => 12,
+ \task_manager::STATE_STOPPED => 11,
+ );
public function __construct($name = null)
{
@@ -35,9 +44,10 @@ class module_console_schedulerState extends Command
'short'
, NULL
, InputOption::VALUE_NONE
- , 'print short result, ie: stopped | started(12345) | stopping'
+ , 'print short result, ie: stopped() | started(12345) | tostop(12345) | stopping(12345)'
, NULL
);
+// $this->setHelp("");
return $this;
}
@@ -47,7 +57,7 @@ class module_console_schedulerState extends Command
if ( ! setup::is_installed()) {
$output->writeln($input->getOption('short') ? 'setup_error' : 'Phraseanet is not set up');
- return 1;
+ return self::EXITCODE_SETUP_ERROR;
}
require_once __DIR__ . '/../../../../lib/bootstrap.php';
@@ -55,37 +65,29 @@ class module_console_schedulerState extends Command
$appbox = appbox::get_instance(\bootstrap::getCore());
$task_manager = new task_manager($appbox);
+ $exitCode = 0;
$state = $task_manager->getSchedulerState();
- if ($state['status'] == 'started') {
- $output->writeln(sprintf(
- 'Scheduler is %s on pid %d'
- , $state['status']
- , $state['pid']
- ));
+ if ($input->getOption('short')) {
+ $output->writeln(sprintf('%s(%s)', $state['status'], $state['pid']));
} else {
- $output->writeln(sprintf('Scheduler is %s', $state['status']));
+ if ($state['pid'] != NULL) {
+ $output->writeln(sprintf(
+ 'Scheduler is %s on pid %d'
+ , $state['status']
+ , $state['pid']
+ ));
+ } else {
+ $output->writeln(sprintf('Scheduler is %s', $state['status']));
+ }
}
- switch ($state['status']) {
- case \task_manager::STATUS_SCHED_STARTED:
-
- return 10;
- break;
- case \task_manager::STATUS_SCHED_STOPPED:
-
- return 11;
- break;
- case \task_manager::STATUS_SCHED_STOPPING:
-
- return 12;
- break;
- case \task_manager::STATUS_SCHED_TOSTOP:
-
- return 13;
- break;
+ if (array_key_exists($state['status'], $this->stateToExitCode)) {
+ $exitCode = $this->stateToExitCode[$state['status']];
+ } else {
+ $exitCode = self::EXITCODE_STATE_UNKNOWN;
}
- return 1;
+ return $exitCode;
}
}
diff --git a/lib/classes/module/console/schedulerStop.class.php b/lib/classes/module/console/schedulerStop.class.php
index b721a276dc..c96f92b46b 100644
--- a/lib/classes/module/console/schedulerStop.class.php
+++ b/lib/classes/module/console/schedulerStop.class.php
@@ -47,7 +47,7 @@ class module_console_schedulerStop extends Command
try {
$appbox = appbox::get_instance(\bootstrap::getCore());
$task_manager = new task_manager($appbox);
- $task_manager->setSchedulerState(task_manager::STATUS_SCHED_TOSTOP);
+ $task_manager->setSchedulerState(task_manager::STATE_TOSTOP);
return 0;
} catch (\Exception $e) {
diff --git a/lib/classes/module/console/taskState.class.php b/lib/classes/module/console/taskState.class.php
new file mode 100644
index 0000000000..c8086b15fe
--- /dev/null
+++ b/lib/classes/module/console/taskState.class.php
@@ -0,0 +1,122 @@
+ 13,
+ \task_abstract::STATE_STARTED => 10,
+ \task_abstract::STATE_TOSTART => 14,
+ \task_abstract::STATE_TORESTART => 15,
+ \task_abstract::STATE_STOPPED => 11,
+ \task_abstract::STATE_TODELETE => 16
+ );
+
+ public function __construct($name = null)
+ {
+ parent::__construct($name);
+
+ $this->addArgument('task_id', InputArgument::REQUIRED, 'The task_id to test');
+
+ $this->setDescription('Get task state');
+
+ $this->addOption(
+ 'short'
+ , NULL
+ , InputOption::VALUE_NONE
+ , 'print short result, ie: stopped() | started(12345) | tostop(12345) | ...'
+ , NULL
+ );
+
+ return $this;
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output)
+ {
+ if ( ! setup::is_installed()) {
+ $output->writeln($input->getOption('short') ? 'setup_error' : 'Phraseanet is not set up');
+
+ return self::EXITCODE_SETUP_ERROR;
+ }
+ $task_id = (int) $input->getArgument('task_id');
+ if ($task_id <= 0 || strlen($task_id) !== strlen($input->getArgument('task_id'))) {
+ $output->writeln($input->getOption('short') ? 'bad_id' : 'Argument must be an ID');
+
+ return self::EXITCODE_BAD_ARGUMENT;
+ }
+
+ require_once __DIR__ . '/../../../../lib/bootstrap.php';
+
+ $appbox = appbox::get_instance(\bootstrap::getCore());
+ $task_manager = new task_manager($appbox);
+
+ $taskPID = $taskState = NULL;
+ $exitCode = 0;
+
+ $task = NULL;
+ try {
+ $task = $task_manager->getTask($task_id);
+ $taskPID = $task->getPID();
+ $taskState = $task->getState();
+ } catch (Exception_NotFound $e) {
+ $output->writeln($input->getOption('short') ? 'unknown_id' : $e->getMessage());
+
+ return self::EXITCODE_TASK_UNKNOWN;
+ } catch (Exception $e) {
+ $output->writeln($input->getOption('short') ? 'fatal_error' : $e->getMessage());
+
+ return self::EXITCODE_FATAL_ERROR;
+ }
+
+ if ($input->getOption('short')) {
+ $output->writeln(sprintf('%s(%s)', $taskState, $taskPID));
+ } else {
+ if ($taskPID !== NULL) {
+ $output->writeln(sprintf(
+ 'Task %d is %s on pid %d'
+ , $task_id
+ , $taskState
+ , $taskPID
+ ));
+ } else {
+ $output->writeln(sprintf('Task %d is %s', $task_id, $taskState));
+ }
+ }
+
+ if (array_key_exists($taskState, $this->stateToExitCode)) {
+ $exitCode = $this->stateToExitCode[$taskState];
+ } else {
+ $exitCode = self::EXITCODE_STATE_UNKNOWN;
+ }
+
+ return $exitCode;
+ }
+}
+
diff --git a/lib/classes/task/Scheduler.class.php b/lib/classes/task/Scheduler.class.php
index 45e63048d4..bb183d533c 100755
--- a/lib/classes/task/Scheduler.class.php
+++ b/lib/classes/task/Scheduler.class.php
@@ -128,8 +128,6 @@ class task_Scheduler
// set every 'auto-start' task to start
foreach ($task_manager->getTasks() as $task) {
if ($task->isActive()) {
- $tid = $task->getID();
-
if ( ! $task->getPID()) {
/* @var $task task_abstract */
$task->resetCrashCounter();
diff --git a/lib/classes/task/abstract.class.php b/lib/classes/task/abstract.class.php
index 70f7ba145c..a322259d53 100755
--- a/lib/classes/task/abstract.class.php
+++ b/lib/classes/task/abstract.class.php
@@ -93,6 +93,11 @@ abstract class task_abstract
"--help" => array("set" => false, "values" => array(), "usage" => " (no help available)")
);
+ /**
+ * get the state of the task (task_abstract::STATE_*)
+ *
+ * @return String
+ */
public function getState()
{
$conn = connection::getPDOConnection();
@@ -118,11 +123,22 @@ abstract class task_abstract
return false;
}
+ public function printInterfaceHTML()
+ {
+ return false;
+ }
+
public function getGraphicForm()
{
return false;
}
+ /**
+ * set the state of the task (task_abstract::STATE_*)
+ *
+ * @param String $status
+ * @throws Exception_InvalidArgument
+ */
public function setState($status)
{
$av_status = array(
@@ -179,6 +195,10 @@ abstract class task_abstract
public function setSettings($settings)
{
+ if (@simplexml_load_string($settings) === FALSE) {
+ throw new Exception_InvalidArgument('Bad XML');
+ }
+
$conn = connection::getPDOConnection();
$sql = 'UPDATE task2 SET settings = :settings WHERE task_id = :taskid';
@@ -301,6 +321,10 @@ abstract class task_abstract
public function setRunner($runner)
{
+ if ($runner != self::RUNNER_MANUAL && $runner != self::RUNNER_SCHEDULER) {
+ throw new Exception_InvalidArgument(sprintf('unknown runner `%s`', $runner));
+ }
+
$this->runner = $runner;
$conn = connection::getPDOConnection();
@@ -364,12 +388,10 @@ abstract class task_abstract
public function getPID()
{
$pid = NULL;
- $taskid = $this->getID();
- $registry = registry::get_instance();
- system_file::mkdir($lockdir = $registry->get('GV_RootPath') . 'tmp/locks/');
+ $lockfile = $this->getLockfilePath();
- if (($fd = fopen(($lockfile = ($lockdir . 'task_' . $taskid . '.lock')), 'a+')) != FALSE) {
+ if (($fd = fopen($lockfile, 'a+')) != FALSE) {
if (flock($fd, LOCK_EX | LOCK_NB) === FALSE) {
// already locked ? : task running
$pid = fgets($fd);
@@ -407,36 +429,50 @@ abstract class task_abstract
}
}
+ private function getLockfilePath()
+ {
+ $registry = registry::get_instance();
+ $lockdir = $registry->get('GV_RootPath') . 'tmp/locks/';
+
+ system_file::mkdir($lockdir);
+ $lockfile = ($lockdir . 'task_' . $this->getID() . '.lock');
+
+ return($lockfile);
+ }
+
+ private function lockTask()
+ {
+ $lockfile = $this->getLockfilePath();
+
+ $lockFD = fopen($lockfile, 'a+');
+
+ $locker = true;
+ if (flock($lockFD, LOCK_EX | LOCK_NB, $locker) === FALSE) {
+ $this->log("runtask::ERROR : task already running.");
+ fclose($lockFD);
+
+ throw new Exception('task already running.', self::ERR_ALREADY_RUNNING);
+ }
+
+ // here we run the task
+ ftruncate($lockFD, 0);
+ fwrite($lockFD, '' . getmypid());
+ fflush($lockFD);
+
+ // for windows : unlock then lock shared to allow OTHER processes to read the file
+ // too bad : no critical section nor atomicity
+ flock($lockFD, LOCK_UN);
+ flock($lockFD, LOCK_SH);
+
+ return $lockFD;
+ }
+
final public function run($runner, $input = null, $output = null)
{
$this->input = $input;
$this->output = $output;
- $taskid = $this->getID();
-
- $registry = registry::get_instance();
- system_file::mkdir($lockdir = $registry->get('GV_RootPath') . 'tmp/locks/');
- $locker = true;
- $lockfile = ($lockdir . 'task_' . $taskid . '.lock');
- $tasklock = fopen($lockfile, 'a+');
-
- if (flock($tasklock, LOCK_EX | LOCK_NB, $locker) === FALSE) {
- $this->log("runtask::ERROR : task already running.");
- fclose($tasklock);
-
- throw new Exception('task already running.', self::ERR_ALREADY_RUNNING);
- return;
- }
-
- // here we run the task
- ftruncate($tasklock, 0);
- fwrite($tasklock, '' . getmypid());
- fflush($tasklock);
-
- // for windows : unlock then lock shared to allow OTHER processes to read the file
- // too bad : no critical section nor atomicity
- flock($tasklock, LOCK_UN);
- flock($tasklock, LOCK_SH);
+ $lockFD = $this->lockTask();
$this->setRunner($runner);
$this->setState(self::STATE_STARTED);
@@ -450,9 +486,21 @@ abstract class task_abstract
}
// in any case, exception or not, the task is ending so unlock the pid file
- flock($tasklock, LOCK_UN | LOCK_NB);
- ftruncate($tasklock, 0);
- fclose($tasklock);
+ $this->unlockTask($lockFD);
+
+ // if something went wrong, report
+ if ($exception) {
+ throw($exception);
+ }
+ }
+
+ public function unlockTask($lockFD)
+ {
+ flock($lockFD, LOCK_UN | LOCK_NB);
+ ftruncate($lockFD, 0);
+ fclose($lockFD);
+
+ $lockfile = $this->getLockfilePath();
@unlink($lockfile);
switch ($this->getState()) {
@@ -463,11 +511,6 @@ abstract class task_abstract
$this->setState(self::STATE_STOPPED);
break;
}
-
- // if something went wrong, report
- if ($exception) {
- throw($exception);
- }
}
abstract protected function run2();
diff --git a/lib/classes/task/manager.class.php b/lib/classes/task/manager.class.php
index cc7a4f54df..2fe14a3d5b 100755
--- a/lib/classes/task/manager.class.php
+++ b/lib/classes/task/manager.class.php
@@ -16,10 +16,10 @@
*/
class task_manager
{
- const STATUS_SCHED_STOPPED = 'stopped';
- const STATUS_SCHED_STOPPING = 'stopping';
- const STATUS_SCHED_STARTED = 'started';
- const STATUS_SCHED_TOSTOP = 'tostop';
+ const STATE_STOPPED = 'stopped';
+ const STATE_STOPPING = 'stopping';
+ const STATE_STARTED = 'started';
+ const STATE_TOSTOP = 'tostop';
protected $appbox;
protected $tasks;
@@ -91,7 +91,7 @@ class task_manager
$tasks = $this->getTasks();
if ( ! isset($tasks[$task_id])) {
- throw new Exception_NotFound('Unknown task_id');
+ throw new Exception_NotFound('Unknown task_id ' . $task_id);
}
return $tasks[$task_id];
@@ -100,10 +100,10 @@ class task_manager
public function setSchedulerState($status)
{
$av_status = array(
- self::STATUS_SCHED_STARTED
- , self::STATUS_SCHED_STOPPED
- , self::STATUS_SCHED_STOPPING
- , self::STATUS_SCHED_TOSTOP
+ self::STATE_STARTED,
+ self::STATE_STOPPED,
+ self::STATE_STOPPING,
+ self::STATE_TOSTOP
);
if ( ! in_array($status, $av_status))
diff --git a/templates/web/admin/task.html b/templates/web/admin/task.html
index 3872d4ba27..72eb8c5552 100644
--- a/templates/web/admin/task.html
+++ b/templates/web/admin/task.html
@@ -418,7 +418,7 @@
{% endif %}
-