Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
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 // Custom stuff first. 152 foreach (self::$callbacks as $data) { 153 list($callback, $params) = $data; 154 try { 155 call_user_func_array($callback, $params); 156 } catch (Throwable $e) { 157 // phpcs:ignore 158 error_log('Exception ignored in shutdown function '.get_callable_name($callback).': '.$e->getMessage()); 159 } 160 } 161 162 // Handle DB transactions, session need to be written afterwards 163 // in order to maintain consistency in all session handlers. 164 if ($DB->is_transaction_started()) { 165 if (!defined('PHPUNIT_TEST') or !PHPUNIT_TEST) { 166 // This should not happen, it usually indicates wrong catching of exceptions, 167 // because all transactions should be finished manually or in default exception handler. 168 $backtrace = $DB->get_transaction_start_backtrace(); 169 error_log('Potential coding error - active database transaction detected during request shutdown:'."\n".format_backtrace($backtrace, true)); 170 } 171 $DB->force_transaction_rollback(); 172 } 173 174 // Close sessions - do it here to make it consistent for all session handlers. 175 \core\session\manager::write_close(); 176 177 // Other cleanup. 178 self::request_shutdown(); 179 180 // Stop profiling. 181 if (function_exists('profiling_is_running')) { 182 if (profiling_is_running()) { 183 profiling_stop(); 184 } 185 } 186 187 // NOTE: do not dispose $DB and MUC here, they might be used from legacy shutdown functions. 188 } 189 190 /** 191 * Standard shutdown sequence. 192 */ 193 protected static function request_shutdown() { 194 global $CFG; 195 196 // Help apache server if possible. 197 $apachereleasemem = false; 198 if (function_exists('apache_child_terminate') && function_exists('memory_get_usage') && ini_get_bool('child_terminate')) { 199 $limit = (empty($CFG->apachemaxmem) ? 64*1024*1024 : $CFG->apachemaxmem); // 64MB default. 200 if (memory_get_usage() > get_real_size($limit)) { 201 $apachereleasemem = $limit; 202 @apache_child_terminate(); 203 } 204 } 205 206 // Deal with perf logging. 207 if ((defined('MDL_PERF') && MDL_PERF) || (!empty($CFG->perfdebug) && $CFG->perfdebug > 7)) { 208 if ($apachereleasemem) { 209 error_log('Mem usage over '.$apachereleasemem.': marking Apache child for reaping.'); 210 } 211 if (defined('MDL_PERFTOLOG') && MDL_PERFTOLOG) { 212 $perf = get_performance_info(); 213 error_log("PERF: " . $perf['txt']); 214 } 215 if (defined('MDL_PERFINC') && MDL_PERFINC) { 216 $inc = get_included_files(); 217 $ts = 0; 218 foreach ($inc as $f) { 219 if (preg_match(':^/:', $f)) { 220 $fs = filesize($f); 221 $ts += $fs; 222 $hfs = display_size($fs); 223 error_log(substr($f, strlen($CFG->dirroot)) . " size: $fs ($hfs)", null, null, 0); 224 } else { 225 error_log($f , null, null, 0); 226 } 227 } 228 if ($ts > 0 ) { 229 $hts = display_size($ts); 230 error_log("Total size of files included: $ts ($hts)"); 231 } 232 } 233 } 234 } 235 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body