diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c index ed4f8d5ca1c609fba1ffb56d5dae720bf6114dbc..0f1b977fa01cd7357960dd9467699a6e4d666506 100644 --- a/apps/app_confbridge.c +++ b/apps/app_confbridge.c @@ -1757,6 +1757,12 @@ static int confbridge_exec(struct ast_channel *chan, const char *data) /* if we're shutting down, don't attempt to do further processing */ if (ast_shutting_down()) { + /* + * Not taking any new calls at this time. We cannot create + * the announcer channel if this is the first channel into + * the conference and we certainly cannot create any + * recording channel. + */ leave_conference(&user); conference = NULL; goto confbridge_cleanup; diff --git a/channels/chan_sip.c b/channels/chan_sip.c index a41a8aeac399780704f77daba2d398a8c2e2c38d..43dc6afe63b6fdb2cbf53a2d2c081497229e03f3 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -24854,6 +24854,10 @@ static int handle_request_options(struct sip_pvt *p, struct sip_request *req, st ast_string_field_set(p, context, sip_cfg.default_context); if (ast_shutting_down()) { + /* + * Not taking any new calls at this time. + * Likely a server availability OPTIONS poll. + */ msg = "503 Unavailable"; } else { msg = "404 Not Found"; diff --git a/include/asterisk.h b/include/asterisk.h index ee1a9c337c1f9365ecd50e85cd8b1434cf326ad3..edb100b6befaca492fdb798b34531aa3aa25e5da 100644 --- a/include/asterisk.h +++ b/include/asterisk.h @@ -111,6 +111,42 @@ int ast_register_cleanup(void (*func)(void)); */ void ast_unregister_atexit(void (*func)(void)); +/*! + * \brief Cancel an existing shutdown and return to normal operation. + * + * \note Shutdown can be cancelled while the server is waiting for + * any existing channels to be destroyed before shutdown becomes + * irreversible. + * + * \return non-zero if shutdown cancelled. + */ +int ast_cancel_shutdown(void); + +/*! + * \details + * The server is preventing new channel creation in preparation for + * shutdown and may actively be releasing resources. The shutdown + * process may be canceled by ast_cancel_shutdown() if it is not too + * late. + * + * \note The preparation to shutdown phase can be quite lengthy + * if we are gracefully shutting down. How long existing calls will + * last is not up to us. + * + * \return non-zero if the server is preparing to or actively shutting down. + */ +int ast_shutting_down(void); + +/*! + * \return non-zero if the server is actively shutting down. + * \since 13.3.0 + * + * \details + * The server is releasing resources and unloading modules. + * It won't be long now. + */ +int ast_shutdown_final(void); + #if !defined(LOW_MEMORY) /*! * \brief Register the version of a source code file with the core. diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 595c91da2e99973166783ee7bde13e0bdff4941f..b6755e256fb40ab0fa2c88186de3ae708926a287 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -1510,6 +1510,14 @@ const struct ast_channel_tech *ast_get_channel_tech(const char *name); */ void ast_hangup(struct ast_channel *chan); +/*! + * \brief Soft hangup all active channels. + * \since 13.3.0 + * + * \return Nothing + */ +void ast_softhangup_all(void); + /*! * \brief Softly hangup up a channel * @@ -2203,23 +2211,12 @@ int ast_channel_defer_dtmf(struct ast_channel *chan); /*! Undo defer. ast_read will return any DTMF characters that were queued */ void ast_channel_undefer_dtmf(struct ast_channel *chan); -/*! Initiate system shutdown -- prevents new channels from being allocated. - * \param hangup If "hangup" is non-zero, all existing channels will receive soft - * hangups */ -void ast_begin_shutdown(int hangup); - -/*! Cancels an existing shutdown and returns to normal operation */ -void ast_cancel_shutdown(void); - /*! \return number of channels available for lookup */ int ast_active_channels(void); /*! \return the number of channels not yet destroyed */ int ast_undestroyed_channels(void); -/*! \return non-zero if Asterisk is being shut down */ -int ast_shutting_down(void); - /*! Activate a given generator */ int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params); diff --git a/main/asterisk.c b/main/asterisk.c index 1e5829616b0161f63d5800fda63b3f4c14a2bd2f..1817eefd8d13a5aa68ee4ea27b7a21b50013251b 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -432,16 +432,34 @@ static char ast_config_AST_CTL[PATH_MAX] = "asterisk.ctl"; extern unsigned int ast_FD_SETSIZE; static char *_argv[256]; + typedef enum { - NOT_SHUTTING_DOWN = -2, - SHUTTING_DOWN = -1, - /* Valid values for quit_handler niceness below: */ + /*! Normal operation */ + NOT_SHUTTING_DOWN, + /*! Committed to shutting down. Final phase */ + SHUTTING_DOWN_FINAL, + /*! Committed to shutting down. Initial phase */ + SHUTTING_DOWN, + /*! + * Valid values for quit_handler() niceness below. + * These shutdown/restart levels can be cancelled. + * + * Remote console exit right now + */ SHUTDOWN_FAST, + /*! core stop/restart now */ SHUTDOWN_NORMAL, + /*! core stop/restart gracefully */ SHUTDOWN_NICE, + /*! core stop/restart when convenient */ SHUTDOWN_REALLY_NICE } shutdown_nice_t; + static shutdown_nice_t shuttingdown = NOT_SHUTTING_DOWN; + +/*! Prevent new channel allocation for shutdown. */ +static int shutdown_pending; + static int restartnow; static pthread_t consolethread = AST_PTHREADT_NULL; static pthread_t mon_sig_flags; @@ -1867,6 +1885,43 @@ int ast_set_priority(int pri) return 0; } +int ast_shutdown_final(void) +{ + return shuttingdown == SHUTTING_DOWN_FINAL; +} + +int ast_shutting_down(void) +{ + return shutdown_pending; +} + +int ast_cancel_shutdown(void) +{ + int shutdown_aborted = 0; + + ast_mutex_lock(&safe_system_lock); + if (shuttingdown >= SHUTDOWN_FAST) { + shuttingdown = NOT_SHUTTING_DOWN; + shutdown_pending = 0; + shutdown_aborted = 1; + } + ast_mutex_unlock(&safe_system_lock); + return shutdown_aborted; +} + +/*! + * \internal + * \brief Initiate system shutdown -- prevents new channels from being allocated. + */ +static void ast_begin_shutdown(void) +{ + ast_mutex_lock(&safe_system_lock); + if (shuttingdown != NOT_SHUTTING_DOWN) { + shutdown_pending = 1; + } + ast_mutex_unlock(&safe_system_lock); +} + static int can_safely_quit(shutdown_nice_t niceness, int restart); static void really_quit(int num, shutdown_nice_t niceness, int restart); @@ -1879,8 +1934,53 @@ static void quit_handler(int num, shutdown_nice_t niceness, int restart) /* It wasn't our time. */ } +#define SHUTDOWN_TIMEOUT 15 /* Seconds */ + +/*! + * \internal + * \brief Wait for all channels to die, a timeout, or shutdown cancelled. + * \since 13.3.0 + * + * \param niceness Shutdown niceness in effect + * \param seconds Number of seconds to wait or less than zero if indefinitely. + * + * \retval zero if waiting wasn't necessary. We were idle. + * \retval non-zero if we had to wait. + */ +static int wait_for_channels_to_die(shutdown_nice_t niceness, int seconds) +{ + time_t start; + time_t now; + int waited = 0; + + time(&start); + for (;;) { + if (!ast_undestroyed_channels() || shuttingdown != niceness) { + break; + } + if (seconds < 0) { + /* No timeout so just poll every second */ + sleep(1); + } else { + time(&now); + + /* Wait up to the given seconds for all channels to go away */ + if (seconds < (now - start)) { + break; + } + + /* Sleep 1/10 of a second */ + usleep(100000); + } + waited = 1; + } + return waited; +} + static int can_safely_quit(shutdown_nice_t niceness, int restart) { + int waited = 0; + /* Check if someone else isn't already doing this. */ ast_mutex_lock(&safe_system_lock); if (shuttingdown != NOT_SHUTTING_DOWN && niceness >= shuttingdown) { @@ -1897,40 +1997,30 @@ static int can_safely_quit(shutdown_nice_t niceness, int restart) * the atexit handlers, otherwise this would be a bit early. */ ast_cdr_engine_term(); - /* Shutdown the message queue for the technology agnostic message channel. - * This has to occur before we pause shutdown pending ast_undestroyed_channels. */ + /* + * Shutdown the message queue for the technology agnostic message channel. + * This has to occur before we pause shutdown pending ast_undestroyed_channels. + * + * XXX This is not reversed on shutdown cancel. + */ ast_msg_shutdown(); if (niceness == SHUTDOWN_NORMAL) { - time_t s, e; /* Begin shutdown routine, hanging up active channels */ - ast_begin_shutdown(1); + ast_begin_shutdown(); if (ast_opt_console) { ast_verb(0, "Beginning asterisk %s....\n", restart ? "restart" : "shutdown"); } - time(&s); - for (;;) { - time(&e); - /* Wait up to 15 seconds for all channels to go away */ - if ((e - s) > 15 || !ast_undestroyed_channels() || shuttingdown != niceness) { - break; - } - /* Sleep 1/10 of a second */ - usleep(100000); - } + ast_softhangup_all(); + waited |= wait_for_channels_to_die(niceness, SHUTDOWN_TIMEOUT); } else if (niceness >= SHUTDOWN_NICE) { if (niceness != SHUTDOWN_REALLY_NICE) { - ast_begin_shutdown(0); + ast_begin_shutdown(); } if (ast_opt_console) { ast_verb(0, "Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt"); } - for (;;) { - if (!ast_undestroyed_channels() || shuttingdown != niceness) { - break; - } - sleep(1); - } + waited |= wait_for_channels_to_die(niceness, -1); } /* Re-acquire lock and check if someone changed the niceness, in which @@ -1944,9 +2034,28 @@ static int can_safely_quit(shutdown_nice_t niceness, int restart) ast_mutex_unlock(&safe_system_lock); return 0; } - shuttingdown = SHUTTING_DOWN; + + if (niceness >= SHUTDOWN_REALLY_NICE) { + shuttingdown = SHUTTING_DOWN; + ast_mutex_unlock(&safe_system_lock); + + /* No more Mr. Nice guy. We are committed to shutting down now. */ + ast_begin_shutdown(); + ast_softhangup_all(); + waited |= wait_for_channels_to_die(SHUTTING_DOWN, SHUTDOWN_TIMEOUT); + + ast_mutex_lock(&safe_system_lock); + } + shuttingdown = SHUTTING_DOWN_FINAL; ast_mutex_unlock(&safe_system_lock); + if (niceness >= SHUTDOWN_NORMAL && waited) { + /* + * We were not idle. Give things in progress a chance to + * recognize the final shutdown phase. + */ + sleep(1); + } return 1; } @@ -2454,8 +2563,6 @@ static char *handle_restart_when_convenient(struct ast_cli_entry *e, int cmd, st static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - int aborting_shutdown = 0; - switch (cmd) { case CLI_INIT: e->command = "core abort shutdown"; @@ -2471,16 +2578,8 @@ static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_ if (a->argc != e->args) return CLI_SHOWUSAGE; - ast_mutex_lock(&safe_system_lock); - if (shuttingdown >= SHUTDOWN_FAST) { - aborting_shutdown = 1; - shuttingdown = NOT_SHUTTING_DOWN; - } - ast_mutex_unlock(&safe_system_lock); + ast_cancel_shutdown(); - if (aborting_shutdown) { - ast_cancel_shutdown(); - } return CLI_SUCCESS; } diff --git a/main/bridge.c b/main/bridge.c index c1fc145c6ed985a30551dc491f004ebde1a70608..6737fa62f53a0e1aa5d19def4e4c0db996a38cbf 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -5322,7 +5322,7 @@ static void bridge_prnt_obj(void *v_obj, void *where, ao2_prnt_fn *prnt) /*! * \internal - * \brief Shutdown the bridging system. + * \brief Shutdown the bridging system. Stuff to always do. * \since 12.0.0 * * \return Nothing @@ -5334,6 +5334,17 @@ static void bridge_shutdown(void) ast_manager_unregister("BridgeTechnologyUnsuspend"); ast_cli_unregister_multiple(bridge_cli, ARRAY_LEN(bridge_cli)); ao2_container_unregister("bridges"); +} + +/*! + * \internal + * \brief Shutdown the bridging system. More stuff to do on graceful shutdown. + * \since 13.3.0 + * + * \return Nothing + */ +static void bridge_cleanup(void) +{ ao2_cleanup(bridges); bridges = NULL; ao2_cleanup(bridge_manager); @@ -5342,6 +5353,7 @@ static void bridge_shutdown(void) int ast_bridging_init(void) { + ast_register_cleanup(bridge_cleanup); ast_register_atexit(bridge_shutdown); if (ast_stasis_bridging_init()) { diff --git a/main/channel.c b/main/channel.c index aab8e228c0be42b39f4f39eb756e519a74d05af7..f8ae442b93032463ebf4e4b7e5cf4b804d698d06 100644 --- a/main/channel.c +++ b/main/channel.c @@ -100,9 +100,6 @@ struct ast_epoll_data { #define MONITOR_DELAY 150 * 8 /*!< 150 ms of MONITORING DELAY */ #endif -/*! \brief Prevent new channel allocation if shutting down. */ -static int shutting_down; - static int chancount; unsigned long global_fin, global_fout; @@ -507,13 +504,9 @@ static int ast_channel_softhangup_cb(void *obj, void *arg, int flags) return 0; } -void ast_begin_shutdown(int hangup) +void ast_softhangup_all(void) { - shutting_down = 1; - - if (hangup) { - ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL); - } + ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL); } /*! \brief returns number of active/allocated channels */ @@ -527,18 +520,6 @@ int ast_undestroyed_channels(void) return ast_atomic_fetchadd_int(&chancount, 0); } -/*! \brief Cancel a shutdown in progress */ -void ast_cancel_shutdown(void) -{ - shutting_down = 0; -} - -/*! \brief Returns non-zero if Asterisk is being shut down */ -int ast_shutting_down(void) -{ - return shutting_down; -} - /*! \brief Set when to hangup channel */ void ast_channel_setwhentohangup_tv(struct ast_channel *chan, struct timeval offset) { diff --git a/main/http.c b/main/http.c index ef3b4b22ba4e8be48f392c7c6ff123bfe19a3145..3346cadb4015e1d2b757255eef7f715eee1e469d 100644 --- a/main/http.c +++ b/main/http.c @@ -1873,6 +1873,11 @@ static int httpd_process_request(struct ast_tcptls_session_instance *ser) return -1; } + if (ast_shutdown_final()) { + ast_http_error(ser, 503, "Service Unavailable", "Shutdown in progress"); + return -1; + } + /* process "Request Headers" lines */ if (http_request_headers_get(ser, &headers)) { return -1; diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c index cf55f4dd64f093ef4bf2d3951d8743438f447e27..44f33f321c5ffe7eccb78a75a03271f8b877dcfd 100644 --- a/res/res_pjsip/pjsip_options.c +++ b/res/res_pjsip/pjsip_options.c @@ -655,6 +655,10 @@ static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten)); if (ast_shutting_down()) { + /* + * Not taking any new calls at this time. + * Likely a server availability OPTIONS poll. + */ send_options_response(rdata, 503); } else if (!ast_strlen_zero(exten) && !ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) { send_options_response(rdata, 404); diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c index 5047184f83698217c0f9b92365b8de4c9cc9c166..4d53091b9b2570e360ffa84644d05a79edbbab9f 100644 --- a/res/res_pjsip_pubsub.c +++ b/res/res_pjsip_pubsub.c @@ -2059,7 +2059,7 @@ static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int forc pjsip_evsub *evsub = sub_tree->evsub; pjsip_tx_data *tdata; - if (ast_shutting_down() + if (ast_shutdown_final() && sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED && sub_tree->persistence) { return 0;