. * --------------------------------------------------------------------- */ namespace Glpi\Console\Task; if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } use CronTask; use Glpi\Console\AbstractCommand; use Glpi\Event; use QueryExpression; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Exception\InvalidArgumentException; class UnlockCommand extends AbstractCommand { protected function configure() { parent::configure(); $this->setName('glpi:task:unlock'); $this->setAliases(['task:unlock']); $this->setDescription(__('Unlock automatic tasks')); $this->addOption( 'all', 'a', InputOption::VALUE_NONE, __('Unlock all tasks') ); $this->addOption( 'cycle', 'c', InputOption::VALUE_OPTIONAL, __('Execution time (in cycles) from which the task is considered as stuck (delay = task frequency * cycle)'), null // Has to be null to detect lack of definition of both 'cycle' and 'delay' to set default delay (see self::validateInput()) ); $this->addOption( 'delay', 'd', InputOption::VALUE_OPTIONAL, __('Execution time (in seconds) from which the task is considered as stuck (default: 1800)'), null // Has to be null to detect lack of definition of both 'cycle' and 'delay' to set default delay (see self::validateInput()) ); $this->addOption( 'task', 't', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, __('Itemtype::name of task to unlock (e.g: "MailCollector::mailgate")') ); } protected function execute(InputInterface $input, OutputInterface $output) { $this->validateInput($input); $all = $input->getOption('all'); $cycle = $input->getOption('cycle'); $delay = $input->getOption('delay'); $tasks = $input->getOption('task'); if (null !== $cycle) { $delay = $cycle . ' * ' . $this->db->quoteName('frequency'); } $task_iterator = $this->db->request( [ 'SELECT' => [ 'id', new QueryExpression( 'CONCAT(' . $this->db->quoteName('itemtype') . ', ' . $this->db->quoteValue('::') . ', ' . $this->db->quoteName('name') . ') AS ' . $this->db->quoteName('task') ) ], 'FROM' => CronTask::getTable(), 'WHERE' => [ 'state' => CronTask::STATE_RUNNING, new QueryExpression( 'UNIX_TIMESTAMP(' . $this->db->quoteName('lastrun') . ') + ' . $delay . ' < UNIX_TIMESTAMP(NOW())' ) ] ] ); $crontask = new CronTask(); $unlocked_count = 0; foreach ($task_iterator as $task) { if (!$all && !in_array($task['task'], $tasks)) { $output->writeln( '' . sprintf(__('Task "%s" is still running but not in the whitelist.'), $task['task']) . '', OutputInterface::VERBOSITY_VERBOSE ); continue; } $input = [ 'id' => $task['id'], 'state' => CronTask::STATE_WAITING, ]; if ($crontask->update($input)) { $unlocked_count++; $message = sprintf(__('Task "%s" unlocked.'), $task['task']); $output->writeln('' . $message . ''); Event::log($task['id'], 'CronTask', 5, 'Configuration', $message); } else { $output->writeln( sprintf( '' . __('An error occurs while trying to unlock "%s" task.') . '', $task['task'] ), OutputInterface::VERBOSITY_QUIET ); } } $output->writeln( '' .sprintf(__('Number of tasks unlocked: %d.'), $unlocked_count) . '' ); return 0; // Success } /** * Validate command input. * * @param InputInterface $input * * @return void * * @throws InvalidArgumentException */ private function validateInput(InputInterface $input) { $all = $input->getOption('all'); $cycle = $input->getOption('cycle'); $delay = $input->getOption('delay'); $tasks = $input->getOption('task'); if (null !== $cycle && null !== $delay) { throw new InvalidArgumentException( __('Option --cycle is not compatible with option --delay.') ); } if (null !== $cycle && !preg_match('/^\d+$/', $cycle)) { throw new InvalidArgumentException( __('Option --cycle has to be an integer.') ); } if (null !== $delay && !preg_match('/^\d+$/', $delay)) { throw new InvalidArgumentException( __('Option --delay has to be an integer.') ); } if (null === $cycle && null === $delay) { $input->setOption('delay', 1800); // Default delay } if ($all && !empty($tasks)) { throw new InvalidArgumentException( __('Option --all is not compatible with option --task.') ); } if (!$all && empty($tasks)) { throw new InvalidArgumentException( __('You have to specify which tasks to unlock using --all or --task options.') ); } } }