diff --git a/main/manager.c b/main/manager.c index 20b434f1eb..7461c46d52 100644 --- a/main/manager.c +++ b/main/manager.c @@ -1728,6 +1728,7 @@ struct mansession_session { time_t noncetime; /*!< Timer for nonce value expiration */ unsigned long oldnonce; /*!< Stale nonce value */ unsigned long nc; /*!< incremental nonce counter */ + unsigned int kicked:1; /*!< Flag set if session is forcibly kicked */ ast_mutex_t notify_lock; /*!< Lock for notifying this session of events */ AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */ AST_LIST_ENTRY(mansession_session) list; @@ -2772,6 +2773,76 @@ static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli return CLI_SUCCESS; } +/*! \brief CLI command manager kick session */ +static char *handle_kickmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct ao2_container *sessions; + struct mansession_session *session; + struct ao2_iterator i; + int fd = -1; + int found = 0; + + switch (cmd) { + case CLI_INIT: + e->command = "manager kick session"; + e->usage = + "Usage: manager kick session \n" + " Kick an active Asterisk Manager Interface session\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 4) { + return CLI_SHOWUSAGE; + } + + fd = atoi(a->argv[3]); + if (fd <= 0) { /* STDOUT won't be a valid AMI fd either */ + ast_cli(a->fd, "Invalid AMI file descriptor: %s\n", a->argv[3]); + return CLI_FAILURE; + } + + sessions = ao2_global_obj_ref(mgr_sessions); + if (sessions) { + i = ao2_iterator_init(sessions, 0); + ao2_ref(sessions, -1); + while ((session = ao2_iterator_next(&i))) { + ao2_lock(session); + if (session->stream) { + if (ast_iostream_get_fd(session->stream) == fd) { + if (session->kicked) { + ast_cli(a->fd, "Manager session using file descriptor %d has already been kicked\n", fd); + ao2_unlock(session); + unref_mansession(session); + break; + } + fd = ast_iostream_get_fd(session->stream); + found = fd; + ast_cli(a->fd, "Kicking manager session connected using file descriptor %d\n", fd); + ast_mutex_lock(&session->notify_lock); + session->kicked = 1; + if (session->waiting_thread != AST_PTHREADT_NULL) { + pthread_kill(session->waiting_thread, SIGURG); + } + ast_mutex_unlock(&session->notify_lock); + ao2_unlock(session); + unref_mansession(session); + break; + } + } + ao2_unlock(session); + unref_mansession(session); + } + ao2_iterator_destroy(&i); + } + + if (!found) { + ast_cli(a->fd, "No manager session found using file descriptor %d\n", fd); + } + return CLI_SUCCESS; +} + /*! \brief CLI command manager list connected */ static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { @@ -7465,6 +7536,10 @@ static int get_input(struct mansession *s, char *output) ast_mutex_unlock(&s->session->notify_lock); } if (res < 0) { + if (s->session->kicked) { + ast_debug(1, "Manager session has been kicked\n"); + return -1; + } /* If we get a signal from some other thread (typically because * there are new events queued), return 0 to notify the caller. */ @@ -7664,7 +7739,7 @@ static void *session_do(void *data) astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */ for (;;) { - if ((res = do_message(&s)) < 0 || s.write_error) { + if ((res = do_message(&s)) < 0 || s.write_error || session->kicked) { break; } if (session->authenticated) { @@ -7674,7 +7749,7 @@ static void *session_do(void *data) /* session is over, explain why and terminate */ if (session->authenticated) { if (manager_displayconnects(session)) { - ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr)); + ast_verb(2, "Manager '%s' %s from %s\n", session->username, session->kicked ? "kicked" : "logged off", ast_sockaddr_stringify_addr(&session->addr)); } } else { ast_atomic_fetchadd_int(&unauth_sessions, -1); @@ -9591,6 +9666,7 @@ static struct ast_cli_entry cli_manager[] = { AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"), AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"), AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"), + AST_CLI_DEFINE(handle_kickmanconn, "Kick a connected manager interface connection"), AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"), AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"), AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),