MDL-24751 fixed default memory limit when deciding to use apache_child_terminate...
[moodle.git] / lib / cronlib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Cron functions.
20  *
21  * @package    core
22  * @subpackage admin
23  * @copyright  1999 onwards Martin Dougiamas  http://dougiamas.com
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 function cron_run() {
28     global $DB, $CFG, $OUTPUT;
30     if (CLI_MAINTENANCE) {
31         echo "CLI maintenance mode active, cron execution suspended.\n";
32         exit(1);
33     }
35     if (moodle_needs_upgrading()) {
36         echo "Moodle upgrade pending, cron execution suspended.\n";
37         exit(1);
38     }
40     require_once($CFG->libdir.'/adminlib.php');
41     require_once($CFG->libdir.'/gradelib.php');
43     if (!empty($CFG->showcronsql)) {
44         $DB->set_debug(true);
45     }
46     if (!empty($CFG->showcrondebugging)) {
47         $CFG->debug = DEBUG_DEVELOPER;
48         $CFG->debugdisplay = true;
49     }
51     set_time_limit(0);
52     $starttime = microtime();
54 /// increase memory limit (PHP 5.2 does different calculation, we need more memory now)
55     @raise_memory_limit('128M');
57 /// emulate normal session
58     cron_setup_user();
60 /// Start output log
62     $timenow  = time();
64     mtrace("Server Time: ".date('r',$timenow)."\n\n");
67 /// Session gc
69     mtrace("Cleaning up stale sessions");
70     session_gc();
72 /// Run all cron jobs for each module
74     mtrace("Starting activity modules");
75     get_mailer('buffer');
76     if ($mods = $DB->get_records_select("modules", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
77         foreach ($mods as $mod) {
78             $libfile = "$CFG->dirroot/mod/$mod->name/lib.php";
79             if (file_exists($libfile)) {
80                 include_once($libfile);
81                 $cron_function = $mod->name."_cron";
82                 if (function_exists($cron_function)) {
83                     mtrace("Processing module function $cron_function ...", '');
84                     $pre_dbqueries = null;
85                     $pre_dbqueries = $DB->perf_get_queries();
86                     $pre_time      = microtime(1);
87                     if ($cron_function()) {
88                         $DB->set_field("modules", "lastcron", $timenow, array("id"=>$mod->id));
89                     }
90                     if (isset($pre_dbqueries)) {
91                         mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
92                         mtrace("... used " . (microtime(1) - $pre_time) . " seconds");
93                     }
94                 /// Reset possible changes by modules to time_limit. MDL-11597
95                     @set_time_limit(0);
96                     mtrace("done.");
97                 }
98             }
99         }
100     }
101     get_mailer('close');
102     mtrace("Finished activity modules");
104     mtrace("Starting blocks");
105     if ($blocks = $DB->get_records_select("block", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
106         // we will need the base class.
107         require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
108         foreach ($blocks as $block) {
109             $blockfile = $CFG->dirroot.'/blocks/'.$block->name.'/block_'.$block->name.'.php';
110             if (file_exists($blockfile)) {
111                 require_once($blockfile);
112                 $classname = 'block_'.$block->name;
113                 $blockobj = new $classname;
114                 if (method_exists($blockobj,'cron')) {
115                     mtrace("Processing cron function for ".$block->name.'....','');
116                     if ($blockobj->cron()) {
117                         $DB->set_field('block', 'lastcron', $timenow, array('id'=>$block->id));
118                     }
119                 /// Reset possible changes by blocks to time_limit. MDL-11597
120                     @set_time_limit(0);
121                     mtrace('done.');
122                 }
123             }
125         }
126     }
127     mtrace('Finished blocks');
129     //now do plagiarism checks
130     require_once($CFG->libdir.'/plagiarismlib.php');
131     plagiarism_cron();
133     mtrace("Starting quiz reports");
134     if ($reports = $DB->get_records_select('quiz_report', "cron > 0 AND ((? - lastcron) > cron)", array($timenow))) {
135         foreach ($reports as $report) {
136             $cronfile = "$CFG->dirroot/mod/quiz/report/$report->name/cron.php";
137             if (file_exists($cronfile)) {
138                 include_once($cronfile);
139                 $cron_function = 'quiz_report_'.$report->name."_cron";
140                 if (function_exists($cron_function)) {
141                     mtrace("Processing quiz report cron function $cron_function ...", '');
142                     $pre_dbqueries = null;
143                     $pre_dbqueries = $DB->perf_get_queries();
144                     $pre_time      = microtime(1);
145                     if ($cron_function()) {
146                         $DB->set_field('quiz_report', "lastcron", $timenow, array("id"=>$report->id));
147                     }
148                     if (isset($pre_dbqueries)) {
149                         mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
150                         mtrace("... used " . (microtime(1) - $pre_time) . " seconds");
151                     }
152                     mtrace("done.");
153                 }
154             }
155         }
156     }
157     mtrace("Finished quiz reports");
159     mtrace('Starting admin reports');
160     // Admin reports do not have a database table that lists them. Instead a
161     // report includes cron.php with function report_reportname_cron() if it wishes
162     // to be cronned. It is up to cron.php to handle e.g. if it only needs to
163     // actually do anything occasionally.
164     $reports = get_plugin_list('report');
165     foreach($reports as $report => $reportdir) {
166         $cronfile = $reportdir.'/cron.php';
167         if (file_exists($cronfile)) {
168             require_once($cronfile);
169             $cronfunction = 'report_'.$report.'_cron';
170             mtrace('Processing cron function for '.$report.'...', '');
171             $pre_dbqueries = null;
172             $pre_dbqueries = $DB->perf_get_queries();
173             $pre_time      = microtime(true);
174             $cronfunction();
175             if (isset($pre_dbqueries)) {
176                 mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
177                 mtrace("... used " . round(microtime(true) - $pre_time, 2) . " seconds");
178             }
179             mtrace('done.');
180         }
181     }
182     mtrace('Finished admin reports');
184     mtrace('Starting main gradebook job ...');
185     grade_cron();
186     mtrace('done.');
189     mtrace('Starting processing the event queue...');
190     events_cron();
191     mtrace('done.');
194     if ($CFG->enablecompletion) {
195         // Completion cron
196         mtrace('Starting the completion cron...');
197         require_once($CFG->libdir . '/completion/cron.php');
198         completion_cron();
199         mtrace('done');
200     }
203     if ($CFG->enableportfolios) {
204         // Portfolio cron
205         mtrace('Starting the portfolio cron...');
206         require_once($CFG->libdir . '/portfoliolib.php');
207         portfolio_cron();
208         mtrace('done');
209     }
211 /// Run all core cron jobs, but not every time since they aren't too important.
212 /// These don't have a timer to reduce load, so we'll use a random number
213 /// to randomly choose the percentage of times we should run these jobs.
215     srand ((double) microtime() * 10000000);
216     $random100 = rand(0,100);
218     if ($random100 < 20) {     // Approximately 20% of the time.
219         mtrace("Running clean-up tasks...");
221         /// Delete users who haven't confirmed within required period
223         if (!empty($CFG->deleteunconfirmed)) {
224             $cuttime = $timenow - ($CFG->deleteunconfirmed * 3600);
225             $rs = $DB->get_recordset_sql ("SELECT id, firstname, lastname
226                                              FROM {user}
227                                             WHERE confirmed = 0 AND firstaccess > 0
228                                                   AND firstaccess < ?", array($cuttime));
229             foreach ($rs as $user) {
230                 if ($DB->delete_records('user', array('id'=>$user->id))) {
231                     mtrace("Deleted unconfirmed user for ".fullname($user, true)." ($user->id)");
232                 }
233             }
234             $rs->close();
235         }
236         flush();
239         /// Delete users who haven't completed profile within required period
241         if (!empty($CFG->deleteincompleteusers)) {
242             $cuttime = $timenow - ($CFG->deleteincompleteusers * 3600);
243             $rs = $DB->get_recordset_sql ("SELECT id, username
244                                              FROM {user}
245                                             WHERE confirmed = 1 AND lastaccess > 0
246                                                   AND lastaccess < ? AND deleted = 0
247                                                   AND (lastname = '' OR firstname = '' OR email = '')",
248                                           array($cuttime));
249             foreach ($rs as $user) {
250                 if (delete_user($user)) {
251                     mtrace("Deleted not fully setup user $user->username ($user->id)");
252                 }
253             }
254             $rs->close();
255         }
256         flush();
259         /// Delete old logs to save space (this might need a timer to slow it down...)
261         if (!empty($CFG->loglifetime)) {  // value in days
262             $loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
263             if ($DB->delete_records_select("log", "time < ?", array($loglifetime))) {
264                 mtrace("Deleted old log records");
265             }
266         }
267         flush();
270         /// Delete old cached texts
272         if (!empty($CFG->cachetext)) {   // Defined in config.php
273             $cachelifetime = time() - $CFG->cachetext - 60;  // Add an extra minute to allow for really heavy sites
274             if ($DB->delete_records_select('cache_text', "timemodified < ?", array($cachelifetime))) {
275                 mtrace("Deleted old cache_text records");
276             }
277         }
278         flush();
280         if (!empty($CFG->notifyloginfailures)) {
281             notify_login_failures();
282             mtrace('Notified login failured');
283         }
284         flush();
286         //
287         // generate new password emails for users
288         //
289         mtrace('checking for create_password');
290         if ($DB->count_records('user_preferences', array('name'=>'create_password', 'value'=>'1'))) {
291             mtrace('creating passwords for new users');
292             $newusers = $DB->get_records_sql("SELECT u.id as id, u.email, u.firstname,
293                                                      u.lastname, u.username,
294                                                      p.id as prefid
295                                                 FROM {user} u
296                                                 JOIN {user_preferences} p ON u.id=p.userid
297                                                WHERE p.name='create_password' AND p.value='1' AND u.email !='' ");
299             foreach ($newusers as $newuserid => $newuser) {
300                 $newuser->emailstop = 0; // send email regardless
301                 // email user
302                 if (setnew_password_and_mail($newuser)) {
303                     // remove user pref
304                     $DB->delete_records('user_preferences', array('id'=>$newuser->prefid));
305                 } else {
306                     trigger_error("Could not create and mail new user password!");
307                 }
308             }
309         }
311         if (!empty($CFG->usetags)) {
312             require_once($CFG->dirroot.'/tag/lib.php');
313             tag_cron();
314             mtrace ('Executed tag cron');
315         }
317         // Accesslib stuff
318         cleanup_contexts();
319         mtrace ('Cleaned up contexts');
320         gc_cache_flags();
321         mtrace ('Cleaned cache flags');
322         // If you suspect that the context paths are somehow corrupt
323         // replace the line below with: build_context_path(true);
324         build_context_path();
325         mtrace ('Built context paths');
327         mtrace("Finished clean-up tasks...");
329     } // End of occasional clean-up tasks
331     // Disabled until implemented. MDL-21432, MDL-22184
332     if (1 == 2 && empty($CFG->disablescheduledbackups)) {   // Defined in config.php
333         //Execute backup's cron
334         //Perhaps a long time and memory could help in large sites
335         @set_time_limit(0);
336         @raise_memory_limit("192M");
337         if (file_exists("$CFG->dirroot/backup/backup_scheduled.php") and
338             file_exists("$CFG->dirroot/backup/backuplib.php") and
339             file_exists("$CFG->dirroot/backup/lib.php") and
340             file_exists("$CFG->libdir/blocklib.php")) {
341             include_once("$CFG->dirroot/backup/backup_scheduled.php");
342             include_once("$CFG->dirroot/backup/backuplib.php");
343             include_once("$CFG->dirroot/backup/lib.php");
344             mtrace("Running backups if required...");
346             if (! schedule_backup_cron()) {
347                 mtrace("ERROR: Something went wrong while performing backup tasks!!!");
348             } else {
349                 mtrace("Backup tasks finished.");
350             }
351         }
352     }
354 /// Run the auth cron, if any
355 /// before enrolments because it might add users that will be needed in enrol plugins
356     $auths = get_enabled_auth_plugins();
358     mtrace("Running auth crons if required...");
359     foreach ($auths as $auth) {
360         $authplugin = get_auth_plugin($auth);
361         if (method_exists($authplugin, 'cron')) {
362             mtrace("Running cron for auth/$auth...");
363             $authplugin->cron();
364             if (!empty($authplugin->log)) {
365                 mtrace($authplugin->log);
366             }
367         }
368         unset($authplugin);
369     }
371     mtrace("Running enrol crons if required...");
372     $enrols = enrol_get_plugins(true);
373     foreach($enrols as $ename=>$enrol) {
374         // do this for all plugins, disabled plugins might want to cleanup stuff such as roles
375         if (!$enrol->is_cron_required()) {
376             continue;
377         }
378         mtrace("Running cron for enrol_$ename...");
379         $enrol->cron();
380         $enrol->set_config('lastcron', time());
381     }
383     if (!empty($CFG->enablestats) and empty($CFG->disablestatsprocessing)) {
384         require_once($CFG->dirroot.'/lib/statslib.php');
385         // check we're not before our runtime
386         $timetocheck = stats_get_base_daily() + $CFG->statsruntimestarthour*60*60 + $CFG->statsruntimestartminute*60;
388         if (time() > $timetocheck) {
389             // process configured number of days as max (defaulting to 31)
390             $maxdays = empty($CFG->statsruntimedays) ? 31 : abs($CFG->statsruntimedays);
391             if (stats_cron_daily($maxdays)) {
392                 if (stats_cron_weekly()) {
393                     if (stats_cron_monthly()) {
394                         stats_clean_old();
395                     }
396                 }
397             }
398             @set_time_limit(0);
399         } else {
400             mtrace('Next stats run after:'. userdate($timetocheck));
401         }
402     }
404     // run gradebook import/export/report cron
405     if ($gradeimports = get_plugin_list('gradeimport')) {
406         foreach ($gradeimports as $gradeimport => $plugindir) {
407             if (file_exists($plugindir.'/lib.php')) {
408                 require_once($plugindir.'/lib.php');
409                 $cron_function = 'grade_import_'.$gradeimport.'_cron';
410                 if (function_exists($cron_function)) {
411                     mtrace("Processing gradebook import function $cron_function ...", '');
412                     $cron_function();
413                 }
414             }
415         }
416     }
418     if ($gradeexports = get_plugin_list('gradeexport')) {
419         foreach ($gradeexports as $gradeexport => $plugindir) {
420             if (file_exists($plugindir.'/lib.php')) {
421                 require_once($plugindir.'/lib.php');
422                 $cron_function = 'grade_export_'.$gradeexport.'_cron';
423                 if (function_exists($cron_function)) {
424                     mtrace("Processing gradebook export function $cron_function ...", '');
425                     $cron_function();
426                 }
427             }
428         }
429     }
431     if ($gradereports = get_plugin_list('gradereport')) {
432         foreach ($gradereports as $gradereport => $plugindir) {
433             if (file_exists($plugindir.'/lib.php')) {
434                 require_once($plugindir.'/lib.php');
435                 $cron_function = 'grade_report_'.$gradereport.'_cron';
436                 if (function_exists($cron_function)) {
437                     mtrace("Processing gradebook report function $cron_function ...", '');
438                     $cron_function();
439                 }
440             }
441         }
442     }
444     // Run external blog cron if needed
445     if ($CFG->useexternalblogs) {
446         require_once($CFG->dirroot . '/blog/lib.php');
447         mtrace("Fetching external blog entries...", '');
448         $sql = "timefetched < ? OR timefetched = 0";
449         $externalblogs = $DB->get_records_select('blog_external', $sql, array(mktime() - $CFG->externalblogcrontime));
451         foreach ($externalblogs as $eb) {
452             blog_sync_external_entries($eb);
453         }
454     }
456     // Run blog associations cleanup
457     if ($CFG->useblogassociations) {
458         require_once($CFG->dirroot . '/blog/lib.php');
459         // delete entries whose contextids no longer exists
460         mtrace("Deleting blog associations linked to non-existent contexts...", '');
461         $DB->delete_records_select('blog_association', 'contextid NOT IN (SELECT id FROM {context})');
462     }
464     //Run registration updated cron
465     mtrace(get_string('siteupdatesstart', 'hub'));
466     require_once($CFG->dirroot . '/admin/registration/lib.php');
467     $registrationmanager = new registration_manager();
468     $registrationmanager->cron();
469     mtrace(get_string('siteupdatesend', 'hub'));
471     // cleanup file trash
472     $fs = get_file_storage();
473     $fs->cron();
475     //cleanup old session linked tokens
476     //deletes the session linked tokens that are over a day old.
477     mtrace("Deleting session linked tokens more than one day old...", '');
478     $DB->delete_records_select('external_tokens', 'lastaccess < :onedayago AND tokentype = :tokentype',
479                     array('onedayago' => time() - DAYSECS, 'tokentype' => EXTERNAL_TOKEN_EMBEDDED));
480     mtrace('done.');
482     // run any customized cronjobs, if any
483     if ($locals = get_plugin_list('local')) {
484         mtrace('Processing customized cron scripts ...', '');
485         foreach ($locals as $local => $localdir) {
486             if (file_exists("$localdir/cron.php")) {
487                 include("$localdir/cron.php");
488             }
489         }
490         mtrace('done.');
491     }
494     mtrace("Cron script completed correctly");
496     $difftime = microtime_diff($starttime, microtime());
497     mtrace("Execution took ".$difftime." seconds");