[asterisk-commits] wdoekes: branch 10 r350889 - in /branches/10: ./ main/asterisk.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sun Jan 15 14:13:08 CST 2012


Author: wdoekes
Date: Sun Jan 15 14:12:54 2012
New Revision: 350889

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=350889
Log:
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/
........

Merged revisions 350888 from http://svn.asterisk.org/svn/asterisk/branches/1.8

Modified:
    branches/10/   (props changed)
    branches/10/main/asterisk.c

Propchange: branches/10/
------------------------------------------------------------------------------
Binary property 'branch-1.8-merged' - no diff available.

Modified: branches/10/main/asterisk.c
URL: http://svnview.digium.com/svn/asterisk/branches/10/main/asterisk.c?view=diff&rev=350889&r1=350888&r2=350889
==============================================================================
--- branches/10/main/asterisk.c (original)
+++ branches/10/main/asterisk.c Sun Jan 15 14:12:54 2012
@@ -280,7 +280,16 @@
 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;
@@ -1614,57 +1623,92 @@
 	AST_RWLIST_UNLOCK(&atexits);
 }
 
-static void quit_handler(int num, int niceness, int safeshutdown, 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) */
+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)
+{
+	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 (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 (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"));
 		}
@@ -1705,11 +1749,12 @@
 		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");
@@ -1731,6 +1776,7 @@
 		/* close logger */
 		close_logger();
 	}
+
 	exit(0);
 }
 
@@ -1839,7 +1885,7 @@
 	}
 	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;
 	}
 
@@ -1872,7 +1918,7 @@
 {
 	if (argc != 1)
 		return RESULT_SHOWUSAGE;
-	quit_handler(0, 0, 1, 0);
+	quit_handler(0, SHUTDOWN_NORMAL, 0);
 	return RESULT_SUCCESS;
 }
 #endif
@@ -1892,7 +1938,7 @@
 
 	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;
 }
 
@@ -1912,7 +1958,7 @@
 
 	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;
 }
 
@@ -1932,7 +1978,7 @@
 	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;
 }
 
@@ -1952,7 +1998,7 @@
 
 	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;
 }
 
@@ -1972,7 +2018,7 @@
 
 	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;
 }
 
@@ -1992,12 +2038,14 @@
 	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";
@@ -2012,8 +2060,17 @@
 
 	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;
 }
 
@@ -2183,7 +2240,7 @@
 			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;
@@ -2203,7 +2260,7 @@
 					}
 					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);
 					}
 				}
 			}
@@ -3136,7 +3193,7 @@
 				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)) {
@@ -3636,12 +3693,12 @@
 		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);
@@ -3934,7 +3991,7 @@
 
 		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);




More information about the asterisk-commits mailing list