ultimate_cron.module 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404
  1. <?php
  2. /**
  3. * @file
  4. *
  5. * @todo Add filter on overview page.
  6. * @todo Add log view (with graph).
  7. * @todo Make proper markup for overview page.
  8. * @todo Refactor drush stuff, too many intimate relations with Background Process
  9. * @todo Refactor Cron % offset stuff. Too mixed up and ungrokable code-wise and 'delta' is not consistent.
  10. *
  11. * hook_cron_alter(&$hooks)
  12. * hook_cron_schedule_alter(&$hooks)
  13. * hook_cron_pre_execute($name, $hook)
  14. * hook_cron_pre_execute_FUNCTION($hook)
  15. * hook_cron_post_execute($name, $hook)
  16. * hook_cron_post_execute_FUNCTION($hook)
  17. */
  18. /**
  19. * Maximum number of simultaneous connections.
  20. */
  21. define('ULTIMATE_CRON_SIMULTANEOUS_CONNECTIONS', 40);
  22. /**
  23. * Default handle prefix for background processes.
  24. */
  25. define('ULTIMATE_CRON_HANDLE_PREFIX', 'uc:');
  26. /**
  27. * Default rule.
  28. */
  29. define('ULTIMATE_CRON_RULE', '*/10+@ * * * *');
  30. /**
  31. * Default rule for easy hook "hourly".
  32. */
  33. define('ULTIMATE_CRON_HOURLY_RULE', '0 * * * *');
  34. /**
  35. * Default rule for easy hook "daily".
  36. */
  37. define('ULTIMATE_CRON_DAILY_RULE', '0 0 * * *');
  38. /**
  39. * Default rule for easy hook "weekly".
  40. */
  41. define('ULTIMATE_CRON_WEEKLY_RULE', '0 0 * * 1');
  42. /**
  43. * Default rule for easy hook "monthly".
  44. */
  45. define('ULTIMATE_CRON_MONTHLY_RULE', '0 0 1 * *');
  46. /**
  47. * Default rule for easy hook "yearly".
  48. */
  49. define('ULTIMATE_CRON_YEARLY_RULE', '0 0 1 1 *');
  50. /**
  51. * Default max execution time for Ultimate Cron.
  52. */
  53. define('ULTIMATE_CRON_MAX_EXECUTION_TIME', 86400);
  54. /**
  55. * Default catch up time for Ultimate Cron.
  56. */
  57. define('ULTIMATE_CRON_CATCH_UP', 300);
  58. /**
  59. * Default lease time for Ultimate Cron queues.
  60. */
  61. define('ULTIMATE_CRON_QUEUE_LEASE_TIME', 30);
  62. /**
  63. * Default clean up time for log entries (30 days).
  64. */
  65. define('ULTIMATE_CRON_CLEANUP_LOG', 86400 * 30);
  66. /**
  67. * Default setting for poorman.
  68. */
  69. define('ULTIMATE_CRON_POORMAN', TRUE);
  70. /**
  71. * Default queue polling latency.
  72. */
  73. define('ULTIMATE_CRON_QUEUE_POLLING_LATENCY', '');
  74. /**
  75. * Time in seconds to spend on launcing cron jobs.
  76. */
  77. define('ULTIMATE_CRON_LAUNCH_WINDOW', 55);
  78. /**
  79. * Time in seconds to spend on launcing cron jobs.
  80. */
  81. define('ULTIMATE_CRON_SERVICE_GROUP', 'default');
  82. // ---------- HOOKS ----------
  83. module_load_include('nagios.inc', 'ultimate_cron');
  84. /**
  85. * Implements hook_help().
  86. */
  87. function ultimate_cron_help($path, $arg) {
  88. switch ($path) {
  89. case 'admin/help#ultimate_cron':
  90. // Return a line-break version of the module README
  91. return '<pre>' . file_get_contents(dirname(__FILE__) . '/README.txt') . '</pre>';
  92. case 'admin/build/cron':
  93. return '<p>' . t('Here you can see the crontab settings for each job available') . '</p>';
  94. case 'admin/build/cron/settings':
  95. return '<p>' . t('Here you can change the crontab settings for each job available') . '</p>';
  96. }
  97. }
  98. /**
  99. * Implements hook_menu().
  100. */
  101. function ultimate_cron_menu() {
  102. $items = array();
  103. $items['admin/config/system/cron/settings'] = array(
  104. 'title' => 'Settings',
  105. 'description' => 'Cron settings',
  106. 'page callback' => 'drupal_get_form',
  107. 'page arguments' => array('ultimate_cron_settings_form'),
  108. 'access arguments' => array('administer ultimate cron'),
  109. 'type' => MENU_LOCAL_TASK,
  110. 'file' => 'ultimate_cron.admin.inc',
  111. );
  112. $items['admin/config/system/cron/settings/%'] = array(
  113. 'title' => 'Settings',
  114. 'page callback' => 'drupal_get_form',
  115. 'page arguments' => array('ultimate_cron_function_settings_form', 5),
  116. 'access arguments' => array('administer ultimate cron'),
  117. 'weight' => 0,
  118. 'file' => 'ultimate_cron.admin.inc',
  119. );
  120. $weight = 0;
  121. foreach (array(
  122. 'error' => 'Errors',
  123. 'warning' => 'Warnings',
  124. 'info' => 'Info',
  125. 'success' => 'Success',
  126. 'running' => 'Running',
  127. ) as $status => $title) {
  128. $items['admin/config/system/cron/overview/' . $status] = array(
  129. 'title' => $title,
  130. 'description' => 'View and manage cron table',
  131. 'page callback' => 'ultimate_cron_view_page',
  132. 'page arguments' => array(5),
  133. 'access arguments' => array('administer ultimate cron'),
  134. 'module' => 'ultimate_cron',
  135. 'file' => 'ultimate_cron.admin.inc',
  136. 'weight' => $weight++,
  137. 'type' => MENU_LOCAL_TASK,
  138. );
  139. }
  140. $items['admin/config/system/cron/overview/all'] = array(
  141. 'title' => 'All',
  142. 'type' => MENU_DEFAULT_LOCAL_TASK,
  143. 'weight' => $weight++,
  144. );
  145. $items['admin/reports/cron'] = array(
  146. 'title' => 'Cron logs',
  147. 'description' => 'View logs for all cron jobs.',
  148. 'page callback' => 'ultimate_cron_view_page',
  149. 'access arguments' => array('administer ultimate cron'),
  150. 'file' => 'ultimate_cron.admin.inc',
  151. );
  152. $items['admin/reports/cron/%'] = array(
  153. 'title' => 'Cron log',
  154. 'description' => 'View log for specific function.',
  155. 'page callback' => 'ultimate_cron_function_log_page',
  156. 'page arguments' => array(3),
  157. 'access arguments' => array('administer ultimate cron'),
  158. 'file' => 'ultimate_cron.admin.inc',
  159. );
  160. $items['admin/ultimate-cron/service/start/%'] = array(
  161. 'type' => MENU_CALLBACK,
  162. 'title' => 'Run cron job',
  163. 'description' => 'Run cron job',
  164. 'page callback' => 'ultimate_cron_service_start',
  165. 'page arguments' => array(4),
  166. 'access arguments' => array('administer ultimate cron'),
  167. 'file' => 'ultimate_cron.admin.inc',
  168. );
  169. $items['admin/ultimate-cron/service/enable/%'] = array(
  170. 'type' => MENU_CALLBACK,
  171. 'title' => 'Enable cron job',
  172. 'description' => 'Enable cron job',
  173. 'page callback' => 'ultimate_cron_service_enable',
  174. 'page arguments' => array(4, TRUE),
  175. 'access arguments' => array('administer ultimate cron'),
  176. 'file' => 'ultimate_cron.admin.inc',
  177. );
  178. $items['admin/ultimate-cron/service/disable/%'] = array(
  179. 'type' => MENU_CALLBACK,
  180. 'title' => 'Disable cron job',
  181. 'description' => 'Disable cron job',
  182. 'page callback' => 'ultimate_cron_service_enable',
  183. 'page arguments' => array(4, FALSE),
  184. 'access arguments' => array('administer ultimate cron'),
  185. 'file' => 'ultimate_cron.admin.inc',
  186. );
  187. $items['admin/ultimate-cron/service/process-status'] = array(
  188. 'type' => MENU_CALLBACK,
  189. 'title' => 'Cron job process status',
  190. 'description' => 'Cron job process status',
  191. 'page callback' => 'ultimate_cron_service_process_status',
  192. 'access arguments' => array('administer ultimate cron'),
  193. 'file' => 'ultimate_cron.admin.inc',
  194. );
  195. return $items;
  196. }
  197. /**
  198. * Implements hook_cron_queue_info().
  199. * Used for code injection in order to hijack cron runs.
  200. */
  201. function ultimate_cron_cron_queue_info() {
  202. static $processed = FALSE;
  203. if (!$processed) {
  204. $processed = TRUE;
  205. if (basename($_SERVER['PHP_SELF']) == 'cron.php') {
  206. ultimate_cron_cron_run(FALSE);
  207. exit;
  208. }
  209. }
  210. return array();
  211. }
  212. /**
  213. * Implements hook_permission().
  214. */
  215. function ultimate_cron_permission() {
  216. return array(
  217. 'administer ultimate cron' => array(
  218. 'title' => t('Administer Ultimate Cron'),
  219. 'description' => t('Lets you configure everything in Ultimate Cron')
  220. )
  221. );
  222. }
  223. /**
  224. * The cron handler takes over the normal Drupal cron handler
  225. * and runs the normal hook_cron() plus the hook_cronapi().
  226. *
  227. * @param boolean $return
  228. * return to caller if TRUE, otherwise exit().
  229. */
  230. function ultimate_cron_cron_run($return = FALSE) {
  231. if (variable_get('install_task', FALSE) != 'done') {
  232. return;
  233. }
  234. // Be other cron module friendly
  235. if (_ultimate_cron_incompatible_modules()) {
  236. return;
  237. }
  238. // If run from core cron through CLI then don't do anything (drush core-cron)
  239. if (!$return && drupal_is_cli()) {
  240. return;
  241. }
  242. // Acquire lock
  243. if (!lock_acquire('cron', 240.0)) {
  244. drupal_set_message(t('Ultimate Cron launcher already running'), 'error');
  245. return;
  246. }
  247. $msc = variable_get('ultimate_cron_simultaneous_connections', ULTIMATE_CRON_SIMULTANEOUS_CONNECTIONS);
  248. // Get list of cron hooks.
  249. $hooks = ultimate_cron_get_hooks();
  250. // Get schedule.
  251. $schedule = ultimate_cron_get_schedule($hooks);
  252. drupal_set_message(t('%jobs jobs scheduled for launch', array('%jobs' => count($schedule))));
  253. // Start the jobs. Keep launching jobs until X seconds into the request.
  254. @set_time_limit(120);
  255. $time = time();
  256. $expire = $time + variable_get('ultimate_cron_launch_window', ULTIMATE_CRON_LAUNCH_WINDOW);
  257. $running = array();
  258. $launched = 0;
  259. $handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
  260. // Try to launch jobs within the given time frame
  261. while (!empty($schedule) && time() < $expire) {
  262. $running_processes = db_select('background_process', 'bp')
  263. ->fields('bp', array('handle'))
  264. ->condition('bp.handle', $handle_prefix . '%', 'LIKE')
  265. ->countQuery()
  266. ->execute()
  267. ->fetchField();
  268. // Launch jobs.
  269. reset($schedule);
  270. while ((list($name, $hook) = each($schedule)) && time() < $expire) {
  271. // Congestion protection
  272. if (empty($hook['override_congestion_protection']) && $running_processes >= $msc) {
  273. continue;
  274. }
  275. if (empty($hook['force_run']) && !ultimate_cron_hook_should_run($hook)) {
  276. unset($schedule[$name]);
  277. continue;
  278. }
  279. $result = ultimate_cron_run_hook($name, $hook);
  280. // Handle errors.
  281. if ($result) {
  282. $handle = $handle_prefix . $name;
  283. $running[$handle] = $result;
  284. unset($schedule[$name]);
  285. $launched++;
  286. $running_processes++;
  287. }
  288. else {
  289. if ($result === FALSE) {
  290. // Could not get lock, skip job.
  291. unset($schedule[$name]);
  292. }
  293. else {
  294. // Failed to start, retry next time.
  295. watchdog('ultimate_cron', "Error starting $name", array(), WATCHDOG_WARNING);
  296. }
  297. }
  298. }
  299. // Jobs running ... check for start
  300. if ($running) {
  301. $result = db_query("SELECT p.name FROM {progress} p WHERE p.name IN (:running)", array(':running' => array_keys($running)));
  302. while ($handle = $result->fetchObject()) {
  303. fclose($running[$handle->name]);
  304. unset($running[$handle->name]);
  305. }
  306. }
  307. sleep(1);
  308. }
  309. // Close all jobs left
  310. if ($running) {
  311. foreach (array_keys($running) as $handle) {
  312. fclose($running[$handle]);
  313. unset($running[$handle]);
  314. }
  315. }
  316. // Update drupals cron timestamp, but don't clear the cache for all variables!
  317. $name = 'cron_last';
  318. $value = time();
  319. global $conf;
  320. db_merge('variable')->key(array('name' => $name))->fields(array('value' => serialize($value)))->execute();
  321. $conf[$name] = $value;
  322. drupal_set_message(t('%jobs jobs launched', array('%jobs' => $launched)));
  323. if (count($schedule)) {
  324. drupal_set_message(t('%jobs jobs failed to launch within %seconds seconds', array(
  325. '%jobs' => count($schedule),
  326. '%seconds' => variable_get('ultimate_cron_launch_window', ULTIMATE_CRON_LAUNCH_WINDOW)
  327. )), empty($schedule) ? 'status' : 'error');
  328. watchdog('ultimate_cron', '%jobs jobs failed to launch within %seconds seconds', array(
  329. '%jobs' => count($schedule),
  330. '%seconds' => variable_get('ultimate_cron_launch_window', ULTIMATE_CRON_LAUNCH_WINDOW)
  331. ), WATCHDOG_WARNING);
  332. }
  333. // Release the lock
  334. lock_release('cron');
  335. // And we're done ...
  336. if ($return) {
  337. return empty($schedule);
  338. }
  339. else {
  340. exit;
  341. }
  342. }
  343. /**
  344. * Implements hook_cronapi().
  345. */
  346. function ultimate_cron_cronapi($op, $job = NULL, $hook = NULL) {
  347. // Grab the defined cron queues.
  348. static $queues = NULL;
  349. if (!isset($queues)) {
  350. $queues = module_invoke_all('cron_queue_info');
  351. drupal_alter('cron_queue_info', $queues);
  352. }
  353. switch ($op) {
  354. case 'list':
  355. $jobs['ultimate_cron_cleanup_log'] = t('Cleanup log entries');
  356. foreach ($queues as $queue_name => $info) {
  357. $jobs['ultimate_cron_queue_' . $queue_name] = t('Queue: %name', array('%name' => $queue_name));
  358. }
  359. return $jobs;
  360. case 'rule':
  361. $queue_name = preg_replace('/^ultimate_cron_queue_/', '', $job);
  362. if ($queue_name === $job) {
  363. return;
  364. }
  365. return '* * * * *';
  366. case 'execute':
  367. $queue_name = preg_replace('/^ultimate_cron_queue_/', '', $job);
  368. if ($queue_name === $job) {
  369. return;
  370. }
  371. $polling_latency = variable_get('ultimate_cron_queue_polling_latency', ULTIMATE_CRON_QUEUE_POLLING_LATENCY);
  372. $info = $queues[$queue_name];
  373. $function = $info['worker callback'];
  374. $end = time() + (isset($info['time']) ? $info['time'] : 15);
  375. $queue = DrupalQueue::get($queue_name);
  376. $items = 0;
  377. while (time() < $end) {
  378. $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);
  379. $item = $queue->claimItem($lease_time);
  380. if (!$item) {
  381. if (is_numeric($polling_latency)) {
  382. usleep($polling_latency * 1000);
  383. continue;
  384. }
  385. else {
  386. break;
  387. }
  388. }
  389. try {
  390. $function($item->data);
  391. $queue->deleteItem($item);
  392. $items++;
  393. }
  394. catch (Exception $e) {
  395. // Just continue ...
  396. watchdog('ultimate_cron', "Queue item %item_id failed with message %message", array(
  397. '%item_id' => $item->item_id,
  398. '%message' => $e->getMessage()
  399. ), WATCHDOG_ERROR);
  400. }
  401. }
  402. drupal_set_message(t('Processed @items items', array('@items' => $items)));
  403. if (is_numeric($polling_latency)) {
  404. $settings = ultimate_cron_get_settings($job);
  405. $hook['settings'] = $settings + $hook['settings'];
  406. if (ultimate_cron_hook_should_run($hook)) {
  407. background_process_keepalive();
  408. }
  409. }
  410. return;
  411. }
  412. }
  413. /**
  414. * Implements hook_progress_message_alter().
  415. */
  416. function ultimate_cron_progress_message_alter(&$message) {
  417. $pseudo_process = new stdClass();
  418. $pseudo_process->handle = $message->data->progress->name;
  419. $pseudo_process->start_stamp = $message->data->progress->start_stamp;
  420. $pseudo_process->progress = $message->data->progress->progress;
  421. $pseudo_process->exec_status = 2;
  422. $message->data->background_process = $pseudo_process;
  423. return ultimate_cron_background_process_message_alter($message);
  424. }
  425. /**
  426. * Implements hook_background_process_message_alter().
  427. */
  428. function ultimate_cron_background_process_message_alter(&$message) {
  429. $handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
  430. // Only alter Ultimate Cron background process messages
  431. if (substr($message->data->background_process->handle, 0, 3) !== $handle_prefix) {
  432. return;
  433. }
  434. $message->data->ultimate_cron['start_stamp'] = format_date((int)$message->data->background_process->start_stamp, 'custom', 'Y-m-d H:i:s');
  435. if ($message->data->background_process->progress >= 0) {
  436. $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);
  437. }
  438. $message->data->ultimate_cron['unlockURL'] = url('background-process/unlock/' . $message->data->background_process->handle, array('query' => array('destination' => 'admin/config/system/cron')));
  439. }
  440. /**
  441. * Implements hook_theme_registry_alter().
  442. */
  443. function ultimate_cron_theme_registry_alter(&$theme_registry) {
  444. $theme_registry['page']['theme paths'][] = drupal_get_path('module', 'ultimate_cron') . '/templates';
  445. }
  446. /**
  447. * Implements hook_watchdog().
  448. */
  449. function ultimate_cron_watchdog($log = array()) {
  450. $record = &drupal_static('ultimate_cron_record', FALSE);
  451. if ($record) {
  452. $log['variables'] = is_array($log['variables']) ? $log['variables'] : array();
  453. ultimate_cron_record_log(t($log['message'], $log['variables']), FALSE, $log['severity']);
  454. }
  455. }
  456. /**
  457. * Implements hook_init().
  458. */
  459. function ultimate_cron_init() {
  460. // No need for hocus pocus and poorman until site is installed.
  461. if (variable_get('install_task') != 'done') {
  462. return;
  463. }
  464. $name = 'cron_last';
  465. if ($value = db_query("SELECT value FROM {variable} WHERE name = :name", array(':name' => $name))->fetchField()) {
  466. $value = unserialize($value);
  467. }
  468. global $conf;
  469. $conf['cron_last'] = $value;
  470. ultimate_cron_trigger_poorman();
  471. }
  472. /**
  473. * Launch poorman cron if it's time to do so.
  474. */
  475. function ultimate_cron_trigger_poorman() {
  476. // Launch poormans cron if applicable
  477. if (variable_get('ultimate_cron_poorman', ULTIMATE_CRON_POORMAN) && !_ultimate_cron_incompatible_modules()) {
  478. $last = variable_get('cron_last', 0);
  479. $last = floor($last / 60) * 60;
  480. $time = time();
  481. $time = floor($time / 60) * 60;
  482. // Don't attempt, if already run within last minute
  483. if ($last < $time) {
  484. ultimate_cron_launch_poorman();
  485. }
  486. }
  487. }
  488. /**
  489. * Launch the poormans cron background process.
  490. */
  491. function ultimate_cron_launch_poorman() {
  492. $handle = 'ultimate_cron_poorman';
  493. if ($process = background_process_get_process($handle)) {
  494. if ($process->start + 120 < time()) {
  495. // Must have timed out or something ... remove it!
  496. if (background_process_remove_process($handle, $process->start)) {
  497. $process = NULL;
  498. }
  499. }
  500. }
  501. if (!$process) {
  502. $process = new BackgroundProcess($handle);
  503. // Because anyone can launch poormans cron, run it as anonymous
  504. $process->uid = 0;
  505. $process->service_host = 'ultimate_cron_poorman';
  506. $result = $process->start('_ultimate_cron_poorman');
  507. }
  508. }
  509. /**
  510. * Implements hook_background_process_shutdown().
  511. *
  512. * Shutdown handler for cronjobs.
  513. */
  514. function ultimate_cron_background_process_shutdown($process, $shutdown_msg = NULL) {
  515. $args = func_get_args();
  516. $record = drupal_static('ultimate_cron_record', FALSE);
  517. $handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
  518. $name = preg_replace('/^' . $handle_prefix . '/', '', $process->handle);
  519. if (!empty($name) && $name != $process->handle) {
  520. static $has_run = array();
  521. if (!empty($has_run[$name])) {
  522. return;
  523. }
  524. $has_run[$name] = TRUE;
  525. // Record end time
  526. $end = microtime(TRUE);
  527. if ($record) {
  528. // Get drupal messages
  529. $messages = drupal_get_messages(NULL, TRUE);
  530. $messages['status'] = empty($messages['status']) ? array() : $messages['status'];
  531. $messages['warning'] = empty($messages['warning']) ? array() : $messages['warning'];
  532. $messages['error'] = empty($messages['error']) ? array() : $messages['error'];
  533. foreach ($messages['status'] as $message) {
  534. ultimate_cron_record_log($message);
  535. }
  536. foreach ($messages['warning'] as $message) {
  537. ultimate_cron_record_log($message, FALSE, WATCHDOG_WARNING);
  538. }
  539. foreach ($messages['error'] as $message) {
  540. ultimate_cron_record_log($message, FALSE, WATCHDOG_ERROR);
  541. }
  542. // Get error messages
  543. $error = error_get_last();
  544. if ($error) {
  545. $message = $error['message'] . ' (line ' . $error['line'] . ' of ' . $error['file'] . ').' . "\n";
  546. $severity = WATCHDOG_INFO;
  547. if ($error['type'] && (E_NOTICE || E_USER_NOTICE || E_USER_WARNING)) {
  548. $severity = WATCHDOG_NOTICE;
  549. }
  550. if ($error['type'] && (E_WARNING || E_CORE_WARNING || E_USER_WARNING)) {
  551. $severity = WATCHDOG_WARNING;
  552. }
  553. if ($error['type'] && (E_ERROR || E_CORE_ERROR || E_USER_ERROR || E_RECOVERABLE_ERROR)) {
  554. $severity = WATCHDOG_ERROR;
  555. }
  556. ultimate_cron_record_log($message, FALSE, $severity);
  557. }
  558. }
  559. if ($shutdown_msg) {
  560. ultimate_cron_record_log($shutdown_msg, FALSE, WATCHDOG_ERROR);
  561. }
  562. $log = drupal_static('ultimate_cron_record_log', array('msg' => '', 'severity' => -1));
  563. $severity = $log['severity'];
  564. $msg = $log['msg'];
  565. $result = $severity < 0 || $severity >= WATCHDOG_INFO ? 1: 0;
  566. // log results here ...
  567. $object = (object)array(
  568. 'name' => $name,
  569. 'start_stamp' => $process->start,
  570. 'end_stamp' => $end,
  571. 'service_host' => $process->service_host,
  572. 'exec_status' => $result,
  573. 'severity' => $severity,
  574. 'msg' => trim($msg),
  575. );
  576. $old_db = db_set_active('background_process');
  577. drupal_write_record('ultimate_cron_log', $object);
  578. db_set_active($old_db);
  579. $object->formatted['start_stamp'] = format_date((int)$object->start_stamp, 'custom', 'Y-m-d H:i:s');
  580. $object->formatted['end_stamp'] = format_date((int)$object->end_stamp, 'custom', 'Y-m-d H:i:s');
  581. $object->formatted['duration'] = gmdate('H:i:s', (int)($object->end_stamp - $object->start_stamp));
  582. $object->formatted['severity'] = $object->severity < 0 ? 'success' : ($object->severity >= WATCHDOG_NOTICE ? 'info' : ($object->severity >= WATCHDOG_WARNING ? 'warning' : 'error'));
  583. $object->formatted['executeURL'] = url('admin/ultimate-cron/service/start/' . $object->name, array('query' => array('destination' => 'admin/config/system/cron')));
  584. $object->formatted['msg'] = strip_tags(html_entity_decode($object->msg, ENT_QUOTES));
  585. if (module_exists('nodejs')) {
  586. $message = (object) array(
  587. 'channel' => 'ultimate_cron',
  588. 'data' => (object) array(
  589. 'action' => 'log',
  590. 'log' => $object,
  591. ),
  592. 'callback' => 'nodejsUltimateCron',
  593. );
  594. nodejs_send_content_channel_message($message);
  595. }
  596. }
  597. }
  598. // ---------- FIXUPS FOR CORE ----------
  599. /**
  600. * Implements hook_menu_alter().
  601. *
  602. * Steal the run-cron, so when you "run cron manually" from the status-reports
  603. * page the ultimate_cron cron handler is run.
  604. */
  605. function ultimate_cron_menu_alter(&$items) {
  606. if (_ultimate_cron_incompatible_modules()) {
  607. return;
  608. }
  609. $items['admin/config/system/cron'] = array(
  610. 'title' => 'Cron',
  611. 'description' => 'View and manage cron table',
  612. 'page callback' => 'ultimate_cron_view_page',
  613. 'access arguments' => array('administer ultimate cron'),
  614. 'module' => 'ultimate_cron',
  615. 'file' => 'ultimate_cron.admin.inc',
  616. );
  617. $items['admin/config/system/cron/overview'] = array(
  618. 'title' => 'List',
  619. 'type' => MENU_DEFAULT_LOCAL_TASK,
  620. 'weight' => -10,
  621. );
  622. $steal = &$items['admin/reports/status/run-cron'];
  623. $steal['page callback'] = 'ultimate_cron_run_cron';
  624. $steal['page arguments'] = array();
  625. $steal['module'] = 'ultimate_cron';
  626. $steal['file'] = 'ultimate_cron.admin.inc';
  627. }
  628. /**
  629. * Implements hook_cron_alter().
  630. * Add better description to core modules.
  631. */
  632. function ultimate_cron_cron_alter(&$hooks) {
  633. $update['dblog_cron']['description'] = t('Remove expired log messages and flood control events');
  634. $update['field_cron']['description'] = t('Purges deleted Field API data');
  635. $update['filter_cron']['description'] = t('Expire outdated filter cache entries');
  636. $update['node_cron']['description'] = t('Mark old nodes as read');
  637. $update['search_cron']['description'] = t('Update indexes');
  638. $update['system_cron']['description'] = t('Cleanup (batch, flood, temp-files, etc.)');
  639. $update['aggregator_cron']['description'] = t('Refresh feeds');
  640. $update['openid_cron']['description'] = t('Remove expired nonces from the database');
  641. $update['ping_cron']['description'] = t('Notify remote sites');
  642. $update['poll_cron']['description'] = t('Close expired polls');
  643. $update['statistics_cron']['description'] = t('Reset counts and clean up');
  644. $update['trigger_cron']['description'] = t('Run actions for cron triggers');
  645. $update['tracker_cron']['description'] = t('Update tracker index');
  646. $update['update_cron']['description'] = t('Check system for updates');
  647. $update['ultimate_cron_cleanup_log']['configure'] = 'admin/config/system/cron/settings';
  648. $update['dblog_cron']['configure'] = 'admin/config/development/logging';
  649. foreach ($update as $name => $data) {
  650. if (isset($hooks[$name])) {
  651. foreach ($data as $key => $value) {
  652. $hooks[$name][$key] = $value;
  653. }
  654. }
  655. }
  656. }
  657. // ---------- HELPER FUNCTIONS ----------
  658. /**
  659. * Set current hook being processed
  660. *
  661. * @param $hook
  662. * @return
  663. * Current hook
  664. */
  665. function ultimate_cron_set_current_hook($hook = NULL) {
  666. static $current_hook;
  667. if ($hook) {
  668. $current_hook = $hook;
  669. }
  670. return $current_hook;
  671. }
  672. /**
  673. * Get current hook being processed
  674. *
  675. * @return
  676. * Current hook
  677. */
  678. function ultimate_cron_get_current_hook() {
  679. return ultimate_cron_set_current_hook();
  680. }
  681. /**
  682. * The actual poorman function
  683. * @return type
  684. */
  685. function _ultimate_cron_poorman() {
  686. if (!variable_get('ultimate_cron_poorman', ULTIMATE_CRON_POORMAN) || _ultimate_cron_incompatible_modules()) {
  687. return;
  688. }
  689. // Restart when done
  690. background_process_keepalive();
  691. // Derive current minute
  692. $time = time();
  693. $time = floor($time / 60) * 60;
  694. // Run the cron
  695. ultimate_cron_cron_run(TRUE);
  696. // Wait until end of "current" minute
  697. $wait = $time + 60 - time();
  698. if ($wait > 0 && $wait <= 60) {
  699. sleep($wait);
  700. }
  701. }
  702. /**
  703. * Clean up log entries.
  704. */
  705. function ultimate_cron_cleanup_log() {
  706. do {
  707. $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)));
  708. $lids = array();
  709. while ($row = $result->fetchObject()) {
  710. $lids[] = $row->lid;
  711. }
  712. if (!empty($lids)) {
  713. db_query("DELETE FROM {ultimate_cron_log} WHERE lid IN (:lids)", array(':lids' => $lids));
  714. }
  715. } while (!empty($lids));
  716. }
  717. /**
  718. * Run a cron hook.
  719. * Launches the cron job in a background process
  720. *
  721. * @param $name
  722. * @param $hook
  723. * @return mixed
  724. * Connections file handle on success.
  725. */
  726. function ultimate_cron_run_hook($name, $hook) {
  727. // Run the job in background
  728. $result = NULL;
  729. $handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
  730. $handle = $handle_prefix . $name;
  731. $process = new BackgroundProcess($handle);
  732. // Always run cron job as anonymous user
  733. $process->uid = 0;
  734. // Determine service group
  735. $process->service_group = empty($hook['settings']['service_group']) ? variable_get('ultimate_cron_service_group', ULTIMATE_CRON_SERVICE_GROUP) : $hook['settings']['service_group'];
  736. $hook['timestamp'] = time();
  737. unset($hook['log']['msg']);
  738. $result = $process->start('_ultimate_cron_run_hook', array($name, $hook));
  739. return $result ? $process->connection : $result;
  740. }
  741. function ultimate_cron_run_hook_cli($name, $hook) {
  742. // Run the job in background
  743. $result = NULL;
  744. $handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
  745. $handle = $handle_prefix . $name;
  746. $process = new BackgroundProcess($handle);
  747. // Always run cron job as anonymous user
  748. $process->uid = 0;
  749. $hook['timestamp'] = time();
  750. if ($process->lock()) {
  751. try {
  752. $process_obj = background_process_get_process($handle);
  753. $process->start = $process_obj->start;
  754. if (function_exists('background_process_update_status')) {
  755. background_process_update_status($handle, BACKGROUND_PROCESS_STATUS_RUNNING);
  756. }
  757. else {
  758. db_update('background_process')
  759. ->fields(array(
  760. 'exec_status' => BACKGROUND_PROCESS_STATUS_RUNNING,
  761. ))
  762. ->condition('handle', $handle)
  763. ->execute();
  764. }
  765. $old_handle = background_process_current_handle();
  766. background_process_current_handle($handle);
  767. _ultimate_cron_run_hook($name, $hook);
  768. module_invoke_all('background_process_shutdown', $process);
  769. background_process_current_handle($old_handle);
  770. background_process_remove_process($handle, $process->start);
  771. return TRUE;
  772. }
  773. catch (Exception $e) {
  774. module_invoke_all('background_process_shutdown', $process, (string)$e);
  775. return NULL;
  776. }
  777. }
  778. return FALSE;
  779. }
  780. /**
  781. * This is the function that is launched into a background process.
  782. * It runs the cron job and does housekeeping, pre/post execute hooks, etc.
  783. *
  784. * @param $module
  785. * Module containing function.
  786. * @param $name
  787. * Function to call.
  788. * @return boolean
  789. * TRUE on success, FALSE on failure.
  790. */
  791. function _ultimate_cron_run_hook($name, $hook) {
  792. @set_time_limit(variable_get('ultimate_cron_max_execution_time', ULTIMATE_CRON_MAX_EXECUTION_TIME));
  793. drupal_save_session(FALSE);
  794. // Load current process
  795. $process = background_process_get_process(background_process_current_handle());
  796. $record = &drupal_static('ultimate_cron_record', FALSE);
  797. $record = TRUE;
  798. ultimate_cron_record_log(NULL, TRUE);
  799. // Load log if not present
  800. if (!isset($hook['log'])) {
  801. $hook['log'] = ultimate_cron_get_log($name);
  802. }
  803. $time = time();
  804. if (empty($hook['skip_catch_up']) && !ultimate_cron_hook_should_run($hook)) {
  805. // Hook started too late!
  806. watchdog('ultimate_cron', '%function skipped. Invoked at %invoke, but did not start until %start', array(
  807. '%function' => $name,
  808. '%invoke' => format_date($hook['timestamp'], 'custom', 'Y-m-d H:i:s'),
  809. '%start' => format_date($time, 'custom', 'Y-m-d H:i:s'),
  810. ), WATCHDOG_ERROR);
  811. ultimate_cron_background_process_shutdown($process, NULL);
  812. return FALSE;
  813. }
  814. // Let other modules do stuff before execution, if they need to.
  815. module_invoke_all('cron_pre_execute', $name, $hook);
  816. module_invoke_all('cron_pre_execute_' . $name, $hook);
  817. if (!empty($hook['file'])) {
  818. include_once $hook['file'];
  819. }
  820. $callback = $hook['callback'];
  821. ultimate_cron_set_current_hook($hook);
  822. if (is_callable($callback)) {
  823. call_user_func($callback);
  824. }
  825. else {
  826. module_invoke($hook['module'], 'cronapi', 'execute', $name, $hook);
  827. }
  828. ultimate_cron_background_process_shutdown($process, NULL);
  829. // Let other modules do stuff before execution, if they need to.
  830. module_invoke_all('cron_post_execute', $name, $hook);
  831. module_invoke_all('cron_post_execute_' . $name, $hook);
  832. return TRUE;
  833. }
  834. /**
  835. * Get a list of functions that should be run now.
  836. *
  837. * @param $hooks
  838. * Array of cron hooks to check.
  839. * @return array
  840. * Functions to run now.
  841. */
  842. function ultimate_cron_get_schedule($hooks) {
  843. // Create list of scheduled functions
  844. $schedule = array();
  845. foreach ($hooks as $name => &$hook) {
  846. ultimate_cron_load_hook_data($hook);
  847. // Store last run in hook for sorting purposes
  848. $last_run = isset($hook['log']['start']) ? $hook['log']['start'] : 0;
  849. $hook['last_run'] = $last_run;
  850. if (ultimate_cron_hook_should_run($hook)) {
  851. $schedule[$name] = $hook;
  852. }
  853. }
  854. // Sort by last run time
  855. uasort($schedule, '_ultimate_cron_sort_schedule');
  856. // Allow other to manipulate the schedule
  857. drupal_alter('cron_schedule', $schedule);
  858. return $schedule;
  859. }
  860. /**
  861. * Populate hook array with settings and log data
  862. *
  863. * @param type $hook
  864. */
  865. function ultimate_cron_load_hook_data(&$hook) {
  866. // Get settings
  867. $hook['settings'] = ultimate_cron_get_settings($hook['function']) + $hook['settings'];
  868. // Get log, used for checking last start time
  869. $hook['log'] = ultimate_cron_get_log($hook['function']);
  870. }
  871. /**
  872. * Check if a hook should be run now.
  873. *
  874. * @param array $hook
  875. * @return boolean
  876. */
  877. function ultimate_cron_hook_should_run($hook) {
  878. // Is it enabled?
  879. if (empty($hook['settings']['enabled'])) {
  880. return FALSE;
  881. }
  882. $last_run = isset($hook['log']['start']) ? $hook['log']['start'] : 0;
  883. return ultimate_cron_should_run($hook['settings']['rules'], $last_run, time(), $hook['settings']['catch_up'], $hook['delta']);
  884. }
  885. /**
  886. * Sort callback for ordering schedule.
  887. *
  888. * @param type $a
  889. * @param type $b
  890. * @return type
  891. */
  892. function _ultimate_cron_sort_schedule($a, $b) {
  893. return $a['last_run'] == $b['last_run'] ? 0 : ($a['last_run'] < $b['last_run'] ? -1 : 1);
  894. }
  895. /**
  896. * Get cron hooks available.
  897. *
  898. * @return array
  899. * List of modules.
  900. */
  901. function ultimate_cron_get_hooks() {
  902. static $hooks = NULL;
  903. if (isset($hooks)) {
  904. return $hooks;
  905. }
  906. $hooks = array();
  907. $delta = 0;
  908. // Generate list of hooks
  909. $modules = module_list();
  910. foreach ($modules as $module) {
  911. $file = drupal_get_path('module', $module) . '/' . $module . '.info';
  912. $info = drupal_parse_info_file($file);
  913. foreach (ultimate_cron_easy_hooks() as $hook => $description) {
  914. if (module_hook($module, $hook)) {
  915. $name = $module . '_' . $hook;
  916. $hooks[$name]['description'] = $description;
  917. $hooks[$name]['module'] = $module;
  918. $hooks[$name]['configure'] = isset($info['configure']) ? $info['configure'] : '';
  919. $hooks[$name]['settings'] = ultimate_cron_get_default_settings($module, $name, ultimate_cron_easy_hooks_rule($hook));
  920. }
  921. }
  922. if ($cronapi = module_invoke($module, 'cronapi', 'list')) {
  923. foreach ($cronapi as $name => $description) {
  924. $hooks[$name]['description'] = $description;
  925. $hooks[$name]['module'] = $module;
  926. $hooks[$name]['settings'] = ultimate_cron_get_default_settings($module, $name, ultimate_cron_easy_hooks_rule('cron'));
  927. $hooks[$name]['configure'] = module_invoke($module, 'cronapi', 'configure', $name);
  928. }
  929. }
  930. }
  931. foreach ($hooks as $name => &$hook) {
  932. $hook['callback'] = $hook['function'] = $name;
  933. $hook['background_process'] = array();
  934. $hook['delta'] = $delta++;
  935. }
  936. // Remove ourselves from the list
  937. unset($hooks['ultimate_cron_cron']);
  938. // Allow other to manipulate the hook list
  939. drupal_alter('cron', $hooks);
  940. return $hooks;
  941. }
  942. /**
  943. * Get default settings for job.
  944. *
  945. * @param type $module
  946. * @param type $name
  947. * @return array
  948. */
  949. function ultimate_cron_get_default_settings($module, $name, $default_rule) {
  950. $conf = module_invoke($module, 'cronapi', 'settings', $name);
  951. if (!is_array($conf)) {
  952. $conf = array();
  953. }
  954. $rule = module_invoke($module, 'cronapi', 'rule', $name);
  955. if (empty($conf['rules']) && !empty($rule)) {
  956. $conf['rules'] = is_array($rule) ? $rule : array($rule);
  957. }
  958. $conf += _ultimate_cron_default_settings($default_rule);
  959. return $conf;
  960. }
  961. /**
  962. * Get settings for a function.
  963. *
  964. * @param $name
  965. * @return array
  966. * Settings for function
  967. */
  968. function ultimate_cron_get_settings($name) {
  969. $settings = array();
  970. if (module_exists('ctools')) {
  971. ctools_include('export');
  972. $function = ctools_export_crud_load('ultimate_cron', $name);
  973. if ($function) {
  974. $settings = (array) $function->settings;
  975. }
  976. }
  977. else {
  978. $function = db_query("SELECT settings FROM {ultimate_cron} WHERE name = :name", array(':name' => $name))->fetchObject();
  979. if (!empty($function) && !empty($function->settings)) {
  980. $settings = (array) unserialize($function->settings);
  981. }
  982. }
  983. if (empty($settings['catch_up'])) {
  984. unset($settings['catch_up']);
  985. }
  986. return $settings;
  987. }
  988. /**
  989. * CRUD save. Also used for ctools integration.
  990. * @param object $object
  991. * object to be saved ->name containing unique machine name.
  992. * @return boolean
  993. * result of query.
  994. */
  995. function ultimate_cron_crud_save($object) {
  996. return db_merge('ultimate_cron')
  997. ->key(array('name' => $object->name))
  998. ->fields(array(
  999. 'settings' => serialize($object->settings),
  1000. ))
  1001. ->execute();
  1002. }
  1003. /**
  1004. * Set settings for a function.
  1005. *
  1006. * @param $name
  1007. * Function to set settings for.
  1008. * @param $settings
  1009. * Settings data
  1010. * @return boolean
  1011. * TRUE on success, FALSE on failure.
  1012. */
  1013. function ultimate_cron_set_settings($name, $settings) {
  1014. if (module_exists('ctools')) {
  1015. ctools_include('export');
  1016. $function = ctools_export_crud_new('ultimate_cron');
  1017. $function->name = $name;
  1018. $function->settings = $settings;
  1019. return ctools_export_crud_save('ultimate_cron', $function);
  1020. }
  1021. else {
  1022. $function = new stdClass();
  1023. $function->name = $name;
  1024. $function->settings = $settings;
  1025. return ultimate_cron_crud_save($function);
  1026. }
  1027. }
  1028. /**
  1029. * Get latest log line for a function.
  1030. *
  1031. * @param $name
  1032. * Function to get latest log line for,
  1033. * @return object
  1034. * Log line.
  1035. */
  1036. function ultimate_cron_get_log($name) {
  1037. $log = db_query_range("
  1038. SELECT l.* FROM {ultimate_cron_log} l
  1039. WHERE l.name = :name
  1040. ORDER BY l.start_stamp DESC",
  1041. 0, 1, array(':name' => $name))
  1042. ->fetchAssoc();
  1043. if ($log) {
  1044. $log['start'] = $log['start_stamp'];
  1045. $log['status'] = $log['exec_status'];
  1046. $log['end'] = $log['end_stamp'];
  1047. }
  1048. return $log;
  1049. }
  1050. /**
  1051. * Store watchdog error messages for later use.
  1052. *
  1053. * @staticvar string $log
  1054. * @param $msg
  1055. * Message to record.
  1056. * @param $reset
  1057. * Reset recorded message.
  1058. * @return string
  1059. * Message recorded.
  1060. */
  1061. function ultimate_cron_record_log($msg = NULL, $reset = FALSE, $severity = -1) {
  1062. $log = &drupal_static('ultimate_cron_record_log', array());
  1063. if ($reset) {
  1064. $log = array();
  1065. }
  1066. $log += array('msg' => '', 'severity' => -1);
  1067. if ($msg) {
  1068. $log['msg'] .= "$msg\n";
  1069. }
  1070. $log['severity'] = $log['severity'] < 0 || ($severity >= 0 && $severity < $log['severity']) ? $severity : $log['severity'];
  1071. return $log['msg'];
  1072. }
  1073. /**
  1074. * Check if rule is valid.
  1075. *
  1076. * @param $rule
  1077. * rule to validate.
  1078. * @return
  1079. * TRUE if valid, FALSE if not.
  1080. */
  1081. function ultimate_cron_validate_rule($rule) {
  1082. require_once 'CronRule.class.php';
  1083. $cron = new CronRule($rule);
  1084. if (!$cron->isValid()) {
  1085. return FALSE;
  1086. }
  1087. else {
  1088. return TRUE;
  1089. }
  1090. }
  1091. /**
  1092. * Check if rule is scheduled to run at a given time.
  1093. *
  1094. * @param $rules
  1095. * rules to validate.
  1096. * @param $last_run
  1097. * last time the rule was run.
  1098. * @param $now
  1099. * time of validation, set to NULL for now.
  1100. * @param $catch_up
  1101. * run if we missed our time window?
  1102. * @return boolean
  1103. * TRUE if rule is scheduled to run, FALSE if not.
  1104. */
  1105. function ultimate_cron_should_run($rules, $last_run, $now = NULL, $catch_up = 0, $offset = 0) {
  1106. $now = is_null($now) ? time() : $now;
  1107. require_once 'CronRule.class.php';
  1108. $cron = new CronRule();
  1109. foreach ($rules as $rule) {
  1110. $cron->rule = $rule;
  1111. $cron->offset = $offset;
  1112. $last_ran = $cron->getLastRan($now);
  1113. if ($last_ran > $last_run && $last_ran >= $now - $catch_up) {
  1114. return TRUE;
  1115. }
  1116. }
  1117. return FALSE;
  1118. }
  1119. /**
  1120. * Get a list of the "easy-hooks".
  1121. *
  1122. * @return array
  1123. * hooks (hook_name => hook_description).
  1124. */
  1125. function ultimate_cron_easy_hooks() {
  1126. return array(
  1127. 'cron' => 'Default cron handler',
  1128. 'hourly' => 'Hourly',
  1129. 'daily' => 'Daily',
  1130. 'weekly' => 'Weekly',
  1131. 'monthly' => 'Monthly',
  1132. 'yearly' => 'Yearly'
  1133. );
  1134. }
  1135. /**
  1136. * Get rule(s) for easy hook(s)
  1137. *
  1138. * @param $hook
  1139. * Hook to get rule for (optional).
  1140. * @return mixed
  1141. * Rule for $hook if specified, otherwise all rules for all easy hooks.
  1142. */
  1143. function ultimate_cron_easy_hooks_rule($hook = NULL) {
  1144. $rules = array(
  1145. 'cron' => variable_get('ultimate_cron_rule', ULTIMATE_CRON_RULE),
  1146. 'hourly' => ULTIMATE_CRON_HOURLY_RULE,
  1147. 'daily' => ULTIMATE_CRON_DAILY_RULE,
  1148. 'weekly' => ULTIMATE_CRON_WEEKLY_RULE,
  1149. 'monthly' => ULTIMATE_CRON_MONTHLY_RULE,
  1150. 'yearly' => ULTIMATE_CRON_YEARLY_RULE,
  1151. );
  1152. return isset($rules[$hook]) ? $rules[$hook] : variable_get('ultimate_cron_rule', ULTIMATE_CRON_RULE);
  1153. }
  1154. /**
  1155. * Get module name
  1156. * @param $module
  1157. * @return string
  1158. * Name of module
  1159. */
  1160. function ultimate_cron_module_name($module) {
  1161. $file = drupal_get_path('module', $module) . '/' . $module . '.info';
  1162. $info = drupal_parse_info_file($file);
  1163. return $info['name'] ? $info['name'] : $module;
  1164. }
  1165. /**
  1166. * Load all cronjob settings and processes.
  1167. *
  1168. * @return array
  1169. * Array of cronjobs and their data.
  1170. */
  1171. function _ultimate_cron_preload_cron_data() {
  1172. $handle_prefix = variable_get('ultimate_cron_handle_prefix', ULTIMATE_CRON_HANDLE_PREFIX);
  1173. if (module_exists('ctools')) {
  1174. ctools_include('export');
  1175. $functions = ctools_export_crud_load_all('ultimate_cron');
  1176. }
  1177. else {
  1178. $functions = db_select('ultimate_cron', 'u')
  1179. ->fields('u', array('name', 'settings'))
  1180. ->execute()
  1181. ->fetchAllAssoc('name', PDO::FETCH_ASSOC);
  1182. foreach ($functions as &$function) {
  1183. $function = (object)$function;
  1184. if (!empty($function->settings)) {
  1185. $function->settings = unserialize($function->settings);
  1186. }
  1187. }
  1188. }
  1189. $hooks = ultimate_cron_get_hooks();
  1190. if (function_exists('background_process_update_status')) {
  1191. $query = db_select('background_process', 'b')
  1192. ->fields('b', array('handle', 'service_host'))
  1193. ->condition('handle', $handle_prefix . '%', 'LIKE');
  1194. $query->addField('b', 'start_stamp', 'start');
  1195. $query->addField('b', 'exec_status', 'status');
  1196. $processes = $query->execute()->fetchAllAssoc('handle', PDO::FETCH_ASSOC);
  1197. }
  1198. else {
  1199. $processes = db_select('background_process', 'b')
  1200. ->fields('b', array('handle', 'service_host', 'start', 'status'))
  1201. ->condition('handle', $handle_prefix . '%', 'LIKE')
  1202. ->execute()
  1203. ->fetchAllAssoc('handle', PDO::FETCH_ASSOC);
  1204. }
  1205. $data = array();
  1206. foreach ($hooks as $name => $hook) {
  1207. $settings = empty($functions[$name]->settings) ? array() : $functions[$name]->settings;
  1208. $settings += $hook['settings'];
  1209. $handle = $handle_prefix . $name;
  1210. $data[$name] = array(
  1211. 'settings' => $settings,
  1212. 'background_process' => empty($processes[$handle]) ? NULL : (object)$processes[$handle],
  1213. );
  1214. }
  1215. return $data;
  1216. }
  1217. /**
  1218. * Return a list of modules that are incompatible with Ultimate Cron
  1219. */
  1220. function _ultimate_cron_incompatible_modules() {
  1221. static $modules = NULL;
  1222. if (isset($modules)) {
  1223. return $modules;
  1224. }
  1225. $modules = array();
  1226. $candidates = array('parallel_cron');
  1227. foreach ($candidates as $module) {
  1228. if (module_exists($module)) {
  1229. $modules[$module] = ultimate_cron_module_name($module);
  1230. }
  1231. }
  1232. return $modules;
  1233. }
  1234. /**
  1235. * Get service hosts defined in the system.
  1236. */
  1237. function ultimate_cron_get_service_groups() {
  1238. if (function_exists('background_process_get_service_groups')) {
  1239. return background_process_get_service_groups();
  1240. }
  1241. // Fallback for setups that havent upgraded Background Process.
  1242. // We have this to avoid upgrade dependencies or majer version bump.
  1243. $service_groups = variable_get('background_process_service_groups', array());
  1244. $service_groups += array(
  1245. 'default' => array(
  1246. 'hosts' => array(variable_get('background_process_default_service_host', 'default')),
  1247. ),
  1248. );
  1249. foreach ($service_groups as &$service_group) {
  1250. $service_group += array(
  1251. 'method' => 'background_process_service_group_random'
  1252. );
  1253. }
  1254. return $service_groups;
  1255. }
  1256. /**
  1257. * Smelly code; $default_rule determines if we should populate ...
  1258. */
  1259. function _ultimate_cron_default_settings($default_rule = NULL) {
  1260. return array(
  1261. 'enabled' => TRUE,
  1262. 'rules' => $default_rule ? array($default_rule) : array(),
  1263. 'catch_up' => $default_rule ? variable_get('ultimate_cron_catch_up', ULTIMATE_CRON_CATCH_UP) : '',
  1264. 'queue_lease_time' => '',
  1265. );
  1266. }