[Asterisk-code-review] Restrict CLI/AMI commands on shutdown. (asterisk[13])

Anonymous Coward asteriskteam at digium.com
Thu Mar 24 19:55:50 CDT 2016


Anonymous Coward #1000019 has submitted this change and it was merged.

Change subject: Restrict CLI/AMI commands on shutdown.
......................................................................


Restrict CLI/AMI commands on shutdown.

During stress testing, we have frequently seen crashes occur because a
CLI or AMI command attempts to access information that is in the process
of being destroyed.

When addressing how to fix this issue, we initially considered fixing
individual crashes we observed. However, the changes required to fix
those problems would introduce considerable overhead to the nominal
case. This is not reasonable in order to prevent a crash from occurring
while Asterisk is already shutting down.

Instead, this change makes it so AMI and CLI commands cannot be executed
if Asterisk is being shut down. For AMI, this is absolute. For CLI,
though, certain commands can be registered so that they may be run
during Asterisk shutdown.

ASTERISK-25825 #close

Change-Id: I8887e215ac352fadf7f4c1e082da9089b1421990
---
M include/asterisk/cli.h
M main/asterisk.c
M main/cli.c
M main/manager.c
M main/utils.c
5 files changed, 80 insertions(+), 4 deletions(-)

Approvals:
  Richard Mudgett: Looks good to me, but someone else must approve
  Joshua Colp: Looks good to me, approved; Verified



diff --git a/include/asterisk/cli.h b/include/asterisk/cli.h
index 458ebc8..c51d89e 100644
--- a/include/asterisk/cli.h
+++ b/include/asterisk/cli.h
@@ -310,6 +310,18 @@
  */
 char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos);
 
+/*!
+ * \brief Allow a CLI command to be executed while Asterisk is shutting down.
+ *
+ * CLI commands by defeault are disabled when Asterisk is shutting down. This is
+ * to ensure the safety of the shutdown since CLI commands may attempt to access
+ * resources that have been freed as a result of the shutdown.
+ *
+ * If a CLI command should be allowed at shutdown, then the best way to enable this
+ * is to call ast_cli_allow_at_shutdown during the CLI_INIT state of the CLI handler.
+ */
+int ast_cli_allow_at_shutdown(struct ast_cli_entry *e);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
diff --git a/main/asterisk.c b/main/asterisk.c
index 3985149..5901d57 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -2441,6 +2441,7 @@
 		e->usage =
 			"Usage: core stop now\n"
 			"       Shuts down a running Asterisk immediately, hanging up all active calls .\n";
+		ast_cli_allow_at_shutdown(e);
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
@@ -2461,6 +2462,7 @@
 			"Usage: core stop gracefully\n"
 			"       Causes Asterisk to not accept new calls, and exit when all\n"
 			"       active calls have terminated normally.\n";
+		ast_cli_allow_at_shutdown(e);
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
@@ -2480,6 +2482,7 @@
 		e->usage =
 			"Usage: core stop when convenient\n"
 			"       Causes Asterisk to perform a shutdown when all active calls have ended.\n";
+		ast_cli_allow_at_shutdown(e);
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
@@ -2501,6 +2504,7 @@
 			"Usage: core restart now\n"
 			"       Causes Asterisk to hangup all calls and exec() itself performing a cold\n"
 			"       restart.\n";
+		ast_cli_allow_at_shutdown(e);
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
@@ -2521,6 +2525,7 @@
 			"Usage: core restart gracefully\n"
 			"       Causes Asterisk to stop accepting new calls and exec() itself performing a cold\n"
 			"       restart when all active calls have ended.\n";
+		ast_cli_allow_at_shutdown(e);
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
@@ -2540,6 +2545,7 @@
 		e->usage =
 			"Usage: core restart when convenient\n"
 			"       Causes Asterisk to perform a cold restart when all active calls have ended.\n";
+		ast_cli_allow_at_shutdown(e);
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
@@ -2561,6 +2567,7 @@
 			"Usage: core abort shutdown\n"
 			"       Causes Asterisk to abort an executing shutdown or restart, and resume normal\n"
 			"       call operations.\n";
+		ast_cli_allow_at_shutdown(e);
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
diff --git a/main/cli.c b/main/cli.c
index 7f86eab..852b32d 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -63,6 +63,7 @@
 #include "asterisk/bridge.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/stasis_bridges.h"
+#include "asterisk/vector.h"
 
 /*!
  * \brief List of restrictions per user.
@@ -108,6 +109,9 @@
 static struct module_level_list debug_modules = AST_RWLIST_HEAD_INIT_VALUE;
 
 AST_THREADSTORAGE(ast_cli_buf);
+
+AST_RWLOCK_DEFINE_STATIC(shutdown_commands_lock);
+static AST_VECTOR(, struct ast_cli_entry *) shutdown_commands;
 
 /*! \brief Initial buffer size for resulting strings in ast_cli() */
 #define AST_CLI_INITLEN   256
@@ -2030,6 +2034,7 @@
 /*! \brief initialize the _full_cmd string in * each of the builtins. */
 void ast_builtins_init(void)
 {
+	AST_VECTOR_INIT(&shutdown_commands, 0);
 	ast_cli_register_multiple(cli_cli, ARRAY_LEN(cli_cli));
 	ast_register_cleanup(cli_shutdown);
 }
@@ -2208,6 +2213,13 @@
 	return 0;
 }
 
+static void remove_shutdown_command(struct ast_cli_entry *e)
+{
+	ast_rwlock_wrlock(&shutdown_commands_lock);
+	AST_VECTOR_REMOVE_ELEM_UNORDERED(&shutdown_commands, e, AST_VECTOR_ELEM_CLEANUP_NOOP);
+	ast_rwlock_unlock(&shutdown_commands_lock);
+}
+
 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
 {
 	if (e->inuse) {
@@ -2216,6 +2228,7 @@
 		AST_RWLIST_WRLOCK(&helpers);
 		AST_RWLIST_REMOVE(&helpers, e, list);
 		AST_RWLIST_UNLOCK(&helpers);
+		remove_shutdown_command(e);
 		ast_free(e->_full_cmd);
 		e->_full_cmd = NULL;
 		if (e->handler) {
@@ -2679,10 +2692,27 @@
 	return __ast_cli_generator(text, word, state, 1);
 }
 
+static int allowed_on_shutdown(struct ast_cli_entry *e)
+{
+	int found = 0;
+	int i;
+
+	ast_rwlock_rdlock(&shutdown_commands_lock);
+	for (i = 0; i < AST_VECTOR_SIZE(&shutdown_commands); ++i) {
+		if (e == AST_VECTOR_GET(&shutdown_commands, i)) {
+			found = 1;
+			break;
+		}
+	}
+	ast_rwlock_unlock(&shutdown_commands_lock);
+
+	return found;
+}
+
 int ast_cli_command_full(int uid, int gid, int fd, const char *s)
 {
 	const char *args[AST_MAX_ARGS + 1];
-	struct ast_cli_entry *e;
+	struct ast_cli_entry *e = NULL;
 	int x;
 	char *duplicate = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
 	char tmp[AST_MAX_ARGS + 1];
@@ -2706,12 +2736,16 @@
 		goto done;
 	}
 
+	if (ast_shutting_down() && !allowed_on_shutdown(e)) {
+		ast_cli(fd, "Command '%s' cannot be run during shutdown\n", s);
+		goto done;
+	}
+
 	ast_join(tmp, sizeof(tmp), args + 1);
 	/* Check if the user has rights to run this command. */
 	if (!cli_has_permissions(uid, gid, tmp)) {
 		ast_cli(fd, "You don't have permissions to run '%s' command\n", tmp);
-		ast_free(duplicate);
-		return 0;
+		goto done;
 	}
 
 	/*
@@ -2728,8 +2762,11 @@
 		if (retval == CLI_FAILURE)
 			ast_cli(fd, "Command '%s' failed.\n", s);
 	}
-	ast_atomic_fetchadd_int(&e->inuse, -1);
+
 done:
+	if (e) {
+		ast_atomic_fetchadd_int(&e->inuse, -1);
+	}
 	ast_free(duplicate);
 	return 0;
 }
@@ -2750,3 +2787,14 @@
 	}
 	return count;
 }
+
+int ast_cli_allow_at_shutdown(struct ast_cli_entry *e)
+{
+	int res;
+
+	ast_rwlock_wrlock(&shutdown_commands_lock);
+	res = AST_VECTOR_APPEND(&shutdown_commands, e);
+	ast_rwlock_unlock(&shutdown_commands_lock);
+
+	return res;
+}
diff --git a/main/manager.c b/main/manager.c
index de00381..7c21550 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -6114,6 +6114,14 @@
 		return 0;
 	}
 
+	if (ast_shutting_down()) {
+		ast_log(LOG_ERROR, "Unable to process manager action '%s'. Asterisk is shutting down.\n", action);
+		mansession_lock(s);
+		astman_send_error(s, m, "Asterisk is shutting down");
+		mansession_unlock(s);
+		return 0;
+	}
+
 	if (!s->session->authenticated
 		&& strcasecmp(action, "Login")
 		&& strcasecmp(action, "Logoff")
diff --git a/main/utils.c b/main/utils.c
index 0b527f0..686af6b 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -1158,6 +1158,7 @@
 			"Usage: core show locks\n"
 			"       This command is for lock debugging.  It prints out which locks\n"
 			"are owned by each active thread.\n";
+		ast_cli_allow_on_shutdown(e);
 		return NULL;
 
 	case CLI_GENERATE:

-- 
To view, visit https://gerrit.asterisk.org/2372
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I8887e215ac352fadf7f4c1e082da9089b1421990
Gerrit-PatchSet: 6
Gerrit-Project: asterisk
Gerrit-Branch: 13
Gerrit-Owner: Mark Michelson <mmichelson at digium.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: Corey Farrell <git at cfware.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Richard Mudgett <rmudgett at digium.com>



More information about the asterisk-code-review mailing list