| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404 | <?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', 55);/** * 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 (variable_get('install_task', FALSE) != '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['ultimate_cron_queue_' . $queue_name] = t('Queue: %name', array('%name' => $queue_name));      }      return $jobs;    case 'rule':      $queue_name = preg_replace('/^ultimate_cron_queue_/', '', $job);      if ($queue_name === $job) {        return;      }      return '* * * * *';    case 'execute':      $queue_name = preg_replace('/^ultimate_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 = DrupalQueue::get($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 (variable_get('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("    SELECT l.* FROM {ultimate_cron_log} l    WHERE l.name = :name    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' => '',  );}
 |