ultimate_cron.module in Ultimate Cron 8
Same filename and directory in other branches
@todo Add filter on overview page. @todo Add log view (with graph). @todo Make proper markup for overview page. @todo Refactor drush stuff, too many intimate relations with Background Process @todo Refactor Cron % offset stuff. Too mixed up and ungrokable code-wise and 'delta' is not consistent.
hook_cron_alter(&$hooks) hook_cron_schedule_alter(&$hooks) hook_cron_pre_execute($name, $hook) hook_cron_pre_execute_FUNCTION($hook) hook_cron_post_execute($name, $hook) hook_cron_post_execute_FUNCTION($hook)
File
ultimate_cron.moduleView source
<?php
/**
* @file
*
* @todo Add filter on overview page.
* @todo Add log view (with graph).
* @todo Make proper markup for overview page.
* @todo Refactor drush stuff, too many intimate relations with Background Process
* @todo Refactor Cron % offset stuff. Too mixed up and ungrokable code-wise and 'delta' is not consistent.
*
* hook_cron_alter(&$hooks)
* hook_cron_schedule_alter(&$hooks)
* hook_cron_pre_execute($name, $hook)
* hook_cron_pre_execute_FUNCTION($hook)
* hook_cron_post_execute($name, $hook)
* hook_cron_post_execute_FUNCTION($hook)
*/
/**
* Maximum number of simultaneous connections.
*/
define('ULTIMATE_CRON_SIMULTANEOUS_CONNECTIONS', 40);
/**
* Default handle prefix for background processes.
*/
define('ULTIMATE_CRON_HANDLE_PREFIX', 'uc:');
/**
* Default rule.
*/
define('ULTIMATE_CRON_RULE', '*/10+@ * * * *');
/**
* Default rule for easy hook "hourly".
*/
define('ULTIMATE_CRON_HOURLY_RULE', '0 * * * *');
/**
* Default rule for easy hook "daily".
*/
define('ULTIMATE_CRON_DAILY_RULE', '0 0 * * *');
/**
* Default rule for easy hook "weekly".
*/
define('ULTIMATE_CRON_WEEKLY_RULE', '0 0 * * 1');
/**
* Default rule for easy hook "monthly".
*/
define('ULTIMATE_CRON_MONTHLY_RULE', '0 0 1 * *');
/**
* Default rule for easy hook "yearly".
*/
define('ULTIMATE_CRON_YEARLY_RULE', '0 0 1 1 *');
/**
* Default max execution time for Ultimate Cron.
*/
define('ULTIMATE_CRON_MAX_EXECUTION_TIME', 86400);
/**
* Default catch up time for Ultimate Cron.
*/
define('ULTIMATE_CRON_CATCH_UP', 300);
/**
* Default lease time for Ultimate Cron queues.
*/
define('ULTIMATE_CRON_QUEUE_LEASE_TIME', 30);
/**
* Default clean up time for log entries (30 days).
*/
define('ULTIMATE_CRON_CLEANUP_LOG', 86400 * 30);
/**
* Default setting for poorman.
*/
define('ULTIMATE_CRON_POORMAN', TRUE);
/**
* Default queue polling latency.
*/
define('ULTIMATE_CRON_QUEUE_POLLING_LATENCY', '');
/**
* Time in seconds to spend on launcing cron jobs.
*/
define('ULTIMATE_CRON_LAUNCH_WINDOW', 5);
/**
* Time in seconds to spend on launcing cron jobs.
*/
define('ULTIMATE_CRON_SERVICE_GROUP', 'default');
// ---------- HOOKS ----------
module_load_include('nagios.inc', 'ultimate_cron');
/**
* Implements hook_help().
*/
function ultimate_cron_help($path, $arg) {
switch ($path) {
case 'admin/help#ultimate_cron':
// Return a line-break version of the module README
return '<pre>' . file_get_contents(dirname(__FILE__) . '/README.txt') . '</pre>';
case 'admin/build/cron':
return '<p>' . t('Here you can see the crontab settings for each job available') . '</p>';
case 'admin/build/cron/settings':
return '<p>' . t('Here you can change the crontab settings for each job available') . '</p>';
}
}
/**
* Implements hook_menu().
*/
function ultimate_cron_menu() {
$items = array();
$items['admin/config/system/cron/settings'] = array(
'title' => 'Settings',
'description' => 'Cron settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'ultimate_cron_settings_form',
),
'access arguments' => array(
'administer ultimate cron',
),
'type' => MENU_LOCAL_TASK,
'file' => 'ultimate_cron.admin.inc',
);
$items['admin/config/system/cron/settings/%'] = array(
'title' => 'Settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'ultimate_cron_function_settings_form',
5,
),
'access arguments' => array(
'administer ultimate cron',
),
'weight' => 0,
'file' => 'ultimate_cron.admin.inc',
);
$weight = 0;
foreach (array(
'error' => 'Errors',
'warning' => 'Warnings',
'info' => 'Info',
'success' => 'Success',
'running' => 'Running',
) as $status => $title) {
$items['admin/config/system/cron/overview/' . $status] = array(
'title' => $title,
'description' => 'View and manage cron table',
'page callback' => 'ultimate_cron_view_page',
'page arguments' => array(
5,
),
'access arguments' => array(
'administer ultimate cron',
),
'module' => 'ultimate_cron',
'file' => 'ultimate_cron.admin.inc',
'weight' => $weight++,
'type' => MENU_LOCAL_TASK,
);
}
$items['admin/config/system/cron/overview/all'] = array(
'title' => 'All',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => $weight++,
);
$items['admin/reports/cron'] = array(
'title' => 'Cron logs',
'description' => 'View logs for all cron jobs.',
'page callback' => 'ultimate_cron_view_page',
'access arguments' => array(
'administer ultimate cron',
),
'file' => 'ultimate_cron.admin.inc',
);
$items['admin/reports/cron/%'] = array(
'title' => 'Cron log',
'description' => 'View log for specific function.',
'page callback' => 'ultimate_cron_function_log_page',
'page arguments' => array(
3,
),
'access arguments' => array(
'administer ultimate cron',
),
'file' => 'ultimate_cron.admin.inc',
);
$items['admin/ultimate-cron/service/start/%'] = array(
'type' => MENU_CALLBACK,
'title' => 'Run cron job',
'description' => 'Run cron job',
'page callback' => 'ultimate_cron_service_start',
'page arguments' => array(
4,
),
'access arguments' => array(
'administer ultimate cron',
),
'file' => 'ultimate_cron.admin.inc',
);
$items['admin/ultimate-cron/service/enable/%'] = array(
'type' => MENU_CALLBACK,
'title' => 'Enable cron job',
'description' => 'Enable cron job',
'page callback' => 'ultimate_cron_service_enable',
'page arguments' => array(
4,
TRUE,
),
'access arguments' => array(
'administer ultimate cron',
),
'file' => 'ultimate_cron.admin.inc',
);
$items['admin/ultimate-cron/service/disable/%'] = array(
'type' => MENU_CALLBACK,
'title' => 'Disable cron job',
'description' => 'Disable cron job',
'page callback' => 'ultimate_cron_service_enable',
'page arguments' => array(
4,
FALSE,
),
'access arguments' => array(
'administer ultimate cron',
),
'file' => 'ultimate_cron.admin.inc',
);
$items['admin/ultimate-cron/service/process-status'] = array(
'type' => MENU_CALLBACK,
'title' => 'Cron job process status',
'description' => 'Cron job process status',
'page callback' => 'ultimate_cron_service_process_status',
'access arguments' => array(
'administer ultimate cron',
),
'file' => 'ultimate_cron.admin.inc',
);
return $items;
}
/**
* Implements hook_cron_queue_info().
* Used for code injection in order to hijack cron runs.
*/
function ultimate_cron_cron_queue_info() {
static $processed = FALSE;
if (!$processed) {
$processed = TRUE;
if (basename($_SERVER['PHP_SELF']) == 'cron.php') {
ultimate_cron_cron_run(FALSE);
exit;
}
}
return array();
}
/**
* Implements hook_permission().
*/
function ultimate_cron_permission() {
return array(
'administer ultimate cron' => array(
'title' => t('Administer Ultimate Cron'),
'description' => t('Lets you configure everything in Ultimate Cron'),
),
);
}
/**
* The cron handler takes over the normal Drupal cron handler
* and runs the normal hook_cron() plus the hook_cronapi().
*
* @param boolean $return
* return to caller if TRUE, otherwise exit().
*/
function ultimate_cron_cron_run($return = FALSE) {
if (state()
->get('system.install_task') != 'done') {
return;
}
// Be other cron module friendly
if (_ultimate_cron_incompatible_modules()) {
return;
}
// If run from core cron through CLI then don't do anything (drush core-cron)
if (!$return && drupal_is_cli()) {
return;
}
// Acquire lock
if (!lock()
->acquire('cron', 240.0)) {
drupal_set_message(t('Ultimate Cron launcher already running'), 'error');
return;
}
$msc = variable_get('ultimate_cron_simultaneous_connections', ULTIMATE_CRON_SIMULTANEOUS_CONNECTIONS);
// Get list of cron hooks.
$hooks = ultimate_cron_get_hooks();
// Get schedule.
$schedule = ultimate_cron_get_schedule($hooks);
drupal_set_message(t('%jobs jobs scheduled for launch', array(
'%jobs' => count($schedule),
)));
// Start the jobs. Keep launching jobs until X seconds into the request.
@set_time_limit(120);
$time = time();
$expire = $time + variable_get('ultimate_cron_launch_window', ULTIMATE_CRON_LAUNCH_WINDOW);
$running = array();
$launched = 0;
$handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
// Try to launch jobs within the given time frame
while (!empty($schedule) && time() < $expire) {
$running_processes = db_select('background_process', 'bp')
->fields('bp', array(
'handle',
))
->condition('bp.handle', $handle_prefix . '%', 'LIKE')
->countQuery()
->execute()
->fetchField();
// Launch jobs.
reset($schedule);
while ((list($name, $hook) = each($schedule)) && time() < $expire) {
// Congestion protection
if (empty($hook['override_congestion_protection']) && $running_processes >= $msc) {
continue;
}
if (empty($hook['force_run']) && !ultimate_cron_hook_should_run($hook)) {
unset($schedule[$name]);
continue;
}
$result = ultimate_cron_run_hook($name, $hook);
// Handle errors.
if ($result) {
$handle = $handle_prefix . $name;
$running[$handle] = $result;
unset($schedule[$name]);
$launched++;
$running_processes++;
}
else {
if ($result === FALSE) {
// Could not get lock, skip job.
unset($schedule[$name]);
}
else {
// Failed to start, retry next time.
watchdog('ultimate_cron', "Error starting {$name}", array(), WATCHDOG_WARNING);
}
}
}
// Jobs running ... check for start
if ($running) {
$result = db_query("SELECT p.name FROM {progress} p WHERE p.name IN (:running)", array(
':running' => array_keys($running),
));
while ($handle = $result
->fetchObject()) {
fclose($running[$handle->name]);
unset($running[$handle->name]);
}
}
sleep(1);
}
// Close all jobs left
if ($running) {
foreach (array_keys($running) as $handle) {
fclose($running[$handle]);
unset($running[$handle]);
}
}
// Update drupals cron timestamp, but don't clear the cache for all variables!
$name = 'cron_last';
$value = time();
global $conf;
db_merge('variable')
->key(array(
'name' => $name,
))
->fields(array(
'value' => serialize($value),
))
->execute();
$conf[$name] = $value;
drupal_set_message(t('%jobs jobs launched', array(
'%jobs' => $launched,
)));
if (count($schedule)) {
drupal_set_message(t('%jobs jobs failed to launch within %seconds seconds', array(
'%jobs' => count($schedule),
'%seconds' => variable_get('ultimate_cron_launch_window', ULTIMATE_CRON_LAUNCH_WINDOW),
)), empty($schedule) ? 'status' : 'error');
watchdog('ultimate_cron', '%jobs jobs failed to launch within %seconds seconds', array(
'%jobs' => count($schedule),
'%seconds' => variable_get('ultimate_cron_launch_window', ULTIMATE_CRON_LAUNCH_WINDOW),
), WATCHDOG_WARNING);
}
// Release the lock
lock()
->release('cron');
// And we're done ...
if ($return) {
return empty($schedule);
}
else {
exit;
}
}
/**
* Implements hook_cronapi().
*/
function ultimate_cron_cronapi($op, $job = NULL, $hook = NULL) {
// Grab the defined cron queues.
static $queues = NULL;
if (!isset($queues)) {
$queues = module_invoke_all('cron_queue_info');
drupal_alter('cron_queue_info', $queues);
}
switch ($op) {
case 'list':
$jobs['ultimate_cron_cleanup_log'] = t('Cleanup log entries');
foreach ($queues as $queue_name => $info) {
$jobs['cron_queue_' . $queue_name] = t('Queue: %name', array(
'%name' => $queue_name,
));
}
return $jobs;
case 'rule':
$queue_name = preg_replace('/^cron_queue_/', '', $job);
if ($queue_name === $job) {
return;
}
return '* * * * *';
case 'execute':
$queue_name = preg_replace('/^cron_queue_/', '', $job);
if ($queue_name === $job) {
return;
}
$polling_latency = variable_get('ultimate_cron_queue_polling_latency', ULTIMATE_CRON_QUEUE_POLLING_LATENCY);
$info = $queues[$queue_name];
$function = $info['worker callback'];
$end = time() + (isset($info['time']) ? $info['time'] : 15);
$queue = queue($queue_name);
$items = 0;
while (time() < $end) {
$lease_time = isset($hook['settings']['queue_lease_time']) ? $hook['settings']['queue_lease_time'] : variable_get('ultimate_cron_queue_lease_time', ULTIMATE_CRON_QUEUE_LEASE_TIME);
$item = $queue
->claimItem($lease_time);
if (!$item) {
if (is_numeric($polling_latency)) {
usleep($polling_latency * 1000);
continue;
}
else {
break;
}
}
try {
$function($item->data);
$queue
->deleteItem($item);
$items++;
} catch (Exception $e) {
// Just continue ...
watchdog('ultimate_cron', "Queue item %item_id failed with message %message", array(
'%item_id' => $item->item_id,
'%message' => $e
->getMessage(),
), WATCHDOG_ERROR);
}
}
drupal_set_message(t('Processed @items items', array(
'@items' => $items,
)));
if (is_numeric($polling_latency)) {
$settings = ultimate_cron_get_settings($job);
$hook['settings'] = $settings + $hook['settings'];
if (ultimate_cron_hook_should_run($hook)) {
background_process_keepalive();
}
}
return;
}
}
/**
* Implements hook_progress_message_alter().
*/
function ultimate_cron_progress_message_alter(&$message) {
$pseudo_process = new stdClass();
$pseudo_process->handle = $message->data->progress->name;
$pseudo_process->start_stamp = $message->data->progress->start_stamp;
$pseudo_process->progress = $message->data->progress->progress;
$pseudo_process->exec_status = 2;
$message->data->background_process = $pseudo_process;
return ultimate_cron_background_process_message_alter($message);
}
/**
* Implements hook_background_process_message_alter().
*/
function ultimate_cron_background_process_message_alter(&$message) {
$handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
// Only alter Ultimate Cron background process messages
if (substr($message->data->background_process->handle, 0, 3) !== $handle_prefix) {
return;
}
$message->data->ultimate_cron['start_stamp'] = format_date((int) $message->data->background_process->start_stamp, 'custom', 'Y-m-d H:i:s');
if ($message->data->background_process->progress >= 0) {
$message->data->ultimate_cron['progress'] = gmdate('H:i:s', (int) (microtime(TRUE) - $message->data->background_process->start_stamp)) . sprintf(" (%3d%%)", $message->data->background_process->progress * 100);
}
$message->data->ultimate_cron['unlockURL'] = url('background-process/unlock/' . $message->data->background_process->handle, array(
'query' => array(
'destination' => 'admin/config/system/cron',
),
));
}
/**
* Implements hook_theme_registry_alter().
*/
function ultimate_cron_theme_registry_alter(&$theme_registry) {
$theme_registry['page']['theme paths'][] = drupal_get_path('module', 'ultimate_cron') . '/templates';
}
/**
* Implements hook_watchdog().
*/
function ultimate_cron_watchdog($log = array()) {
$record =& drupal_static('ultimate_cron_record', FALSE);
if ($record) {
$log['variables'] = is_array($log['variables']) ? $log['variables'] : array();
ultimate_cron_record_log(t($log['message'], $log['variables']), FALSE, $log['severity']);
}
}
/**
* Implements hook_init().
*/
function ultimate_cron_init() {
// No need for hocus pocus and poorman until site is installed.
if (state()
->get('system.install_task') != 'done') {
return;
}
$name = 'cron_last';
if ($value = db_query("SELECT value FROM {variable} WHERE name = :name", array(
':name' => $name,
))
->fetchField()) {
$value = unserialize($value);
}
global $conf;
$conf['cron_last'] = $value;
ultimate_cron_trigger_poorman();
}
/**
* Launch poorman cron if it's time to do so.
*/
function ultimate_cron_trigger_poorman() {
// Launch poormans cron if applicable
if (variable_get('ultimate_cron_poorman', ULTIMATE_CRON_POORMAN) && !_ultimate_cron_incompatible_modules()) {
$last = variable_get('cron_last', 0);
$last = floor($last / 60) * 60;
$time = time();
$time = floor($time / 60) * 60;
// Don't attempt, if already run within last minute
if ($last < $time) {
ultimate_cron_launch_poorman();
}
}
}
/**
* Launch the poormans cron background process.
*/
function ultimate_cron_launch_poorman() {
$handle = 'ultimate_cron_poorman';
if ($process = background_process_get_process($handle)) {
if ($process->start + 120 < time()) {
// Must have timed out or something ... remove it!
if (background_process_remove_process($handle, $process->start)) {
$process = NULL;
}
}
}
if (!$process) {
$process = new BackgroundProcess($handle);
// Because anyone can launch poormans cron, run it as anonymous
$process->uid = 0;
$process->service_host = 'ultimate_cron_poorman';
$result = $process
->start('_ultimate_cron_poorman');
}
}
/**
* Implements hook_background_process_shutdown().
*
* Shutdown handler for cronjobs.
*/
function ultimate_cron_background_process_shutdown($process, $shutdown_msg = NULL) {
$args = func_get_args();
$record = drupal_static('ultimate_cron_record', FALSE);
$handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
$name = preg_replace('/^' . $handle_prefix . '/', '', $process->handle);
if (!empty($name) && $name != $process->handle) {
static $has_run = array();
if (!empty($has_run[$name])) {
return;
}
$has_run[$name] = TRUE;
// Record end time
$end = microtime(TRUE);
if ($record) {
// Get drupal messages
$messages = drupal_get_messages(NULL, TRUE);
$messages['status'] = empty($messages['status']) ? array() : $messages['status'];
$messages['warning'] = empty($messages['warning']) ? array() : $messages['warning'];
$messages['error'] = empty($messages['error']) ? array() : $messages['error'];
foreach ($messages['status'] as $message) {
ultimate_cron_record_log($message);
}
foreach ($messages['warning'] as $message) {
ultimate_cron_record_log($message, FALSE, WATCHDOG_WARNING);
}
foreach ($messages['error'] as $message) {
ultimate_cron_record_log($message, FALSE, WATCHDOG_ERROR);
}
// Get error messages
$error = error_get_last();
if ($error) {
$message = $error['message'] . ' (line ' . $error['line'] . ' of ' . $error['file'] . ').' . "\n";
$severity = WATCHDOG_INFO;
if ($error['type'] && (E_NOTICE || E_USER_NOTICE || E_USER_WARNING)) {
$severity = WATCHDOG_NOTICE;
}
if ($error['type'] && (E_WARNING || E_CORE_WARNING || E_USER_WARNING)) {
$severity = WATCHDOG_WARNING;
}
if ($error['type'] && (E_ERROR || E_CORE_ERROR || E_USER_ERROR || E_RECOVERABLE_ERROR)) {
$severity = WATCHDOG_ERROR;
}
ultimate_cron_record_log($message, FALSE, $severity);
}
}
if ($shutdown_msg) {
ultimate_cron_record_log($shutdown_msg, FALSE, WATCHDOG_ERROR);
}
$log = drupal_static('ultimate_cron_record_log', array(
'msg' => '',
'severity' => -1,
));
$severity = $log['severity'];
$msg = $log['msg'];
$result = $severity < 0 || $severity >= WATCHDOG_INFO ? 1 : 0;
// log results here ...
$object = (object) array(
'name' => $name,
'start_stamp' => $process->start,
'end_stamp' => $end,
'service_host' => $process->service_host,
'exec_status' => $result,
'severity' => $severity,
'msg' => trim($msg),
);
$old_db = db_set_active('background_process');
drupal_write_record('ultimate_cron_log', $object);
db_set_active($old_db);
$object->formatted['start_stamp'] = format_date((int) $object->start_stamp, 'custom', 'Y-m-d H:i:s');
$object->formatted['end_stamp'] = format_date((int) $object->end_stamp, 'custom', 'Y-m-d H:i:s');
$object->formatted['duration'] = gmdate('H:i:s', (int) ($object->end_stamp - $object->start_stamp));
$object->formatted['severity'] = $object->severity < 0 ? 'success' : ($object->severity >= WATCHDOG_NOTICE ? 'info' : ($object->severity >= WATCHDOG_WARNING ? 'warning' : 'error'));
$object->formatted['executeURL'] = url('admin/ultimate-cron/service/start/' . $object->name, array(
'query' => array(
'destination' => 'admin/config/system/cron',
),
));
$object->formatted['msg'] = strip_tags(html_entity_decode($object->msg, ENT_QUOTES));
if (module_exists('nodejs')) {
$message = (object) array(
'channel' => 'ultimate_cron',
'data' => (object) array(
'action' => 'log',
'log' => $object,
),
'callback' => 'nodejsUltimateCron',
);
nodejs_send_content_channel_message($message);
}
}
}
// ---------- FIXUPS FOR CORE ----------
/**
* Implements hook_menu_alter().
*
* Steal the run-cron, so when you "run cron manually" from the status-reports
* page the ultimate_cron cron handler is run.
*/
function ultimate_cron_menu_alter(&$items) {
if (_ultimate_cron_incompatible_modules()) {
return;
}
$items['admin/config/system/cron'] = array(
'title' => 'Cron',
'description' => 'View and manage cron table',
'page callback' => 'ultimate_cron_view_page',
'access arguments' => array(
'administer ultimate cron',
),
'module' => 'ultimate_cron',
'file' => 'ultimate_cron.admin.inc',
);
$items['admin/config/system/cron/overview'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$steal =& $items['admin/reports/status/run-cron'];
$steal['page callback'] = 'ultimate_cron_run_cron';
$steal['page arguments'] = array();
$steal['module'] = 'ultimate_cron';
$steal['file'] = 'ultimate_cron.admin.inc';
}
/**
* Implements hook_cron_alter().
* Add better description to core modules.
*/
function ultimate_cron_cron_alter(&$hooks) {
$update['dblog_cron']['description'] = t('Remove expired log messages and flood control events');
$update['field_cron']['description'] = t('Purges deleted Field API data');
$update['filter_cron']['description'] = t('Expire outdated filter cache entries');
$update['node_cron']['description'] = t('Mark old nodes as read');
$update['search_cron']['description'] = t('Update indexes');
$update['system_cron']['description'] = t('Cleanup (batch, flood, temp-files, etc.)');
$update['aggregator_cron']['description'] = t('Refresh feeds');
$update['openid_cron']['description'] = t('Remove expired nonces from the database');
$update['ping_cron']['description'] = t('Notify remote sites');
$update['poll_cron']['description'] = t('Close expired polls');
$update['statistics_cron']['description'] = t('Reset counts and clean up');
$update['trigger_cron']['description'] = t('Run actions for cron triggers');
$update['tracker_cron']['description'] = t('Update tracker index');
$update['update_cron']['description'] = t('Check system for updates');
$update['ultimate_cron_cleanup_log']['configure'] = 'admin/config/system/cron/settings';
$update['dblog_cron']['configure'] = 'admin/config/development/logging';
foreach ($update as $name => $data) {
if (isset($hooks[$name])) {
foreach ($data as $key => $value) {
$hooks[$name][$key] = $value;
}
}
}
}
// ---------- HELPER FUNCTIONS ----------
/**
* Set current hook being processed
*
* @param $hook
* @return
* Current hook
*/
function ultimate_cron_set_current_hook($hook = NULL) {
static $current_hook;
if ($hook) {
$current_hook = $hook;
}
return $current_hook;
}
/**
* Get current hook being processed
*
* @return
* Current hook
*/
function ultimate_cron_get_current_hook() {
return ultimate_cron_set_current_hook();
}
/**
* The actual poorman function
* @return type
*/
function _ultimate_cron_poorman() {
if (!variable_get('ultimate_cron_poorman', ULTIMATE_CRON_POORMAN) || _ultimate_cron_incompatible_modules()) {
return;
}
// Restart when done
background_process_keepalive();
// Derive current minute
$time = time();
$time = floor($time / 60) * 60;
// Run the cron
ultimate_cron_cron_run(TRUE);
// Wait until end of "current" minute
$wait = $time + 60 - time();
if ($wait > 0 && $wait <= 60) {
sleep($wait);
}
}
/**
* Clean up log entries.
*/
function ultimate_cron_cleanup_log() {
do {
$result = db_query_range("SELECT lid FROM {ultimate_cron_log} WHERE start_stamp < :start", 0, 1000, array(
':start' => time() - variable_get('ultimate_cron_cleanup_log', ULTIMATE_CRON_CLEANUP_LOG),
));
$lids = array();
while ($row = $result
->fetchObject()) {
$lids[] = $row->lid;
}
if (!empty($lids)) {
db_query("DELETE FROM {ultimate_cron_log} WHERE lid IN (:lids)", array(
':lids' => $lids,
));
}
} while (!empty($lids));
}
/**
* Run a cron hook.
* Launches the cron job in a background process
*
* @param $name
* @param $hook
* @return mixed
* Connections file handle on success.
*/
function ultimate_cron_run_hook($name, $hook) {
// Run the job in background
$result = NULL;
$handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
$handle = $handle_prefix . $name;
$process = new BackgroundProcess($handle);
// Always run cron job as anonymous user
$process->uid = 0;
// Determine service group
$process->service_group = empty($hook['settings']['service_group']) ? variable_get('ultimate_cron_service_group', ULTIMATE_CRON_SERVICE_GROUP) : $hook['settings']['service_group'];
$hook['timestamp'] = time();
unset($hook['log']['msg']);
$result = $process
->start('_ultimate_cron_run_hook', array(
$name,
$hook,
));
return $result ? $process->connection : $result;
}
function ultimate_cron_run_hook_cli($name, $hook) {
// Run the job in background
$result = NULL;
$handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
$handle = $handle_prefix . $name;
$process = new BackgroundProcess($handle);
// Always run cron job as anonymous user
$process->uid = 0;
$hook['timestamp'] = time();
if ($process
->lock()) {
try {
$process_obj = background_process_get_process($handle);
$process->start = $process_obj->start;
if (function_exists('background_process_update_status')) {
background_process_update_status($handle, BACKGROUND_PROCESS_STATUS_RUNNING);
}
else {
db_update('background_process')
->fields(array(
'exec_status' => BACKGROUND_PROCESS_STATUS_RUNNING,
))
->condition('handle', $handle)
->execute();
}
$old_handle = background_process_current_handle();
background_process_current_handle($handle);
_ultimate_cron_run_hook($name, $hook);
module_invoke_all('background_process_shutdown', $process);
background_process_current_handle($old_handle);
background_process_remove_process($handle, $process->start);
return TRUE;
} catch (Exception $e) {
module_invoke_all('background_process_shutdown', $process, (string) $e);
return NULL;
}
}
return FALSE;
}
/**
* This is the function that is launched into a background process.
* It runs the cron job and does housekeeping, pre/post execute hooks, etc.
*
* @param $module
* Module containing function.
* @param $name
* Function to call.
* @return boolean
* TRUE on success, FALSE on failure.
*/
function _ultimate_cron_run_hook($name, $hook) {
@set_time_limit(variable_get('ultimate_cron_max_execution_time', ULTIMATE_CRON_MAX_EXECUTION_TIME));
drupal_save_session(FALSE);
// Load current process
$process = background_process_get_process(background_process_current_handle());
$record =& drupal_static('ultimate_cron_record', FALSE);
$record = TRUE;
ultimate_cron_record_log(NULL, TRUE);
// Load log if not present
if (!isset($hook['log'])) {
$hook['log'] = ultimate_cron_get_log($name);
}
$time = time();
if (empty($hook['skip_catch_up']) && !ultimate_cron_hook_should_run($hook)) {
// Hook started too late!
watchdog('ultimate_cron', '%function skipped. Invoked at %invoke, but did not start until %start', array(
'%function' => $name,
'%invoke' => format_date($hook['timestamp'], 'custom', 'Y-m-d H:i:s'),
'%start' => format_date($time, 'custom', 'Y-m-d H:i:s'),
), WATCHDOG_ERROR);
ultimate_cron_background_process_shutdown($process, NULL);
return FALSE;
}
// Let other modules do stuff before execution, if they need to.
module_invoke_all('cron_pre_execute', $name, $hook);
module_invoke_all('cron_pre_execute_' . $name, $hook);
if (!empty($hook['file'])) {
include_once $hook['file'];
}
$callback = $hook['callback'];
ultimate_cron_set_current_hook($hook);
if (is_callable($callback)) {
call_user_func($callback);
}
else {
module_invoke($hook['module'], 'cronapi', 'execute', $name, $hook);
}
ultimate_cron_background_process_shutdown($process, NULL);
// Let other modules do stuff before execution, if they need to.
module_invoke_all('cron_post_execute', $name, $hook);
module_invoke_all('cron_post_execute_' . $name, $hook);
return TRUE;
}
/**
* Get a list of functions that should be run now.
*
* @param $hooks
* Array of cron hooks to check.
* @return array
* Functions to run now.
*/
function ultimate_cron_get_schedule($hooks) {
// Create list of scheduled functions
$schedule = array();
foreach ($hooks as $name => &$hook) {
ultimate_cron_load_hook_data($hook);
// Store last run in hook for sorting purposes
$last_run = isset($hook['log']['start']) ? $hook['log']['start'] : 0;
$hook['last_run'] = $last_run;
if (ultimate_cron_hook_should_run($hook)) {
$schedule[$name] = $hook;
}
}
// Sort by last run time
uasort($schedule, '_ultimate_cron_sort_schedule');
// Allow other to manipulate the schedule
drupal_alter('cron_schedule', $schedule);
return $schedule;
}
/**
* Populate hook array with settings and log data
*
* @param type $hook
*/
function ultimate_cron_load_hook_data(&$hook) {
// Get settings
$hook['settings'] = ultimate_cron_get_settings($hook['function']) + $hook['settings'];
// Get log, used for checking last start time
$hook['log'] = ultimate_cron_get_log($hook['function']);
}
/**
* Check if a hook should be run now.
*
* @param array $hook
* @return boolean
*/
function ultimate_cron_hook_should_run($hook) {
// Is it enabled?
if (empty($hook['settings']['enabled'])) {
return FALSE;
}
$last_run = isset($hook['log']['start']) ? $hook['log']['start'] : 0;
return ultimate_cron_should_run($hook['settings']['rules'], $last_run, time(), $hook['settings']['catch_up'], $hook['delta']);
}
/**
* Sort callback for ordering schedule.
*
* @param type $a
* @param type $b
* @return type
*/
function _ultimate_cron_sort_schedule($a, $b) {
return $a['last_run'] == $b['last_run'] ? 0 : ($a['last_run'] < $b['last_run'] ? -1 : 1);
}
/**
* Get cron hooks available.
*
* @return array
* List of modules.
*/
function ultimate_cron_get_hooks() {
static $hooks = NULL;
if (isset($hooks)) {
return $hooks;
}
$hooks = array();
$delta = 0;
// Generate list of hooks
$modules = module_list();
foreach ($modules as $module) {
$file = drupal_get_path('module', $module) . '/' . $module . '.info';
$info = drupal_parse_info_file($file);
foreach (ultimate_cron_easy_hooks() as $hook => $description) {
if (module_hook($module, $hook)) {
$name = $module . '_' . $hook;
$hooks[$name]['description'] = $description;
$hooks[$name]['module'] = $module;
$hooks[$name]['configure'] = isset($info['configure']) ? $info['configure'] : '';
$hooks[$name]['settings'] = ultimate_cron_get_default_settings($module, $name, ultimate_cron_easy_hooks_rule($hook));
}
}
if ($cronapi = module_invoke($module, 'cronapi', 'list')) {
foreach ($cronapi as $name => $description) {
$hooks[$name]['description'] = $description;
$hooks[$name]['module'] = $module;
$hooks[$name]['settings'] = ultimate_cron_get_default_settings($module, $name, ultimate_cron_easy_hooks_rule('cron'));
$hooks[$name]['configure'] = module_invoke($module, 'cronapi', 'configure', $name);
}
}
}
foreach ($hooks as $name => &$hook) {
$hook['callback'] = $hook['function'] = $name;
$hook['background_process'] = array();
$hook['delta'] = $delta++;
}
// Remove ourselves from the list
unset($hooks['ultimate_cron_cron']);
// Allow other to manipulate the hook list
drupal_alter('cron', $hooks);
return $hooks;
}
/**
* Get default settings for job.
*
* @param type $module
* @param type $name
* @return array
*/
function ultimate_cron_get_default_settings($module, $name, $default_rule) {
$conf = module_invoke($module, 'cronapi', 'settings', $name);
if (!is_array($conf)) {
$conf = array();
}
$rule = module_invoke($module, 'cronapi', 'rule', $name);
if (empty($conf['rules']) && !empty($rule)) {
$conf['rules'] = is_array($rule) ? $rule : array(
$rule,
);
}
$conf += _ultimate_cron_default_settings($default_rule);
return $conf;
}
/**
* Get settings for a function.
*
* @param $name
* @return array
* Settings for function
*/
function ultimate_cron_get_settings($name) {
$settings = array();
if (module_exists('ctools')) {
ctools_include('export');
$function = ctools_export_crud_load('ultimate_cron', $name);
if ($function) {
$settings = (array) $function->settings;
}
}
else {
$function = db_query("SELECT settings FROM {ultimate_cron} WHERE name = :name", array(
':name' => $name,
))
->fetchObject();
if (!empty($function) && !empty($function->settings)) {
$settings = (array) unserialize($function->settings);
}
}
if (empty($settings['catch_up'])) {
unset($settings['catch_up']);
}
return $settings;
}
/**
* CRUD save. Also used for ctools integration.
* @param object $object
* object to be saved ->name containing unique machine name.
* @return boolean
* result of query.
*/
function ultimate_cron_crud_save($object) {
return db_merge('ultimate_cron')
->key(array(
'name' => $object->name,
))
->fields(array(
'settings' => serialize($object->settings),
))
->execute();
}
/**
* Set settings for a function.
*
* @param $name
* Function to set settings for.
* @param $settings
* Settings data
* @return boolean
* TRUE on success, FALSE on failure.
*/
function ultimate_cron_set_settings($name, $settings) {
if (module_exists('ctools')) {
ctools_include('export');
$function = ctools_export_crud_new('ultimate_cron');
$function->name = $name;
$function->settings = $settings;
return ctools_export_crud_save('ultimate_cron', $function);
}
else {
$function = new stdClass();
$function->name = $name;
$function->settings = $settings;
return ultimate_cron_crud_save($function);
}
}
/**
* Get latest log line for a function.
*
* @param $name
* Function to get latest log line for,
* @return object
* Log line.
*/
function ultimate_cron_get_log($name) {
$log = db_query_range("\n SELECT l.* FROM {ultimate_cron_log} l\n WHERE l.name = :name\n ORDER BY l.start_stamp DESC", 0, 1, array(
':name' => $name,
))
->fetchAssoc();
if ($log) {
$log['start'] = $log['start_stamp'];
$log['status'] = $log['exec_status'];
$log['end'] = $log['end_stamp'];
}
return $log;
}
/**
* Store watchdog error messages for later use.
*
* @staticvar string $log
* @param $msg
* Message to record.
* @param $reset
* Reset recorded message.
* @return string
* Message recorded.
*/
function ultimate_cron_record_log($msg = NULL, $reset = FALSE, $severity = -1) {
$log =& drupal_static('ultimate_cron_record_log', array());
if ($reset) {
$log = array();
}
$log += array(
'msg' => '',
'severity' => -1,
);
if ($msg) {
$log['msg'] .= "{$msg}\n";
}
$log['severity'] = $log['severity'] < 0 || $severity >= 0 && $severity < $log['severity'] ? $severity : $log['severity'];
return $log['msg'];
}
/**
* Check if rule is valid.
*
* @param $rule
* rule to validate.
* @return
* TRUE if valid, FALSE if not.
*/
function ultimate_cron_validate_rule($rule) {
require_once 'CronRule.class.php';
$cron = new CronRule($rule);
if (!$cron
->isValid()) {
return FALSE;
}
else {
return TRUE;
}
}
/**
* Check if rule is scheduled to run at a given time.
*
* @param $rules
* rules to validate.
* @param $last_run
* last time the rule was run.
* @param $now
* time of validation, set to NULL for now.
* @param $catch_up
* run if we missed our time window?
* @return boolean
* TRUE if rule is scheduled to run, FALSE if not.
*/
function ultimate_cron_should_run($rules, $last_run, $now = NULL, $catch_up = 0, $offset = 0) {
$now = is_null($now) ? time() : $now;
require_once 'CronRule.class.php';
$cron = new CronRule();
foreach ($rules as $rule) {
$cron->rule = $rule;
$cron->offset = $offset;
$last_ran = $cron
->getLastRan($now);
if ($last_ran > $last_run && $last_ran >= $now - $catch_up) {
return TRUE;
}
}
return FALSE;
}
/**
* Get a list of the "easy-hooks".
*
* @return array
* hooks (hook_name => hook_description).
*/
function ultimate_cron_easy_hooks() {
return array(
'cron' => 'Default cron handler',
'hourly' => 'Hourly',
'daily' => 'Daily',
'weekly' => 'Weekly',
'monthly' => 'Monthly',
'yearly' => 'Yearly',
);
}
/**
* Get rule(s) for easy hook(s)
*
* @param $hook
* Hook to get rule for (optional).
* @return mixed
* Rule for $hook if specified, otherwise all rules for all easy hooks.
*/
function ultimate_cron_easy_hooks_rule($hook = NULL) {
$rules = array(
'cron' => variable_get('ultimate_cron_rule', ULTIMATE_CRON_RULE),
'hourly' => ULTIMATE_CRON_HOURLY_RULE,
'daily' => ULTIMATE_CRON_DAILY_RULE,
'weekly' => ULTIMATE_CRON_WEEKLY_RULE,
'monthly' => ULTIMATE_CRON_MONTHLY_RULE,
'yearly' => ULTIMATE_CRON_YEARLY_RULE,
);
return isset($rules[$hook]) ? $rules[$hook] : variable_get('ultimate_cron_rule', ULTIMATE_CRON_RULE);
}
/**
* Get module name
* @param $module
* @return string
* Name of module
*/
function ultimate_cron_module_name($module) {
$file = drupal_get_path('module', $module) . '/' . $module . '.info';
$info = drupal_parse_info_file($file);
return $info['name'] ? $info['name'] : $module;
}
/**
* Load all cronjob settings and processes.
*
* @return array
* Array of cronjobs and their data.
*/
function _ultimate_cron_preload_cron_data() {
$handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
if (module_exists('ctools')) {
ctools_include('export');
$functions = ctools_export_crud_load_all('ultimate_cron');
}
else {
$functions = db_select('ultimate_cron', 'u')
->fields('u', array(
'name',
'settings',
))
->execute()
->fetchAllAssoc('name', PDO::FETCH_ASSOC);
foreach ($functions as &$function) {
$function = (object) $function;
if (!empty($function->settings)) {
$function->settings = unserialize($function->settings);
}
}
}
$hooks = ultimate_cron_get_hooks();
if (function_exists('background_process_update_status')) {
$query = db_select('background_process', 'b')
->fields('b', array(
'handle',
'service_host',
))
->condition('handle', $handle_prefix . '%', 'LIKE');
$query
->addField('b', 'start_stamp', 'start');
$query
->addField('b', 'exec_status', 'status');
$processes = $query
->execute()
->fetchAllAssoc('handle', PDO::FETCH_ASSOC);
}
else {
$processes = db_select('background_process', 'b')
->fields('b', array(
'handle',
'service_host',
'start',
'status',
))
->condition('handle', $handle_prefix . '%', 'LIKE')
->execute()
->fetchAllAssoc('handle', PDO::FETCH_ASSOC);
}
$data = array();
foreach ($hooks as $name => $hook) {
$settings = empty($functions[$name]->settings) ? array() : $functions[$name]->settings;
$settings += $hook['settings'];
$handle = $handle_prefix . $name;
$data[$name] = array(
'settings' => $settings,
'background_process' => empty($processes[$handle]) ? NULL : (object) $processes[$handle],
);
}
return $data;
}
/**
* Return a list of modules that are incompatible with Ultimate Cron
*/
function _ultimate_cron_incompatible_modules() {
static $modules = NULL;
if (isset($modules)) {
return $modules;
}
$modules = array();
$candidates = array(
'parallel_cron',
);
foreach ($candidates as $module) {
if (module_exists($module)) {
$modules[$module] = ultimate_cron_module_name($module);
}
}
return $modules;
}
/**
* Get service hosts defined in the system.
*/
function ultimate_cron_get_service_groups() {
if (function_exists('background_process_get_service_groups')) {
return background_process_get_service_groups();
}
// Fallback for setups that havent upgraded Background Process.
// We have this to avoid upgrade dependencies or majer version bump.
$service_groups = variable_get('background_process_service_groups', array());
$service_groups += array(
'default' => array(
'hosts' => array(
variable_get('background_process_default_service_host', 'default'),
),
),
);
foreach ($service_groups as &$service_group) {
$service_group += array(
'method' => 'background_process_service_group_random',
);
}
return $service_groups;
}
/**
* Smelly code; $default_rule determines if we should populate ...
*/
function _ultimate_cron_default_settings($default_rule = NULL) {
return array(
'enabled' => TRUE,
'rules' => $default_rule ? array(
$default_rule,
) : array(),
'catch_up' => $default_rule ? variable_get('ultimate_cron_catch_up', ULTIMATE_CRON_CATCH_UP) : '',
'queue_lease_time' => '',
);
}
Functions
Name | Description |
---|---|
ultimate_cron_background_process_message_alter | Implements hook_background_process_message_alter(). |
ultimate_cron_background_process_shutdown | Implements hook_background_process_shutdown(). |
ultimate_cron_cleanup_log | Clean up log entries. |
ultimate_cron_cronapi | Implements hook_cronapi(). |
ultimate_cron_cron_alter | Implements hook_cron_alter(). Add better description to core modules. |
ultimate_cron_cron_queue_info | Implements hook_cron_queue_info(). Used for code injection in order to hijack cron runs. |
ultimate_cron_cron_run | The cron handler takes over the normal Drupal cron handler and runs the normal hook_cron() plus the hook_cronapi(). |
ultimate_cron_crud_save | CRUD save. Also used for ctools integration. |
ultimate_cron_easy_hooks | Get a list of the "easy-hooks". |
ultimate_cron_easy_hooks_rule | Get rule(s) for easy hook(s) |
ultimate_cron_get_current_hook | Get current hook being processed |
ultimate_cron_get_default_settings | Get default settings for job. |
ultimate_cron_get_hooks | Get cron hooks available. |
ultimate_cron_get_log | Get latest log line for a function. |
ultimate_cron_get_schedule | Get a list of functions that should be run now. |
ultimate_cron_get_service_groups | Get service hosts defined in the system. |
ultimate_cron_get_settings | Get settings for a function. |
ultimate_cron_help | Implements hook_help(). |
ultimate_cron_hook_should_run | Check if a hook should be run now. |
ultimate_cron_init | Implements hook_init(). |
ultimate_cron_launch_poorman | Launch the poormans cron background process. |
ultimate_cron_load_hook_data | Populate hook array with settings and log data |
ultimate_cron_menu | Implements hook_menu(). |
ultimate_cron_menu_alter | Implements hook_menu_alter(). |
ultimate_cron_module_name | Get module name |
ultimate_cron_permission | Implements hook_permission(). |
ultimate_cron_progress_message_alter | Implements hook_progress_message_alter(). |
ultimate_cron_record_log | Store watchdog error messages for later use. |
ultimate_cron_run_hook | Run a cron hook. Launches the cron job in a background process |
ultimate_cron_run_hook_cli | |
ultimate_cron_set_current_hook | Set current hook being processed |
ultimate_cron_set_settings | Set settings for a function. |
ultimate_cron_should_run | Check if rule is scheduled to run at a given time. |
ultimate_cron_theme_registry_alter | Implements hook_theme_registry_alter(). |
ultimate_cron_trigger_poorman | Launch poorman cron if it's time to do so. |
ultimate_cron_validate_rule | Check if rule is valid. |
ultimate_cron_watchdog | Implements hook_watchdog(). |
_ultimate_cron_default_settings | Smelly code; $default_rule determines if we should populate ... |
_ultimate_cron_incompatible_modules | Return a list of modules that are incompatible with Ultimate Cron |
_ultimate_cron_poorman | The actual poorman function |
_ultimate_cron_preload_cron_data | Load all cronjob settings and processes. |
_ultimate_cron_run_hook | This is the function that is launched into a background process. It runs the cron job and does housekeeping, pre/post execute hooks, etc. |
_ultimate_cron_sort_schedule | Sort callback for ordering schedule. |
Constants
Name | Description |
---|---|
ULTIMATE_CRON_CATCH_UP | Default catch up time for Ultimate Cron. |
ULTIMATE_CRON_CLEANUP_LOG | Default clean up time for log entries (30 days). |
ULTIMATE_CRON_DAILY_RULE | Default rule for easy hook "daily". |
ULTIMATE_CRON_HANDLE_PREFIX | Default handle prefix for background processes. |
ULTIMATE_CRON_HOURLY_RULE | Default rule for easy hook "hourly". |
ULTIMATE_CRON_LAUNCH_WINDOW | Time in seconds to spend on launcing cron jobs. |
ULTIMATE_CRON_MAX_EXECUTION_TIME | Default max execution time for Ultimate Cron. |
ULTIMATE_CRON_MONTHLY_RULE | Default rule for easy hook "monthly". |
ULTIMATE_CRON_POORMAN | Default setting for poorman. |
ULTIMATE_CRON_QUEUE_LEASE_TIME | Default lease time for Ultimate Cron queues. |
ULTIMATE_CRON_QUEUE_POLLING_LATENCY | Default queue polling latency. |
ULTIMATE_CRON_RULE | Default rule. |
ULTIMATE_CRON_SERVICE_GROUP | Time in seconds to spend on launcing cron jobs. |
ULTIMATE_CRON_SIMULTANEOUS_CONNECTIONS | Maximum number of simultaneous connections. |
ULTIMATE_CRON_WEEKLY_RULE | Default rule for easy hook "weekly". |
ULTIMATE_CRON_YEARLY_RULE | Default rule for easy hook "yearly". |