See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Shutdown management class. 19 * 20 * @package core 21 * @copyright 2013 Petr Skoda {@link http://skodak.org} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 27 /** 28 * Shutdown management class. 29 * 30 * @package core 31 * @copyright 2013 Petr Skoda {@link http://skodak.org} 32 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 */ 34 class core_shutdown_manager { 35 /** @var array list of custom callbacks */ 36 protected static $callbacks = []; 37 /** @var array list of custom signal callbacks */ 38 protected static $signalcallbacks = []; 39 /** @var bool is this manager already registered? */ 40 protected static $registered = false; 41 42 /** 43 * Register self as main shutdown handler. 44 * 45 * @private to be called from lib/setup.php only! 46 */ 47 public static function initialize() { 48 if (self::$registered) { 49 debugging('Shutdown manager is already initialised!'); 50 } 51 self::$registered = true; 52 register_shutdown_function(array('core_shutdown_manager', 'shutdown_handler')); 53 54 // Signal handlers should only be used when dealing with a CLI script. 55 // In the case of PHP called in a web server the server is the owning process and should handle the signal chain 56 // properly itself. 57 // The 'pcntl' extension is optional and not available on Windows. 58 if (CLI_SCRIPT && extension_loaded('pcntl') && function_exists('pcntl_async_signals')) { 59 // We capture and handle SIGINT (Ctrl+C) and SIGTERM (termination requested). 60 pcntl_async_signals(true); 61 pcntl_signal(SIGINT, ['core_shutdown_manager', 'signal_handler']); 62 pcntl_signal(SIGTERM, ['core_shutdown_manager', 'signal_handler']); 63 } 64 } 65 66 /** 67 * Signal handler for SIGINT, and SIGTERM. 68 * 69 * @param int $signo The signal being handled 70 */ 71 public static function signal_handler(int $signo) { 72 // Note: There is no need to manually call the shutdown handler. 73 // The fact that we are calling exit() in this script means that the standard shutdown handling is performed 74 // anyway. 75 switch ($signo) { 76 case SIGTERM: 77 // Replicate native behaviour. 78 echo "Terminated: {$signo}\n"; 79 80 // The standard exit code for SIGTERM is 143. 81 $exitcode = 143; 82 break; 83 case SIGINT: 84 // Replicate native behaviour. 85 echo "\n"; 86 87 // The standard exit code for SIGINT (Ctrl+C) is 130. 88 $exitcode = 130; 89 break; 90 default: 91 // The signal handler was called with a signal it was not expecting. 92 // We should exit and complain. 93 echo "Warning: \core_shutdown_manager::signal_handler() was called with an unexpected signal ({$signo}).\n"; 94 $exitcode = 1; 95 } 96 97 // Normally we should exit unless a callback tells us to wait. 98 $shouldexit = true; 99 foreach (self::$signalcallbacks as $data) { 100 list($callback, $params) = $data; 101 try { 102 array_unshift($params, $signo); 103 $shouldexit = call_user_func_array($callback, $params) && $shouldexit; 104 } catch (Throwable $e) { 105 // phpcs:ignore 106 error_log('Exception ignored in signal function ' . get_callable_name($callback) . ': ' . $e->getMessage()); 107 } 108 } 109 110 if ($shouldexit) { 111 exit ($exitcode); 112 } 113 } 114 115 /** 116 * Register custom signal handler function. 117 * 118 * If a handler returns false the signal will be ignored. 119 * 120 * @param callable $callback 121 * @param array $params 122 * @return void 123 */ 124 public static function register_signal_handler($callback, array $params = null): void { 125 if (!is_callable($callback)) { 126 error_log('Invalid custom signal function detected ' . var_export($callback, true)); // phpcs:ignore 127 } 128 self::$signalcallbacks[] = [$callback, $params ?? []]; 129 } 130 131 /** 132 * Register custom shutdown function. 133 * 134 * @param callable $callback 135 * @param array $params 136 * @return void 137 */ 138 public static function register_function($callback, array $params = null): void { 139 if (!is_callable($callback)) { 140 error_log('Invalid custom shutdown function detected '.var_export($callback, true)); // phpcs:ignore 141 } 142 self::$callbacks[] = [$callback, $params ? array_values($params) : []]; 143 } 144 145 /** 146 * @private - do NOT call directly. 147 */ 148 public static function shutdown_handler() { 149 global $DB; 150 151 // Always ensure we know who the user is in access logs even if they 152 // were logged in a weird way midway through the request. 153 set_access_log_user(); 154 155 // Custom stuff first. 156 foreach (self::$callbacks as $data) { 157 list($callback, $params) = $data; 158 try { 159 call_user_func_array($callback, $params); 160 } catch (Throwable $e) { 161 // phpcs:ignore 162 error_log('Exception ignored in shutdown function '.get_callable_name($callback).': '.$e->getMessage()); 163 } 164 } 165 166 // Handle DB transactions, session need to be written afterwards 167 // in order to maintain consistency in all session handlers. 168 if ($DB->is_transaction_started()) { 169 if (!defined('PHPUNIT_TEST') or !PHPUNIT_TEST) { 170 // This should not happen, it usually indicates wrong catching of exceptions, 171 // because all transactions should be finished manually or in default exception handler. 172 $backtrace = $DB->get_transaction_start_backtrace(); 173 error_log('Potential coding error - active database transaction detected during request shutdown:'."\n".format_backtrace($backtrace, true)); 174 } 175 $DB->force_transaction_rollback(); 176 } 177 178 // Close sessions - do it here to make it consistent for all session handlers. 179 \core\session\manager::write_close(); 180 181 // Other cleanup. 182 self::request_shutdown(); 183 184 // Stop profiling. 185 if (function_exists('profiling_is_running')) { 186 if (profiling_is_running()) { 187 profiling_stop(); 188 } 189 } 190 191 // NOTE: do not dispose $DB and MUC here, they might be used from legacy shutdown functions. 192 } 193 194 /** 195 * Standard shutdown sequence. 196 */ 197 protected static function request_shutdown() { 198 global $CFG; 199 200 // Help apache server if possible. 201 $apachereleasemem = false; 202 if (function_exists('apache_child_terminate') && function_exists('memory_get_usage') && ini_get_bool('child_terminate')) { 203 $limit = (empty($CFG->apachemaxmem) ? 64*1024*1024 : $CFG->apachemaxmem); // 64MB default. 204 if (memory_get_usage() > get_real_size($limit)) { 205 $apachereleasemem = $limit; 206 @apache_child_terminate(); 207 } 208 } 209 210 // Deal with perf logging. 211 if ((defined('MDL_PERF') && MDL_PERF) || (!empty($CFG->perfdebug) && $CFG->perfdebug > 7)) { 212 if ($apachereleasemem) { 213 error_log('Mem usage over '.$apachereleasemem.': marking Apache child for reaping.'); 214 } 215 if (defined('MDL_PERFTOLOG') && MDL_PERFTOLOG) { 216 $perf = get_performance_info(); 217 error_log("PERF: " . $perf['txt']); 218 } 219 if (defined('MDL_PERFINC') && MDL_PERFINC) { 220 $inc = get_included_files(); 221 $ts = 0; 222 foreach ($inc as $f) { 223 if (preg_match(':^/:', $f)) { 224 $fs = filesize($f); 225 $ts += $fs; 226 $hfs = display_size($fs); 227 error_log(substr($f, strlen($CFG->dirroot)) . " size: $fs ($hfs)", null, null, 0); 228 } else { 229 error_log($f , null, null, 0); 230 } 231 } 232 if ($ts > 0 ) { 233 $hts = display_size($ts); 234 error_log("Total size of files included: $ts ($hts)"); 235 } 236 } 237 } 238 } 239 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body