Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.577 diff -u -r1.577 common.inc --- includes/common.inc 3 Oct 2006 00:24:19 -0000 1.577 +++ includes/common.inc 5 Oct 2006 00:43:34 -0000 @@ -1693,15 +1693,20 @@ * Returns TRUE if ran successfully */ function drupal_cron_run() { + $cron_run_time = variable_get('cron_run_time', 240); + $advanced_cron = variable_get('cron_run_advanced', FALSE); + $cron_info = array(); + // If not in 'safe mode', increase the maximum execution time: + if (!ini_get('safe_mode')) { - set_time_limit(240); + set_time_limit($cron_run_time); } // Fetch the cron semaphore $semaphore = variable_get('cron_semaphore', FALSE); - if ($semaphore) { + if ($semaphore && !$_REQUEST['rid']) { if (time() - $semaphore > 3600) { // Either cron has been running for more than an hour or the semaphore // was not reset due to a database error. @@ -1715,19 +1720,93 @@ watchdog('cron', t('Attempting to re-run cron while it is already running.'), WATCHDOG_WARNING); } } + else if($_REQUEST['rid']) { + $rid = $_REQUEST['rid']; + $riid = $_REQUEST['riid']; + db_query("UPDATE {cron_run_info} SET run_date = " . time() . " WHERE riid = %d", $riid); + // Run this cron + module_invoke($_REQUEST['module'], 'cron'); + # End Run + db_query("UPDATE {cron_run_info} SET run_status = %d, run_stop = %d WHERE riid = %d", WATCHDOG_NOTICE, time(), $riid); + db_query("UPDATE {cron_runs} SET run_ticks = run_ticks + 1 WHERE rid = %d", $rid); + } else { + # First truncate any old crons + $max_crons = variable_get('cron_run_history', 50); + $truncate = db_result(db_query("SELECT COUNT(*) FROM {cron_runs}")); + if($truncate) { + $rs = db_query_range("SELECT rid FROM {cron_runs} ORDER BY rid DESC", $max_crons, $truncate); + while($row = db_fetch_object($rs)) { + db_query("DELETE FROM {cron_run_info} WHERE rid = %d", $row->rid); + db_query("DELETE FROM {cron_runs} WHERE rid = %d", $row->rid); + } + } + + if($advanced_cron) { + # Before we start again, if were in advanced mode, we need to see if we need to + # time out or error and cron runs. + # Since we're dealing with dates, and need a date diff, much easier to work + # with just a unix time stamp to do a simple (-) + db_query("UPDATE {cron_runs} SET run_status = '%d' WHERE (run_date - run_stop >= $cron_run_time) || (run_date - %d >= $cron_run_time)", + WATCHDOG_ERROR, time()); + db_query("UPDATE {cron_run_info} SET run_status = '%d' WHERE (run_date - run_stop >= $cron_run_time) || (run_date - %d >= $cron_run_time)", + WATCHDOG_ERROR, time()); + } + // Register shutdown callback register_shutdown_function('drupal_cron_cleanup'); // Lock cron semaphore - variable_set('cron_semaphore', time()); - - // Iterate through the modules calling their cron handlers (if any): - module_invoke_all('cron'); - + $semaphore = time(); + variable_set('cron_semaphore', $semaphore); + if($advanced_cron) { + global $base_url; + db_query("INSERT INTO {cron_runs}(run_date, run_status) VALUES(%d, %d)", time(), WATCHDOG_WARNING); + $rid = db_result(db_query("SELECT MAX(rid) FROM {cron_runs}")); + $cmd = variable_get('cron_run_command', ''); + if($cmd == '') { + exit(); + } + # Start firing the modules + $modules = module_list(); + $winos = eregi("windows",strtolower(php_uname())); + $module_count = 0; + foreach($modules as $module) { + if(module_hook($module, 'cron')) { + db_query("INSERT INTO {cron_run_info}(rid, module_name, run_status) VALUES(%d, '%s', %d)", $rid, $module, WATCHDOG_WARNING); + $riid = db_result(db_query("SELECT MAX(riid) FROM {cron_run_info}")); + + $run_cmd = "$cmd \"$base_url/cron.php?rid=$rid&riid=$riid&module=$module\""; + if(!$winos) { + pclose(popen("$run_cmd & ", "r")); + } + else { + pclose(popen("start \"bla\" " . $run_cmd . " ", "r")); + } + $module_count++; + } + } + db_query("UPDATE {cron_runs} SET run_status = %d, run_stop = %d, run_ticks = %d", WATCHDOG_OK, time(), (-1 * $module_count)); + } + else { + // Iterate through the modules calling their cron handlers (if any): + db_query("INSERT INTO {cron_runs}(run_date, run_status) VALUES(%d, %d)", time(), WATCHDOG_WARNING); + $rid = db_result(db_query("SELECT MAX(rid) FROM {cron_runs}")); + db_query("INSERT INTO {cron_run_info}(rid, module_name, run_status, run_date) VALUES(%d, '%s', %d, %d)", $rid, 'system-all-modules', WATCHDOG_WARNING, time()); + $riid = db_result(db_query("SELECT MAX(riid) FROM {cron_run_info}")); + module_invoke_all('cron'); + db_query("UPDATE {cron_run_info} SET run_status = %d, run_stop = %d WHERE riid = %d", WATCHDOG_NOTICE, time(), $riid); + db_query("UPDATE {cron_runs} SET run_status = %d, run_ticks = %d, run_stop = %d WHERE rid = %d",WATCHDOG_OK, 0, time(), $rid); + } + // Record cron time variable_set('cron_last', time()); - watchdog('cron', t('Cron run completed.'), WATCHDOG_NOTICE); + if($advanced_cron) { + watchdog('cron', t('Cron run started %module_count runs.', array('%module_count' => $module_count)), WATCHDOG_NOTICE); + } + else { + watchdog('cron', t('Cron run completed.'), WATCHDOG_NOTICE); + } // Release cron semaphore variable_del('cron_semaphore'); Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.372 diff -u -r1.372 system.module --- modules/system/system.module 2 Oct 2006 16:49:08 -0000 1.372 +++ modules/system/system.module 5 Oct 2006 00:43:35 -0000 @@ -185,6 +185,7 @@ } } + // Modules: $items[] = array('path' => 'admin/settings/modules', 'title' => t('modules'), @@ -194,6 +195,20 @@ 'callback arguments' => array('system_modules'), 'access' => $access); + // Cron: + $items[] = array('path' => 'admin/settings/cron', + 'title' => t('cron'), + 'description' => t('Cron Settings'), + 'callback' => 'drupal_get_form', + 'callback arguments' => array('system_cron_settings'), + 'access' => $access); + $items[] = array('path' => 'admin/logs/cron', + 'title' => t('cron runs'), + 'description' => t('Information about past cron runs'), + 'callback' => 'drupal_get_form', + 'callback arguments' => array('system_cron_logs'), + 'access' => $access); + // Settings: $items[] = array( 'path' => 'admin/settings/site-information', @@ -1219,6 +1234,155 @@ return 'admin/build/themes'; } + /** + * Cron logs + */ +function system_cron_logs($form_values = NULL) { + if(is_numeric(arg(3))) { + $rid = arg(3); + $cron_run = db_fetch_object(db_query("SELECT cr.rid, cr.run_date, cr.run_status, cr.run_ticks, cr.run_stop, COUNT(riid) modules_ran + FROM {cron_runs} cr INNER JOIN {cron_run_info} ci ON cr.rid = ci.rid + WHERE cr.rid = %d + GROUP BY cr.rid, cr.run_date, cr.run_status, cr.run_ticks, cr.run_stop + ", $rid)); + if(!$cron_run) { + drupal_set_message(t('Invalid cron run given'), 'error'); + drupal_goto('admin/logs/cron'); + } + + # Icon Setup + $icons = array(WATCHDOG_NOTICE => theme('image', 'misc/watchdog-ok.png', t('ok'), t('ok')), + WATCHDOG_WARNING => theme('image', 'misc/watchdog-warning.png', t('warning'), t('warning')), + WATCHDOG_ERROR => theme('image', 'misc/watchdog-error.png', t('error'), t('error'))); + + # General Run Information + $form['cron_run_info'] = array('#value' => " + Run ID: $rid
+ Run Date: " . format_date($cron_run->run_date, 'large') . "
+ Modules Ran: $cron_run->modules_ran
+ Run Status: " . $icons[$cron_run->run_status] . "

+ "); + + # Get the modules that ran + $module_runs = db_query("SELECT * FROM {cron_run_info} WHERE rid = %d", $rid); + $rows = $module_run = $row = $headers = array(); + $time_format = 'h:i:s a'; + while($module_run = db_fetch_object($module_runs)) { + $row = array(); + $row[] = $module_run->module_name; + $row[] = format_date($module_run->run_date, 'custom', $time_format); + $row[] = format_date($module_run->run_stop, 'custom', $time_format); + if($module_run->run_stop) { + $row[] = ($module_run->run_stop - $module_run->run_date) . " seconds"; + } + else { + $row[] = (time() - $module_run->run_date) . " seconds"; + } + $row[] = $icons[$module_run->run_status]; + $rows[] = $row; + } + $headers = array(t('Module'), t('Start'), t('Finish'), t('Run time'), t('Status')); + $form['run_table'] = array('#value' => + theme('table', $headers, $rows)); + + } + else { + $rs = pager_query ("SELECT * FROM {cron_runs}", 20); + while($row = db_fetch_object($rs)) { + $cron_runs[$row->rid] = $row; + } + + $form['cron_runs'] = array('#value' => $cron_runs); + $form['pager_info'] = array('#value' => theme('pager', 20, NULL, 0)); + $form['#theme'] = 'system_cron_runs_logs'; + } + + return $form; + +} + +function theme_system_cron_runs_logs($form) { + + $icons = array(WATCHDOG_NOTICE => theme('image', 'misc/watchdog-ok.png', t('ok'), t('ok')), + WATCHDOG_WARNING => theme('image', 'misc/watchdog-warning.png', t('warning'), t('warning')), + WATCHDOG_ERROR => theme('image', 'misc/watchdog-error.png', t('error'), t('error'))); + $headers = array( + array('field' => 'run_date', 'data' => t('Cron run date')), + array('field' => 'rid', 'data' => t('Run ID')), + array('field' => 'run_information', 'data' => t('Run status')) + ); + $cron_runs = $form['cron_runs']['#value']; + unset($form['cron_runs']); + $row = $rows = array(); + foreach($cron_runs as $rid => $info) { + $link = 'admin/logs/cron/' . $rid; + $row['data'] = array(l(format_date($info->run_date,'long'), $link), l($rid, $link), $icons[$info->run_status]); + $row['class'] = 'ok'; + $row['align'] = 'center'; + $rows[] = $row; + $row = array(); + } + + $output .= drupal_render($form); + $output .= theme('table', $headers, $rows, array('class' => 'system-status-report')); + return $output; + +} + + +/** + * Menu callback; provides the cron basic/advanced settings interface. + * + * A cron run can be setup to run in a "multi-threaded" enviroment. + * Each module will have it's own "report" of how it ran, etc. + * + * @return + * The form array. + */ +function system_cron_settings($form_values = NULL) { + global $base_url; + $form['basic_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Basic Cron Settings'), + '#collapsible' => FALSE); + $form['basic_settings']['cron_run_time'] = array( + '#type' => 'textfield', + '#size' => 5, + '#title' => t('Cron run time'), + '#description' => t('This is the time in seconds that a cron run is alloted to complete a run. Try increasing this value if you notice that + you have a lot of errors of your cron run not finishing.'), + '#default_value' => variable_get('cron_run_time', 240)); + $history_options = drupal_map_assoc(range(50,1000,50)); + $form['basic_settings']['cron_run_history'] = array( + '#type' => 'select', + '#title' => t('Cron history'), + '#options' => $history_options, + '#default_value' => variable_get('cron_run_history', 50), + '#description' => t('The number of cron runs you would like to keep. It will delete starting with the oldest cron run.')); + if($base_url == '') { + drupal_set_message('warning', t('In order to use advanced cron settings, you must set the $base_url variable in your settings.php file.')); + } else { + $form['advanced_cron_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Advanced Cron Settings'), + '#collapsible' => FALSE); + $form['advanced_cron_settings']['cron_run_advanced'] = array( + '#type' => 'checkbox', + '#title' => t('Enable advanced cron runs'), + '#description' => t('Check this box to have your cron runs run in a multi-threaded enviroment. + This will also give you more information about each individual modules cron run.'), + '#default_value' => variable_get('cron_run_advanced', FALSE)); + $form['advanced_cron_settings']['cron_run_command'] = array( + '#type' => 'textfield', + '#title' => t('Cron run command'), + '#description' => t('This is the command you use to run your cron. Use ONLY the command. Example: /usr/bin/lynx -source'), + '#default_value' => variable_get('cron_run_command', "")); + } + + + return system_settings_form($form); +} + /** * Menu callback; provides module enable/disable interface. * @@ -1541,7 +1705,12 @@ function system_run_cron() { // Run cron manually if (drupal_cron_run()) { - drupal_set_message(t('Cron ran successfully')); + if(variable_get('cron_run_advanced', FALSE)) { + drupal_set_message(t('Cron started successfull')); + } + else { + drupal_set_message(t('Cron ran successfully')); + } } else { drupal_set_message(t('Cron run failed')); Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.30 diff -u -r1.30 system.install --- modules/system/system.install 2 Oct 2006 11:31:35 -0000 1.30 +++ modules/system/system.install 5 Oct 2006 00:43:36 -0000 @@ -160,6 +160,25 @@ UNIQUE KEY info (info) ) /*!40100 DEFAULT CHARACTER SET UTF8 */ "); + db_query("CREATE TABLE {cron_runs} ( + rid int(11) NOT NULL auto_increment, + run_date varchar(20), + run_stop varchar(20), + run_ticks int(11), + run_status char(1), + PRIMARY KEY(rid) + ) /*!40100 DEFAULT CHRACTER SET UTF8 */"); + + db_query("CREATE TABLE {cron_run_info} ( + riid int(11) NOT NULL auto_increment, + rid int(11) NOT NULL, + module_name varchar(50), + run_date varchar(20), + run_stop varchar(20), + run_status char(1), + PRIAMRY KEY(riid) + ) /*!40100 DEFAULT CHRACTER SET UTF8 */"); + db_query("CREATE TABLE {cache} ( cid varchar(255) NOT NULL default '', data longblob,