mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +00:00 
			
		
		
		
	Allow only one thread at a time to do asterisk cleanup/shutdown.
Add locking around the really-really-quit part of the core stop/restart part. Previously more than one thread could be called to do cleanup, causing atexit handlers to be run multiple times, in turn causing segfaults. (issue ASTERISK-18883) Reviewed by: Terry Wilson Review: https://reviewboard.asterisk.org/r/1662/ Review: https://reviewboard.asterisk.org/r/1658/ git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.8@350888 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		
							
								
								
									
										191
									
								
								main/asterisk.c
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								main/asterisk.c
									
									
									
									
									
								
							| @@ -275,7 +275,16 @@ static char ast_config_AST_CTL[PATH_MAX] = "asterisk.ctl"; | ||||
| extern unsigned int ast_FD_SETSIZE; | ||||
|  | ||||
| static char *_argv[256]; | ||||
| static int shuttingdown; | ||||
| typedef enum { | ||||
| 	NOT_SHUTTING_DOWN = -2, | ||||
| 	SHUTTING_DOWN = -1, | ||||
| 	/* Valid values for quit_handler niceness below: */ | ||||
| 	SHUTDOWN_FAST, | ||||
| 	SHUTDOWN_NORMAL, | ||||
| 	SHUTDOWN_NICE, | ||||
| 	SHUTDOWN_REALLY_NICE | ||||
| } shutdown_nice_t; | ||||
| static shutdown_nice_t shuttingdown = NOT_SHUTTING_DOWN; | ||||
| static int restartnow; | ||||
| static pthread_t consolethread = AST_PTHREADT_NULL; | ||||
| static pthread_t mon_sig_flags; | ||||
| @@ -1607,57 +1616,92 @@ static void ast_run_atexits(void) | ||||
| 	AST_RWLIST_UNLOCK(&atexits); | ||||
| } | ||||
|  | ||||
| static void quit_handler(int num, int niceness, int safeshutdown, int restart) | ||||
| static int can_safely_quit(shutdown_nice_t niceness, int restart); | ||||
| static void really_quit(int num, shutdown_nice_t niceness, int restart); | ||||
|  | ||||
| static void quit_handler(int num, shutdown_nice_t niceness, int restart) | ||||
| { | ||||
| 	char filename[80] = ""; | ||||
| 	time_t s,e; | ||||
| 	int x; | ||||
| 	/* Try to get as many CDRs as possible submitted to the backend engines (if in batch mode) */ | ||||
| 	ast_cdr_engine_term(); | ||||
| 	if (safeshutdown) { | ||||
| 		shuttingdown = 1; | ||||
| 		if (!niceness) { | ||||
| 			/* Begin shutdown routine, hanging up active channels */ | ||||
| 			ast_begin_shutdown(1); | ||||
| 			if (option_verbose && ast_opt_console) | ||||
| 				ast_verbose("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) | ||||
| 					break; | ||||
| 				if (!ast_active_channels()) | ||||
| 					break; | ||||
| 				if (!shuttingdown) | ||||
| 					break; | ||||
| 				/* Sleep 1/10 of a second */ | ||||
| 				usleep(100000); | ||||
| 			} | ||||
| 		} else { | ||||
| 			if (niceness < 2) | ||||
| 				ast_begin_shutdown(0); | ||||
| 			if (option_verbose && ast_opt_console) | ||||
| 				ast_verbose("Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt"); | ||||
| 			for (;;) { | ||||
| 				if (!ast_active_channels()) | ||||
| 					break; | ||||
| 				if (!shuttingdown) | ||||
| 					break; | ||||
| 				sleep(1); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (!shuttingdown) { | ||||
| 			if (option_verbose && ast_opt_console) | ||||
| 				ast_verbose("Asterisk %s cancelled.\n", restart ? "restart" : "shutdown"); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		if (niceness) | ||||
| 			ast_module_shutdown(); | ||||
| 	if (can_safely_quit(niceness, restart)) { | ||||
| 		really_quit(num, niceness, restart); | ||||
| 		/* No one gets here. */ | ||||
| 	} | ||||
| 	/* It wasn't our time. */ | ||||
| } | ||||
|  | ||||
| static int can_safely_quit(shutdown_nice_t niceness, int restart) | ||||
| { | ||||
| 	/* Check if someone else isn't already doing this. */ | ||||
| 	ast_mutex_lock(&safe_system_lock); | ||||
| 	if (shuttingdown != NOT_SHUTTING_DOWN && niceness >= shuttingdown) { | ||||
| 		/* Already in progress and other request was less nice. */ | ||||
| 		ast_mutex_unlock(&safe_system_lock); | ||||
| 		ast_verbose("Ignoring asterisk %s request, already in progress.\n", restart ? "restart" : "shutdown"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	shuttingdown = niceness; | ||||
| 	ast_mutex_unlock(&safe_system_lock); | ||||
|  | ||||
| 	/* Try to get as many CDRs as possible submitted to the backend engines | ||||
| 	 * (if in batch mode). really_quit happens to call it again when running | ||||
| 	 * the atexit handlers, otherwise this would be a bit early. */ | ||||
| 	ast_cdr_engine_term(); | ||||
|  | ||||
| 	if (niceness == SHUTDOWN_NORMAL) { | ||||
| 		time_t s, e; | ||||
| 		/* Begin shutdown routine, hanging up active channels */ | ||||
| 		ast_begin_shutdown(1); | ||||
| 		if (option_verbose && ast_opt_console) { | ||||
| 			ast_verbose("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_active_channels() || shuttingdown != niceness) { | ||||
| 				break; | ||||
| 			} | ||||
| 			/* Sleep 1/10 of a second */ | ||||
| 			usleep(100000); | ||||
| 		} | ||||
| 	} else if (niceness >= SHUTDOWN_NICE) { | ||||
| 		if (niceness != SHUTDOWN_REALLY_NICE) { | ||||
| 			ast_begin_shutdown(0); | ||||
| 		} | ||||
| 		if (option_verbose && ast_opt_console) { | ||||
| 			ast_verbose("Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt"); | ||||
| 		} | ||||
| 		for (;;) { | ||||
| 			if (!ast_active_channels() || shuttingdown != niceness) { | ||||
| 				break; | ||||
| 			} | ||||
| 			sleep(1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Re-acquire lock and check if someone changed the niceness, in which | ||||
| 	 * case someone else has taken over the shutdown. */ | ||||
| 	ast_mutex_lock(&safe_system_lock); | ||||
| 	if (shuttingdown != niceness) { | ||||
| 		if (shuttingdown == NOT_SHUTTING_DOWN && option_verbose && ast_opt_console) { | ||||
| 			ast_verbose("Asterisk %s cancelled.\n", restart ? "restart" : "shutdown"); | ||||
| 		} | ||||
| 		ast_mutex_unlock(&safe_system_lock); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	shuttingdown = SHUTTING_DOWN; | ||||
| 	ast_mutex_unlock(&safe_system_lock); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void really_quit(int num, shutdown_nice_t niceness, int restart) | ||||
| { | ||||
| 	if (niceness >= SHUTDOWN_NICE) { | ||||
| 		ast_module_shutdown(); | ||||
| 	} | ||||
|  | ||||
| 	if (ast_opt_console || (ast_opt_remote && !ast_opt_exec)) { | ||||
| 		char filename[80] = ""; | ||||
| 		if (getenv("HOME")) { | ||||
| 			snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); | ||||
| 		} | ||||
| @@ -1698,11 +1742,12 @@ static void quit_handler(int num, int niceness, int safeshutdown, int restart) | ||||
| 		unlink(ast_config_AST_PID); | ||||
| 	printf("%s", term_quit()); | ||||
| 	if (restart) { | ||||
| 		int i; | ||||
| 		if (option_verbose || ast_opt_console) | ||||
| 			ast_verbose("Preparing for Asterisk restart...\n"); | ||||
| 		/* Mark all FD's for closing on exec */ | ||||
| 		for (x=3; x < 32768; x++) { | ||||
| 			fcntl(x, F_SETFD, FD_CLOEXEC); | ||||
| 		for (i = 3; i < 32768; i++) { | ||||
| 			fcntl(i, F_SETFD, FD_CLOEXEC); | ||||
| 		} | ||||
| 		if (option_verbose || ast_opt_console) | ||||
| 			ast_verbose("Asterisk is now restarting...\n"); | ||||
| @@ -1724,6 +1769,7 @@ static void quit_handler(int num, int niceness, int safeshutdown, int restart) | ||||
| 		/* close logger */ | ||||
| 		close_logger(); | ||||
| 	} | ||||
|  | ||||
| 	exit(0); | ||||
| } | ||||
|  | ||||
| @@ -1832,7 +1878,7 @@ static int remoteconsolehandler(char *s) | ||||
| 	} | ||||
| 	if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) && | ||||
| 	    (s[4] == '\0' || isspace(s[4]))) { | ||||
| 		quit_handler(0, 0, 0, 0); | ||||
| 		quit_handler(0, SHUTDOWN_FAST, 0); | ||||
| 		ret = 1; | ||||
| 	} | ||||
|  | ||||
| @@ -1865,7 +1911,7 @@ static int handle_quit(int fd, int argc, char *argv[]) | ||||
| { | ||||
| 	if (argc != 1) | ||||
| 		return RESULT_SHOWUSAGE; | ||||
| 	quit_handler(0, 0, 1, 0); | ||||
| 	quit_handler(0, SHUTDOWN_NORMAL, 0); | ||||
| 	return RESULT_SUCCESS; | ||||
| } | ||||
| #endif | ||||
| @@ -1885,7 +1931,7 @@ static char *handle_stop_now(struct ast_cli_entry *e, int cmd, struct ast_cli_ar | ||||
|  | ||||
| 	if (a->argc != e->args) | ||||
| 		return CLI_SHOWUSAGE; | ||||
| 	quit_handler(0, 0 /* Not nice */, 1 /* safely */, 0 /* not restart */); | ||||
| 	quit_handler(0, SHUTDOWN_NORMAL, 0 /* not restart */); | ||||
| 	return CLI_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1905,7 +1951,7 @@ static char *handle_stop_gracefully(struct ast_cli_entry *e, int cmd, struct ast | ||||
|  | ||||
| 	if (a->argc != e->args) | ||||
| 		return CLI_SHOWUSAGE; | ||||
| 	quit_handler(0, 1 /* nicely */, 1 /* safely */, 0 /* no restart */); | ||||
| 	quit_handler(0, SHUTDOWN_NICE, 0 /* no restart */); | ||||
| 	return CLI_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1925,7 +1971,7 @@ static char *handle_stop_when_convenient(struct ast_cli_entry *e, int cmd, struc | ||||
| 	if (a->argc != e->args) | ||||
| 		return CLI_SHOWUSAGE; | ||||
| 	ast_cli(a->fd, "Waiting for inactivity to perform halt\n"); | ||||
| 	quit_handler(0, 2 /* really nicely */, 1 /* safely */, 0 /* don't restart */); | ||||
| 	quit_handler(0, SHUTDOWN_REALLY_NICE, 0 /* don't restart */); | ||||
| 	return CLI_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1945,7 +1991,7 @@ static char *handle_restart_now(struct ast_cli_entry *e, int cmd, struct ast_cli | ||||
|  | ||||
| 	if (a->argc != e->args) | ||||
| 		return CLI_SHOWUSAGE; | ||||
| 	quit_handler(0, 0 /* not nicely */, 1 /* safely */, 1 /* restart */); | ||||
| 	quit_handler(0, SHUTDOWN_NORMAL, 1 /* restart */); | ||||
| 	return CLI_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1965,7 +2011,7 @@ static char *handle_restart_gracefully(struct ast_cli_entry *e, int cmd, struct | ||||
|  | ||||
| 	if (a->argc != e->args) | ||||
| 		return CLI_SHOWUSAGE; | ||||
| 	quit_handler(0, 1 /* nicely */, 1 /* safely */, 1 /* restart */); | ||||
| 	quit_handler(0, SHUTDOWN_NICE, 1 /* restart */); | ||||
| 	return CLI_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1985,12 +2031,14 @@ static char *handle_restart_when_convenient(struct ast_cli_entry *e, int cmd, st | ||||
| 	if (a->argc != e->args) | ||||
| 		return CLI_SHOWUSAGE; | ||||
| 	ast_cli(a->fd, "Waiting for inactivity to perform restart\n"); | ||||
| 	quit_handler(0, 2 /* really nicely */, 1 /* safely */, 1 /* restart */); | ||||
| 	quit_handler(0, SHUTDOWN_REALLY_NICE, 1 /* restart */); | ||||
| 	return CLI_SUCCESS; | ||||
| } | ||||
|  | ||||
| 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"; | ||||
| @@ -2005,8 +2053,17 @@ static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_ | ||||
|  | ||||
| 	if (a->argc != e->args) | ||||
| 		return CLI_SHOWUSAGE; | ||||
| 	ast_cancel_shutdown(); | ||||
| 	shuttingdown = 0; | ||||
|  | ||||
| 	ast_mutex_lock(&safe_system_lock); | ||||
| 	if (shuttingdown >= SHUTDOWN_FAST) { | ||||
| 		aborting_shutdown = 1; | ||||
| 		shuttingdown = NOT_SHUTTING_DOWN; | ||||
| 	} | ||||
| 	ast_mutex_unlock(&safe_system_lock); | ||||
|  | ||||
| 	if (aborting_shutdown) { | ||||
| 		ast_cancel_shutdown(); | ||||
| 	} | ||||
| 	return CLI_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -2176,7 +2233,7 @@ static int ast_el_read_char(EditLine *editline, char *cp) | ||||
| 			if (res < 1) { | ||||
| 				fprintf(stderr, "\nDisconnected from Asterisk server\n"); | ||||
| 				if (!ast_opt_reconnect) { | ||||
| 					quit_handler(0, 0, 0, 0); | ||||
| 					quit_handler(0, SHUTDOWN_FAST, 0); | ||||
| 				} else { | ||||
| 					int tries; | ||||
| 					int reconnects_per_second = 20; | ||||
| @@ -2196,7 +2253,7 @@ static int ast_el_read_char(EditLine *editline, char *cp) | ||||
| 					} | ||||
| 					if (tries >= 30 * reconnects_per_second) { | ||||
| 						fprintf(stderr, "Failed to reconnect for 30 seconds.  Quitting.\n"); | ||||
| 						quit_handler(0, 0, 0, 0); | ||||
| 						quit_handler(0, SHUTDOWN_FAST, 0); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| @@ -3108,7 +3165,7 @@ static void *monitor_sig_flags(void *unused) | ||||
| 				sig_flags.need_quit_handler = 1; | ||||
| 				pthread_kill(consolethread, SIGURG); | ||||
| 			} else { | ||||
| 				quit_handler(0, 0, 1, 0); | ||||
| 				quit_handler(0, SHUTDOWN_NORMAL, 0); | ||||
| 			} | ||||
| 		} | ||||
| 		if (read(sig_alert_pipe[0], &a, sizeof(a)) != sizeof(a)) { | ||||
| @@ -3608,12 +3665,12 @@ int main(int argc, char *argv[]) | ||||
| 		if (ast_opt_remote) { | ||||
| 			if (ast_opt_exec) { | ||||
| 				ast_remotecontrol(xarg); | ||||
| 				quit_handler(0, 0, 0, 0); | ||||
| 				quit_handler(0, SHUTDOWN_FAST, 0); | ||||
| 				exit(0); | ||||
| 			} | ||||
| 			printf("%s", term_quit()); | ||||
| 			ast_remotecontrol(NULL); | ||||
| 			quit_handler(0, 0, 0, 0); | ||||
| 			quit_handler(0, SHUTDOWN_FAST, 0); | ||||
| 			exit(0); | ||||
| 		} else { | ||||
| 			ast_log(LOG_ERROR, "Asterisk already running on %s.  Use 'asterisk -r' to connect.\n", ast_config_AST_SOCKET); | ||||
| @@ -3897,7 +3954,7 @@ int main(int argc, char *argv[]) | ||||
|  | ||||
| 		for (;;) { | ||||
| 			if (sig_flags.need_quit || sig_flags.need_quit_handler) { | ||||
| 				quit_handler(0, 0, 0, 0); | ||||
| 				quit_handler(0, SHUTDOWN_FAST, 0); | ||||
| 				break; | ||||
| 			} | ||||
| 			buf = (char *) el_gets(el, &num); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user