Index: CHANGES =================================================================== --- CHANGES (revision 177785) +++ CHANGES (working copy) @@ -308,6 +308,8 @@ CLI Changes ----------- + * New CLI command, "manager logout [from ]" that will logout a + user manager based on the username and also (optional) on the ip address. * New CLI command, "config reload " which reloads any module that references that particular configuration file. Also added "config list" which shows which configuration files are in use. Index: main/manager.c =================================================================== --- main/manager.c (revision 177785) +++ main/manager.c (working copy) @@ -197,6 +197,7 @@ char username[80]; /*!< Logged in username */ char challenge[10]; /*!< Authentication challenge */ int authenticated; /*!< Authentication status */ + int loggedoff; /*!< Mark this session as logged off. */ int readperm; /*!< Authorization for reading */ int writeperm; /*!< Authorization for writing */ char inbuf[1025]; /*!< Buffer */ @@ -689,7 +690,79 @@ return CLI_SUCCESS; } +/*! \brief Implement CLI command 'manager logout ' */ +static char *handle_managerlogout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct mansession_session *s = NULL; + char *ret = NULL; + size_t l; + int which = 0; + char *choice[] = { "from", NULL }; + switch (cmd) { + case CLI_INIT: + e->command = "manager logout"; + e->usage = + "Usage: manager logout [from ]\n" + " Logout a connected manager user.\n"; + return NULL; + case CLI_GENERATE: + /* username */ + if (a->pos == e->args) { + l = strlen(a->word); + AST_LIST_LOCK(&sessions); + AST_LIST_TRAVERSE(&sessions, s, list) { + if (!strncasecmp(a->word, s->username, l) && ++which > a->n ) { + ret = ast_strdup(s->username); + break; + } + } + AST_LIST_UNLOCK(&sessions); + } + /* 'from' */ + if (a->pos == e->args + 1) { + return ast_cli_complete(a->word, choice, a->n); + } + /* */ + if (a->pos == e->args + 2) { + l = strlen(a->word); + AST_LIST_LOCK(&sessions); + AST_LIST_TRAVERSE(&sessions, s, list) { + if (!strcasecmp(a->argv[e->args], s->username)) { + if (!strncasecmp(a->word, ast_inet_ntoa(s->sin.sin_addr), l) && ++which > a->n ) { + ret = ast_strdup(ast_inet_ntoa(s->sin.sin_addr)); + break; + } + } + } + AST_LIST_UNLOCK(&sessions); + } + return ret; + } + + if (a->argc != e->args + 1 && a->argc != e->args + 3) { + return CLI_SHOWUSAGE; + } else if (a->argc == e->args + 3 && strcasecmp(a->argv[3], "from")) { + return CLI_SHOWUSAGE; + } + + AST_LIST_LOCK(&sessions); + AST_LIST_TRAVERSE(&sessions, s, list) { + if (!strcasecmp(s->username, a->argv[2])) { + if (a->argc == e->args + 3) { + /* compare ip address. */ + if (strcmp(ast_inet_ntoa(s->sin.sin_addr), a->argv[e->args + 2])) { + continue; + } + } + s->loggedoff = 1; + } + } + AST_LIST_UNLOCK(&sessions); + + return CLI_SUCCESS; +} + /*! \brief CLI command manager list commands */ static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { @@ -805,6 +878,7 @@ AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"), AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"), AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"), + AST_CLI_DEFINE(handle_managerlogout, "Logout a manager user"), AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"), AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"), AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"), @@ -2965,7 +3039,7 @@ char action[80] = ""; int ret = 0; struct manager_action *tmp; - const char *user = astman_get_header(m, "Username"); + const char *user; ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action)); ast_debug(1, "Manager received command '%s'\n", action); @@ -2984,6 +3058,7 @@ return 0; } + user = astman_get_header(m, "Username"); if (!allowmultiplelogin && !s->session->authenticated && user && (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) { if (check_manager_session_inuse(user)) { @@ -3035,7 +3110,7 @@ * Also note that we assume output to have at least "maxlen" space. * \endverbatim */ -static int get_input(struct mansession *s, char *output) +static int get_input(struct mansession *s, char *output, int timeout_ms) { int res, x; int maxlen = sizeof(s->session->inbuf) - 1; @@ -3065,24 +3140,23 @@ ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->session->sin.sin_addr), src); s->session->inlen = 0; } - res = 0; - while (res == 0) { - /* XXX do we really need this locking ? */ - ast_mutex_lock(&s->session->__lock); - if (s->session->pending_event) { - s->session->pending_event = 0; - ast_mutex_unlock(&s->session->__lock); - return 0; - } - s->session->waiting_thread = pthread_self(); + + /* XXX do we really need this locking ? */ + ast_mutex_lock(&s->session->__lock); + if (s->session->pending_event) { + s->session->pending_event = 0; ast_mutex_unlock(&s->session->__lock); + return 0; + } + s->session->waiting_thread = pthread_self(); + ast_mutex_unlock(&s->session->__lock); - res = ast_wait_for_input(s->session->fd, -1); /* return 0 on timeout ? */ + res = ast_wait_for_input(s->session->fd, timeout_ms); /* return 0 on timeout ? */ - ast_mutex_lock(&s->session->__lock); - s->session->waiting_thread = AST_PTHREADT_NULL; - ast_mutex_unlock(&s->session->__lock); - } + ast_mutex_lock(&s->session->__lock); + s->session->waiting_thread = AST_PTHREADT_NULL; + ast_mutex_unlock(&s->session->__lock); + if (res < 0) { /* If we get a signal from some other thread (typically because * there are new events queued), return 0 to notify the caller. @@ -3091,7 +3165,10 @@ return 0; ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno)); return -1; + } else if (!res) { + return 0; } + ast_mutex_lock(&s->session->__lock); res = fread(src + s->session->inlen, 1, maxlen - s->session->inlen, s->session->f); if (res < 1) @@ -3113,17 +3190,16 @@ for (;;) { /* Check if any events are pending and do them if needed */ - if (process_events(s)) + if (process_events(s)) { return -1; - res = get_input(s, header_buf); - if (res == 0) { - continue; - } else if (res > 0) { + } + res = get_input(s, header_buf, 500); + if (res > 0) { if (ast_strlen_zero(header_buf)) return process_message(s, &m) ? -1 : 0; else if (m.hdrcount < (AST_MAX_MANHEADERS - 1)) m.headers[m.hdrcount++] = ast_strdupa(header_buf); - } else { + } else if (res < 0 || s->session->loggedoff) { return res; } } @@ -3178,9 +3254,11 @@ astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */ for (;;) { - if ((res = do_message(&s)) < 0) + if ((res = do_message(&s)) < 0 || session->loggedoff) { break; + } } + /* session is over, explain why and terminate */ if (session->authenticated) { if (manager_displayconnects(session))