Commit the changes from issue #5240.
[asterisk/asterisk.git] / main / manager.c
index a2883c8..5b220df 100644 (file)
  * \author Mark Spencer <markster@digium.com>
  *
  * Channel Management and more
- *
+ * 
  * \ref amiconf
  */
 
-/*! \addtogroup Group_AMI AMI functions
+/*! \addtogroup Group_AMI AMI functions 
 */
-/*! @{
+/*! @{ 
  Doxygen group */
 
 #include "asterisk.h"
@@ -103,12 +103,9 @@ static pthread_t t;
 static int block_sockets = 0;
 static int num_sessions = 0;
 
-static struct ast_manager_user *amus =NULL;
-
 /* Protected by the sessions list lock */
 struct eventqent *master_eventq = NULL;
 
-AST_MUTEX_DEFINE_STATIC(amulock);
 AST_THREADSTORAGE(manager_event_buf, manager_event_buf_init);
 #define MANAGER_EVENT_BUF_INITSIZE   256
 
@@ -178,6 +175,8 @@ struct mansession {
        AST_LIST_ENTRY(mansession) list;
 };
 
+static AST_LIST_HEAD_STATIC(sessions, mansession);
+
 struct ast_manager_user {
        char username[80];
        char *secret;
@@ -186,10 +185,11 @@ struct ast_manager_user {
        char *read;
        char *write;
        unsigned int displayconnects:1;
-       struct ast_manager_user *next;
+       int keep;
+       AST_LIST_ENTRY(ast_manager_user) list;
 };
 
-static AST_LIST_HEAD_STATIC(sessions, mansession);
+static AST_LIST_HEAD_STATIC(users, ast_manager_user);
 
 static struct manager_action *first_action = NULL;
 AST_MUTEX_DEFINE_STATIC(actionlock);
@@ -213,7 +213,7 @@ static char *authority_to_str(int authority, char *res, int reslen)
 
        if (ast_strlen_zero(res))
                ast_copy_string(res, "<none>", reslen);
-
+       
        return res;
 }
 
@@ -263,7 +263,7 @@ static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int low
                        strcpy(*dst, "&amp;");
                        (*dst) += 5;
                        *maxlen -= 5;
-                       break;
+                       break;          
                default:
                        *(*dst)++ = lower ? tolower(*src) : *src;
                        (*maxlen)--;
@@ -285,11 +285,11 @@ static char *xml_translate(char *in, struct ast_variable *vars)
        int escaped = 0;
        int inobj = 0;
        int x;
-
+       
        for (v = vars; v; v = v->next) {
                if (!dest && !strcasecmp(v->name, "ajaxdest"))
                        dest = v->value;
-               else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
+               else if (!objtype && !strcasecmp(v->name, "ajaxobjtype")) 
                        objtype = v->value;
        }
        if (!dest)
@@ -334,7 +334,7 @@ static char *xml_translate(char *in, struct ast_variable *vars)
                                        ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
                                        inobj = 1;
                                }
-                               ast_build_string(&tmp, &len, " ");
+                               ast_build_string(&tmp, &len, " ");                              
                                xml_copy_escape(&tmp, &len, var, 1);
                                ast_build_string(&tmp, &len, "='");
                                xml_copy_escape(&tmp, &len, val, 0);
@@ -396,32 +396,14 @@ static char *html_translate(char *in)
 
 
 
-struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
+static struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
 {
-       struct ast_manager_user *tmp = NULL;
-       tmp=amus;
-       while (tmp) {
-               if (!strcasecmp(tmp->username, name))
-                       break;
-               tmp = tmp->next;
-       }
-       if ( tmp)
-               return tmp;
-       return NULL;
-}
-
+       struct ast_manager_user *user = NULL;
 
-
-int *ast_manager_user_add(struct ast_manager_user *amu) {
-       if (!amu) {
-               ast_log(LOG_DEBUG, "You cant pass NULL to that function");
-               return NULL;
-       }
-       ast_mutex_lock(&amulock);
-       amu->next = amus;
-       amus = amu;
-       ast_mutex_unlock(&amulock);
-       return NULL;
+       AST_LIST_TRAVERSE(&users, user, list)
+               if (!strcasecmp(user->username, name))
+                       break;
+       return user;
 }
 
 void astman_append(struct mansession *s, const char *fmt, ...)
@@ -435,14 +417,14 @@ void astman_append(struct mansession *s, const char *fmt, ...)
        va_start(ap, fmt);
        ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
        va_end(ap);
-
+       
        if (s->fd > -1)
                ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->writetimeout);
        else {
                if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr))))
                        return;
 
-               ast_dynamic_str_append(&s->outputstr, 0, "%s", buf->str);
+               ast_dynamic_str_append(&s->outputstr, 0, "%s", buf->str);       
        }
 }
 
@@ -470,23 +452,19 @@ static int handle_showmancmd(int fd, int argc, char *argv[])
 
 static int handle_showmanager(int fd, int argc, char *argv[])
 {
-       struct ast_manager_user *tmp;
-       if (argc != 4 )
-               return RESULT_SHOWUSAGE;
+       struct ast_manager_user *user = NULL;
 
-       /* try to lock manager_user list ... */
-       if (ast_mutex_lock(&amulock)) {
-               ast_log(LOG_ERROR, "Unable to lock manager_user list\n");
-               return -1;
-       }
+       if (argc != 4)
+               return RESULT_SHOWUSAGE;
 
-       tmp = ast_get_manager_by_name_locked(argv[3]);
+       AST_LIST_LOCK(&users);
 
-       if (!tmp) {
-               ast_cli(fd, "There are no manager called %s\n",argv[3]);
-               ast_mutex_unlock(&amulock);
+       if (!(user = ast_get_manager_by_name_locked(argv[3]))) {
+               ast_cli(fd, "There is no manager called %s\n", argv[3]);
+               AST_LIST_UNLOCK(&users);
                return -1;
        }
+
        ast_cli(fd,"\n");
        ast_cli(fd,
                "       username: %s\n"
@@ -496,52 +474,54 @@ static int handle_showmanager(int fd, int argc, char *argv[])
                "           read: %s\n"
                "          write: %s\n"
                "displayconnects: %s\n",
-               (tmp->username ? tmp->username : "(N/A)"),
-               (tmp->secret ? tmp->secret : "(N/A)"),
-               (tmp->deny ? tmp->deny : "(N/A)"),
-               (tmp->permit ? tmp->permit : "(N/A)"),
-               (tmp->read ? tmp->read : "(N/A)"),
-               (tmp->write ? tmp->write : "(N/A)"),
-               (tmp->displayconnects ? "yes" : "no"));
-       ast_mutex_unlock(&amulock);
+               (user->username ? user->username : "(N/A)"),
+               (user->secret ? user->secret : "(N/A)"),
+               (user->deny ? user->deny : "(N/A)"),
+               (user->permit ? user->permit : "(N/A)"),
+               (user->read ? user->read : "(N/A)"),
+               (user->write ? user->write : "(N/A)"),
+               (user->displayconnects ? "yes" : "no"));
+
+       AST_LIST_UNLOCK(&users);
 
        return RESULT_SUCCESS;
 }
 
 
-static int handle_showmanagers(int fd, int argc, char *argv[]) {
-       struct ast_manager_user *tmp;
+static int handle_showmanagers(int fd, int argc, char *argv[])
+{
+       struct ast_manager_user *user = NULL;
        int count_amu = 0;
 
-       if ( argc > 4 )
+       if (argc != 3)
                return RESULT_SHOWUSAGE;
 
-       /* try to lock manager_user list ... */
-       if (ast_mutex_lock(&amulock)) {
-               ast_log(LOG_ERROR, "Unable to lock manager_user list\n");
-               return -1;
-       }
+       AST_LIST_LOCK(&users);
 
-       tmp=amus;
-       if (!tmp) {
-               ast_cli(fd, "There are no manager user.\n");
-               ast_mutex_unlock(&amulock);
-               return -1;
+       /* If there are no users, print out something along those lines */
+       if (AST_LIST_EMPTY(&users)) {
+               ast_cli(fd, "There are no manager users.\n");
+               AST_LIST_UNLOCK(&users);
+               return RESULT_SUCCESS;
        }
+
        ast_cli(fd, "\nusername\n--------\n");
-       while (tmp) {
-               ast_cli(fd, "%s\n", tmp->username);
-               tmp = tmp->next;
+
+       AST_LIST_TRAVERSE(&users, user, list) {
+               ast_cli(fd, "%s\n", user->username);
                count_amu++;
        }
-       ast_mutex_unlock(&amulock);
+
+       AST_LIST_UNLOCK(&users);
+
        ast_cli(fd,"-------------------\n");
        ast_cli(fd,"%d manager users configured.\n", count_amu);
+
        return RESULT_SUCCESS;
 }
 
 
-/*! \brief  CLI command
+/*! \brief  CLI command 
        Should change to "manager show commands" */
 static int handle_showmancmds(int fd, int argc, char *argv[])
 {
@@ -551,12 +531,12 @@ static int handle_showmancmds(int fd, int argc, char *argv[])
 
        ast_cli(fd, format, "Action", "Privilege", "Synopsis");
        ast_cli(fd, format, "------", "---------", "--------");
-
+       
        ast_mutex_lock(&actionlock);
        for (; cur; cur = cur->next) /* Walk the list of actions */
                ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
        ast_mutex_unlock(&actionlock);
-
+       
        return RESULT_SUCCESS;
 }
 
@@ -568,7 +548,7 @@ static int handle_showmanconn(int fd, int argc, char *argv[])
        char *format = "  %-15.15s  %-15.15s\n";
 
        ast_cli(fd, format, "Username", "IP Address");
-
+       
        AST_LIST_LOCK(&sessions);
        AST_LIST_TRAVERSE(&sessions, s, list)
                ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
@@ -594,56 +574,78 @@ static int handle_showmaneventq(int fd, int argc, char *argv[])
        return RESULT_SUCCESS;
 }
 
-static char showmancmd_help[] =
-"Usage: show manager command <actionname>\n"
+static char showmancmd_help[] = 
+"Usage: manager show command <actionname>\n"
 "      Shows the detailed description for a specific Asterisk manager interface command.\n";
 
-static char showmancmds_help[] =
-"Usage: show manager commands\n"
+static char showmancmds_help[] = 
+"Usage: manager list commands\n"
 "      Prints a listing of all the available Asterisk manager interface commands.\n";
 
-static char showmanconn_help[] =
-"Usage: show manager connected\n"
+static char showmanconn_help[] = 
+"Usage: manager list connected\n"
 "      Prints a listing of the users that are currently connected to the\n"
 "Asterisk manager interface.\n";
 
-static char showmaneventq_help[] =
-"Usage: show manager eventq\n"
+static char showmaneventq_help[] = 
+"Usage: manager list eventq\n"
 "      Prints a listing of all events pending in the Asterisk manger\n"
 "event queue.\n";
 
 static char showmanagers_help[] =
-"Usage: show managers\n"
+"Usage: manager list users\n"
 "       Prints a listing of all managers that are currently configured on that\n"
 " system.\n";
 
 static char showmanager_help[] =
-" Usage: show manager foobar\n"
-" Display all the infos related to the manager foobar.\n";
-
-static struct ast_cli_entry show_managers_cli =
-       { { "manager", "show", "users" },
-       handle_showmanagers, "Show all managers users (connected or not)", showmanagers_help };
-
-static struct ast_cli_entry show_manager_cli =
-{ { "manager", "show", "user" }, handle_showmanager, "Display information on a specific manager", showmanager_help};
-
-
-static struct ast_cli_entry show_mancmd_cli =
-       { { "show", "manager", "command", NULL },
-       handle_showmancmd, "Show a manager interface command", showmancmd_help, complete_show_mancmd };
-
-static struct ast_cli_entry show_mancmds_cli =
-       { { "show", "manager", "commands", NULL },
-       handle_showmancmds, "List manager interface commands", showmancmds_help };
-
-static struct ast_cli_entry show_manconn_cli =
-       { { "show", "manager", "connected", NULL },
-       handle_showmanconn, "Show connected manager interface users", showmanconn_help };
-
-static struct ast_cli_entry show_maneventq_cli =
-       { { "show", "manager", "eventq", NULL },
-       handle_showmaneventq, "Show manager interface queued events", showmaneventq_help };
+" Usage: manager show user <user>\n"
+"        Display all information related to the manager user specified.\n";
+
+static struct ast_cli_entry cli_show_manager_command_deprecated = {
+       { "show", "manager", "command", NULL },
+       handle_showmancmd, NULL,
+       NULL, complete_show_mancmd };
+
+static struct ast_cli_entry cli_show_manager_commands_deprecated = {
+       { "show", "manager", "commands", NULL },
+       handle_showmancmds, NULL,
+       NULL };
+
+static struct ast_cli_entry cli_show_manager_connected_deprecated = {
+       { "show", "manager", "connected", NULL },
+       handle_showmanconn, NULL,
+       NULL };
+
+static struct ast_cli_entry cli_show_manager_eventq_deprecated = {
+       { "show", "manager", "eventq", NULL },
+       handle_showmaneventq, NULL,
+       NULL };
+
+static struct ast_cli_entry cli_manager[] = {
+       { { "manager", "show", "command", NULL },
+       handle_showmancmd, "Show a manager interface command",
+       showmancmd_help, complete_show_mancmd, &cli_show_manager_command_deprecated },
+
+       { { "manager", "list", "commands", NULL },
+       handle_showmancmds, "List manager interface commands",
+       showmancmds_help, NULL, &cli_show_manager_commands_deprecated },
+
+       { { "manager", "list", "connected", NULL },
+       handle_showmanconn, "List connected manager interface users",
+       showmanconn_help, NULL, &cli_show_manager_connected_deprecated },
+
+       { { "manager", "list", "eventq", NULL },
+       handle_showmaneventq, "List manager interface queued events",
+       showmaneventq_help, NULL, &cli_show_manager_eventq_deprecated },
+
+       { { "manager", "list", "users", NULL },
+       handle_showmanagers, "List configured manager users",
+       showmanagers_help, NULL, NULL },
+
+       { { "manager", "show", "user", NULL },
+       handle_showmanager, "Display information on a specific manager user",
+       showmanager_help, NULL, NULL },
+};
 
 static void unuse_eventqent(struct eventqent *e)
 {
@@ -698,12 +700,12 @@ struct ast_variable *astman_get_variables(struct message *m)
        struct ast_variable *head = NULL, *cur;
        char *var, *val;
 
-       char *parse;
+       char *parse;    
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(vars)[32];
        );
 
-       varlen = strlen("Variable: ");
+       varlen = strlen("Variable: ");  
 
        for (x = 0; x < m->hdrcount; x++) {
                if (strncasecmp("Variable: ", m->headers[x], varlen))
@@ -774,7 +776,7 @@ void astman_send_ack(struct mansession *s, struct message *m, char *msg)
    ast_instring("this|that|more","this",',') == 1;
 
    feel free to move this to app.c -anthm */
-static int ast_instring(char *bigstr, char *smallstr, char delim)
+static int ast_instring(char *bigstr, char *smallstr, char delim) 
 {
        char *val = bigstr, *next;
 
@@ -803,11 +805,11 @@ static int get_perm(char *instr)
                if (ast_instring(instr, perms[x].label, ','))
                        ret |= perms[x].num;
        }
-
+       
        return ret;
 }
 
-static int ast_is_number(char *string)
+static int ast_is_number(char *string) 
 {
        int ret = 1, x = 0;
 
@@ -820,14 +822,14 @@ static int ast_is_number(char *string)
                        break;
                }
        }
-
+       
        return ret ? atoi(string) : 0;
 }
 
-static int ast_strings_to_mask(char *string)
+static int ast_strings_to_mask(char *string) 
 {
        int x, ret = -1;
-
+       
        x = ast_is_number(string);
 
        if (x)
@@ -839,12 +841,12 @@ static int ast_strings_to_mask(char *string)
        else if (ast_true(string)) {
                ret = 0;
                for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
-                       ret |= perms[x].num;
+                       ret |= perms[x].num;            
        } else {
                ret = 0;
                for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
-                       if (ast_instring(string, perms[x].label, ','))
-                               ret |= perms[x].num;
+                       if (ast_instring(string, perms[x].label, ',')) 
+                               ret |= perms[x].num;            
                }
        }
 
@@ -852,7 +854,7 @@ static int ast_strings_to_mask(char *string)
 }
 
 /*! \brief
-   Rather than braindead on,off this now can also accept a specific int mask value
+   Rather than braindead on,off this now can also accept a specific int mask value 
    or a ',' delim list of mask strings (the same as manager.conf) -anthm
 */
 static int set_eventmask(struct mansession *s, char *eventmask)
@@ -860,10 +862,10 @@ static int set_eventmask(struct mansession *s, char *eventmask)
        int maskint = ast_strings_to_mask(eventmask);
 
        ast_mutex_lock(&s->__lock);
-       if (maskint >= 0)
+       if (maskint >= 0)       
                s->send_events = maskint;
        ast_mutex_unlock(&s->__lock);
-
+       
        return maskint;
 }
 
@@ -876,7 +878,7 @@ static int authenticate(struct mansession *s, struct message *m)
        char *authtype = astman_get_header(m, "AuthType");
        char *key = astman_get_header(m, "Key");
        char *events = astman_get_header(m, "Events");
-
+       
        cfg = ast_config_load("manager.conf");
        if (!cfg)
                return -1;
@@ -911,7 +913,7 @@ static int authenticate(struct mansession *s, struct message *m)
                                                else
                                                        s->writetimeout = val;
                                        }
-
+                                               
                                }
                                if (ha && !ast_apply_ha(ha, &(s->sin))) {
                                        ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
@@ -946,7 +948,7 @@ static int authenticate(struct mansession *s, struct message *m)
                                        ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
                                        ast_config_destroy(cfg);
                                        return -1;
-                               }
+                               }       
                        }
                }
                cat = ast_category_browse(cfg, cat);
@@ -966,7 +968,7 @@ static int authenticate(struct mansession *s, struct message *m)
 }
 
 /*! \brief Manager PING */
-static char mandescr_ping[] =
+static char mandescr_ping[] = 
 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
 "  manager connection open.\n"
 "Variables: NONE\n";
@@ -1027,7 +1029,7 @@ static void handle_updates(struct mansession *s, struct message *m, struct ast_c
        char *action, *cat, *var, *value, *match;
        struct ast_category *category;
        struct ast_variable *v;
-
+       
        for (x=0;x<100000;x++) {
                snprintf(hdr, sizeof(hdr), "Action-%06d", x);
                action = astman_get_header(m, hdr);
@@ -1051,7 +1053,7 @@ static void handle_updates(struct mansession *s, struct message *m, struct ast_c
                } else if (!strcasecmp(action, "renamecat")) {
                        if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
                                category = ast_category_get(cfg, cat);
-                               if (category)
+                               if (category) 
                                        ast_category_rename(category, value);
                        }
                } else if (!strcasecmp(action, "delcat")) {
@@ -1064,8 +1066,8 @@ static void handle_updates(struct mansession *s, struct message *m, struct ast_c
                        if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
                                ast_variable_delete(category, var, match);
                } else if (!strcasecmp(action, "append")) {
-                       if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
-                               (category = ast_category_get(cfg, cat)) &&
+                       if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && 
+                               (category = ast_category_get(cfg, cat)) && 
                                (v = ast_variable_new(var, value))){
                                if (match && !strcasecmp(match, "object"))
                                        v->object = 1;
@@ -1120,13 +1122,13 @@ static int action_updateconfig(struct mansession *s, struct message *m)
        if (!ast_strlen_zero(rld)) {
                if (ast_true(rld))
                        rld = NULL;
-               ast_module_reload(rld);
+               ast_module_reload(rld); 
        }
        return 0;
 }
 
 /*! \brief Manager WAITEVENT */
-static char mandescr_waitevent[] =
+static char mandescr_waitevent[] = 
 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
 "session, events will be generated and queued.\n"
@@ -1150,7 +1152,7 @@ static int action_waitevent(struct mansession *s, struct message *m)
        if (!ast_strlen_zero(timeouts)) {
                sscanf(timeouts, "%i", &timeout);
        }
-
+       
        ast_mutex_lock(&s->__lock);
        if (s->waiting_thread != AST_PTHREADT_NULL) {
                pthread_kill(s->waiting_thread, SIGURG);
@@ -1217,7 +1219,7 @@ static int action_waitevent(struct mansession *s, struct message *m)
        return 0;
 }
 
-static char mandescr_listcommands[] =
+static char mandescr_listcommands[] = 
 "Description: Returns the action name and synopsis for every\n"
 "  action that is available to the user\n"
 "Variables: NONE\n";
@@ -1244,7 +1246,7 @@ static int action_listcommands(struct mansession *s, struct message *m)
        return 0;
 }
 
-static char mandescr_events[] =
+static char mandescr_events[] = 
 "Description: Enable/Disable sending of events to this manager\n"
 "  client.\n"
 "Variables:\n"
@@ -1266,7 +1268,7 @@ static int action_events(struct mansession *s, struct message *m)
        return 0;
 }
 
-static char mandescr_logoff[] =
+static char mandescr_logoff[] = 
 "Description: Logoff this manager session\n"
 "Variables: NONE\n";
 
@@ -1276,7 +1278,7 @@ static int action_logoff(struct mansession *s, struct message *m)
        return -1;
 }
 
-static char mandescr_hangup[] =
+static char mandescr_hangup[] = 
 "Description: Hangup a channel\n"
 "Variables: \n"
 "      Channel: The channel name to be hungup\n";
@@ -1300,7 +1302,7 @@ static int action_hangup(struct mansession *s, struct message *m)
        return 0;
 }
 
-static char mandescr_setvar[] =
+static char mandescr_setvar[] = 
 "Description: Set a global or local channel variable.\n"
 "Variables: (Names marked with * are required)\n"
 "      Channel: Channel to set variable for\n"
@@ -1313,12 +1315,12 @@ static int action_setvar(struct mansession *s, struct message *m)
         char *name = astman_get_header(m, "Channel");
         char *varname = astman_get_header(m, "Variable");
         char *varval = astman_get_header(m, "Value");
-
+       
        if (ast_strlen_zero(varname)) {
                astman_send_error(s, m, "No variable specified");
                return 0;
        }
-
+       
        if (ast_strlen_zero(varval)) {
                astman_send_error(s, m, "No value specified");
                return 0;
@@ -1331,18 +1333,18 @@ static int action_setvar(struct mansession *s, struct message *m)
                        return 0;
                }
        }
-
+       
        pbx_builtin_setvar_helper(c, varname, varval);
-
+         
        if (c)
                ast_channel_unlock(c);
 
-       astman_send_ack(s, m, "Variable Set");
+       astman_send_ack(s, m, "Variable Set");  
 
        return 0;
 }
 
-static char mandescr_getvar[] =
+static char mandescr_getvar[] = 
 "Description: Get the value of a global or local channel variable.\n"
 "Variables: (Names marked with * are required)\n"
 "      Channel: Channel to read variable from\n"
@@ -1441,10 +1443,10 @@ static int action_status(struct mansession *s, struct message *m)
                        "Uniqueid: %s\r\n"
                        "%s"
                        "\r\n",
-                       c->name,
-                       S_OR(c->cid.cid_num, "<unknown>"),
-                       S_OR(c->cid.cid_num, "<unknown>"),
-                       S_OR(c->cid.cid_name, "<unknown>"),
+                       c->name, 
+                       S_OR(c->cid.cid_num, "<unknown>"), 
+                       S_OR(c->cid.cid_num, "<unknown>"), 
+                       S_OR(c->cid.cid_name, "<unknown>"), 
                        c->accountcode,
                        ast_state2str(c->_state), c->context,
                        c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
@@ -1462,10 +1464,10 @@ static int action_status(struct mansession *s, struct message *m)
                        "Uniqueid: %s\r\n"
                        "%s"
                        "\r\n",
-                       c->name,
-                       S_OR(c->cid.cid_num, "<unknown>"),
-                       S_OR(c->cid.cid_num, "<unknown>"),
-                       S_OR(c->cid.cid_name, "<unknown>"),
+                       c->name, 
+                       S_OR(c->cid.cid_num, "<unknown>"), 
+                       S_OR(c->cid.cid_num, "<unknown>"), 
+                       S_OR(c->cid.cid_name, "<unknown>"), 
                        c->accountcode,
                        ast_state2str(c->_state), bridge, c->uniqueid, idText);
                }
@@ -1481,7 +1483,7 @@ static int action_status(struct mansession *s, struct message *m)
        return 0;
 }
 
-static char mandescr_redirect[] =
+static char mandescr_redirect[] = 
 "Description: Redirect (transfer) a call.\n"
 "Variables: (Names marked with * are required)\n"
 "      *Channel: Channel to redirect\n"
@@ -1545,7 +1547,7 @@ static int action_redirect(struct mansession *s, struct message *m)
        return 0;
 }
 
-static char mandescr_command[] =
+static char mandescr_command[] = 
 "Description: Run a CLI command.\n"
 "Variables: (Names marked with * are required)\n"
 "      *Command: Asterisk CLI command to run\n"
@@ -1573,17 +1575,17 @@ static void *fast_originate(void *data)
        struct ast_channel *chan = NULL;
 
        if (!ast_strlen_zero(in->app)) {
-               res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
-                       S_OR(in->cid_num, NULL),
+               res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
+                       S_OR(in->cid_num, NULL), 
                        S_OR(in->cid_name, NULL),
                        in->vars, in->account, &chan);
        } else {
-               res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
-                       S_OR(in->cid_num, NULL),
+               res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
+                       S_OR(in->cid_num, NULL), 
                        S_OR(in->cid_name, NULL),
                        in->vars, in->account, &chan);
-       }
-
+       }   
+       
        /* Tell the manager what happened with the channel */
        manager_event(EVENT_FLAG_CALL,
                res ? "OriginateFailure" : "OriginateSuccess",
@@ -1596,7 +1598,7 @@ static void *fast_originate(void *data)
                "CallerID: %s\r\n"              /* This parameter is deprecated and will be removed post-1.4 */
                "CallerIDNum: %s\r\n"
                "CallerIDName: %s\r\n",
-               in->idtext, in->tech, in->data, in->context, in->exten, reason,
+               in->idtext, in->tech, in->data, in->context, in->exten, reason, 
                chan ? chan->uniqueid : "<null>",
                S_OR(in->cid_num, "<unknown>"),
                S_OR(in->cid_num, "<unknown>"),
@@ -1610,7 +1612,7 @@ static void *fast_originate(void *data)
        return NULL;
 }
 
-static char mandescr_originate[] =
+static char mandescr_originate[] = 
 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
 "  Application/Data\n"
 "Variables: (Names marked with * are required)\n"
@@ -1648,7 +1650,7 @@ static int action_originate(struct mansession *s, struct message *m)
        int reason = 0;
        char tmp[256];
        char tmp2[256];
-
+       
        pthread_t th;
        pthread_attr_t attr;
        if (!name) {
@@ -1699,7 +1701,7 @@ static int action_originate(struct mansession *s, struct message *m)
                                ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
                        if (n)
                                ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
-                       fast->vars = vars;
+                       fast->vars = vars;      
                        ast_copy_string(fast->context, context, sizeof(fast->context));
                        ast_copy_string(fast->exten, exten, sizeof(fast->exten));
                        ast_copy_string(fast->account, account, sizeof(fast->account));
@@ -1722,7 +1724,7 @@ static int action_originate(struct mansession *s, struct message *m)
                        astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
                        return 0;
                }
-       }
+       }   
        if (!res)
                astman_send_ack(s, m, "Originate successfully queued");
        else
@@ -1732,7 +1734,7 @@ static int action_originate(struct mansession *s, struct message *m)
 
 /*! \brief Help text for manager command mailboxstatus
  */
-static char mandescr_mailboxstatus[] =
+static char mandescr_mailboxstatus[] = 
 "Description: Checks a voicemail account for status.\n"
 "Variables: (Names marked with * are required)\n"
 "      *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
@@ -1764,7 +1766,7 @@ static int action_mailboxstatus(struct mansession *s, struct message *m)
        return 0;
 }
 
-static char mandescr_mailboxcount[] =
+static char mandescr_mailboxcount[] = 
 "Description: Checks a voicemail account for new messages.\n"
 "Variables: (Names marked with * are required)\n"
 "      *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
@@ -1794,13 +1796,13 @@ static int action_mailboxcount(struct mansession *s, struct message *m)
                                   "Message: Mailbox Message Count\r\n"
                                   "Mailbox: %s\r\n"
                                   "NewMessages: %d\r\n"
-                                  "OldMessages: %d\r\n"
+                                  "OldMessages: %d\r\n" 
                                   "\r\n",
                                    idText,mailbox, newmsgs, oldmsgs);
        return 0;
 }
 
-static char mandescr_extensionstate[] =
+static char mandescr_extensionstate[] = 
 "Description: Report the extension state for given extension.\n"
 "  If the extension has a hint, will use devicestate to check\n"
 "  the status of the device connected to the extension.\n"
@@ -1841,7 +1843,7 @@ static int action_extensionstate(struct mansession *s, struct message *m)
        return 0;
 }
 
-static char mandescr_timeout[] =
+static char mandescr_timeout[] = 
 "Description: Hangup a channel after a certain time.\n"
 "Variables: (Names marked with * are required)\n"
 "      *Channel: Channel name to hangup\n"
@@ -1981,7 +1983,7 @@ static int process_message(struct mansession *s, struct message *m)
                ast_mutex_lock(&s->__lock);
                s->busy++;
                ast_mutex_unlock(&s->__lock);
-               while (tmp) {
+               while (tmp) {           
                        if (!strcasecmp(action, tmp->action)) {
                                if ((s->writeperm & tmp->authority) == tmp->authority) {
                                        if (tmp->func(s, m))
@@ -2018,7 +2020,7 @@ static int get_input(struct mansession *s, char *output)
                        s->inlen -= (x + 1);
                        return 1;
                }
-       }
+       } 
        if (s->inlen >= sizeof(s->inbuf) - 1) {
                ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
                s->inlen = 0;
@@ -2062,7 +2064,7 @@ static void *session_do(void *data)
        struct mansession *s = data;
        struct message m;
        int res;
-
+       
        ast_mutex_lock(&s->__lock);
        astman_append(s, "Asterisk Call Manager/1.0\r\n");
        ast_mutex_unlock(&s->__lock);
@@ -2089,7 +2091,7 @@ static void *session_do(void *data)
        }
        if (s->authenticated) {
                if (option_verbose > 1) {
-                       if (displayconnects)
+                       if (displayconnects) 
                                ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
                }
                ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
@@ -2132,7 +2134,7 @@ static void *accept_thread(void *ignore)
                                                s->username, ast_inet_ntoa(s->sin.sin_addr));
                                }
                                free_session(s);
-                               break;
+                               break;  
                        }
                }
                AST_LIST_TRAVERSE_SAFE_END
@@ -2170,7 +2172,7 @@ static void *accept_thread(void *ignore)
                        continue;
 
                ast_atomic_fetchadd_int(&num_sessions, 1);
-
+               
                memcpy(&s->sin, &sin, sizeof(sin));
                s->writetimeout = 100;
                s->waiting_thread = AST_PTHREADT_NULL;
@@ -2210,18 +2212,18 @@ static int append_event(const char *str, int category)
        tmp->next = NULL;
        tmp->category = category;
        strcpy(tmp->eventdata, str);
-
+       
        if (master_eventq) {
                prev = master_eventq;
-               while (prev->next)
+               while (prev->next) 
                        prev = prev->next;
                prev->next = tmp;
        } else {
                master_eventq = tmp;
        }
-
+       
        tmp->usecount = num_sessions;
-
+       
        return 0;
 }
 
@@ -2255,11 +2257,11 @@ int manager_event(int category, const char *event, const char *fmt, ...)
        va_start(ap, fmt);
        ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
        va_end(ap);
-
-       ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
-
+       
+       ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");     
+       
        append_event(buf->str, category);
-
+       
        /* Append even to master list and wake up any sleeping sessions */
        AST_LIST_LOCK(&sessions);
        AST_LIST_TRAVERSE(&sessions, s, list) {
@@ -2273,7 +2275,7 @@ int manager_event(int category, const char *event, const char *fmt, ...)
        return 0;
 }
 
-int ast_manager_unregister(char *action)
+int ast_manager_unregister(char *action) 
 {
        struct manager_action *cur = first_action, *prev = first_action;
 
@@ -2282,7 +2284,7 @@ int ast_manager_unregister(char *action)
                if (!strcasecmp(action, cur->action)) {
                        prev->next = cur->next;
                        free(cur);
-                       if (option_verbose > 1)
+                       if (option_verbose > 1) 
                                ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
                        ast_mutex_unlock(&actionlock);
                        return 0;
@@ -2324,10 +2326,10 @@ static int ast_manager_register_struct(struct manager_action *act)
                        }
                        break;
                }
-               prev = cur;
+               prev = cur; 
                cur = cur->next;
        }
-
+       
        if (!cur) {
                if (prev)
                        prev->next = act;
@@ -2336,13 +2338,13 @@ static int ast_manager_register_struct(struct manager_action *act)
                act->next = NULL;
        }
 
-       if (option_verbose > 1)
+       if (option_verbose > 1) 
                ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
        ast_mutex_unlock(&actionlock);
        return 0;
 }
 
-/*! \brief register a new command with manager, including online help. This is
+/*! \brief register a new command with manager, including online help. This is 
        the preferred way to register a manager command */
 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
 {
@@ -2351,7 +2353,7 @@ int ast_manager_register2(const char *action, int auth, int (*func)(struct manse
        cur = ast_malloc(sizeof(*cur));
        if (!cur)
                return -1;
-
+       
        cur->action = action;
        cur->authority = auth;
        cur->func = func;
@@ -2422,7 +2424,7 @@ static char *generic_http_callback(int format, struct sockaddr_in *requestor, co
                        break;
                }
        }
-
+       
        if (!(s = find_session(ident))) {
                /* Create new session */
                if (!(s = ast_calloc(1, sizeof(*s)))) {
@@ -2455,7 +2457,7 @@ static char *generic_http_callback(int format, struct sockaddr_in *requestor, co
        else
                s->sessiontimeout += httptimeout;
        ast_mutex_unlock(&s->__lock);
-
+       
        memset(&m, 0, sizeof(m));
        if (s) {
                char tmp[80];
@@ -2474,8 +2476,8 @@ static char *generic_http_callback(int format, struct sockaddr_in *requestor, co
                if (process_message(s, &m)) {
                        if (s->authenticated) {
                                if (option_verbose > 1) {
-                                       if (displayconnects)
-                                               ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+                                       if (displayconnects) 
+                                               ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));    
                                }
                                ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
                        } else {
@@ -2533,12 +2535,12 @@ static char *generic_http_callback(int format, struct sockaddr_in *requestor, co
        } else
                s->inuse--;
        ast_mutex_unlock(&s->__lock);
-
+       
        if (blastaway)
                destroy_session(s);
 generic_callback_out:
        if (*status != 200)
-               return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
+               return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); 
        return retval;
 }
 
@@ -2583,15 +2585,16 @@ static int webregged = 0;
 
 int init_manager(void)
 {
-       struct ast_config *cfg;
-       char *val,*cat;
+       struct ast_config *cfg = NULL;
+       char *val, *cat = NULL;
        int oldportno = portno;
        static struct sockaddr_in ba;
        int x = 1;
        int flags;
        int webenabled = 0;
        int newhttptimeout = 60;
-       amus = NULL;
+       struct ast_manager_user *user = NULL;
+
        if (!registered) {
                /* Register default actions */
                ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
@@ -2614,13 +2617,7 @@ int init_manager(void)
                ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
                ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
 
-               ast_cli_register(&show_mancmd_cli);
-               ast_cli_register(&show_mancmds_cli);
-               ast_cli_register(&show_manconn_cli);
-               ast_cli_register(&show_maneventq_cli);
-               ast_cli_register(&show_managers_cli);
-               ast_cli_register(&show_manager_cli);
-
+               ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
                ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
                registered = 1;
                /* Append placeholder event so master_eventq never runs dry */
@@ -2666,12 +2663,12 @@ int init_manager(void)
        ba.sin_port = htons(portno);
 
        if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
-               if (!inet_aton(val, &ba.sin_addr)) {
+               if (!inet_aton(val, &ba.sin_addr)) { 
                        ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
                        memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
                }
        }
-
+       
 
        if ((asock > -1) && ((portno != oldportno) || !enabled)) {
 #if 0
@@ -2683,61 +2680,84 @@ int init_manager(void)
 #endif
        }
 
+       AST_LIST_LOCK(&users);
 
+       while ((cat = ast_category_browse(cfg, cat))) {
+               struct ast_variable *var = NULL;
 
-               cat = ast_category_browse(cfg, NULL);
-               amus=NULL; /* Resetting the boss */
-               while(cat) {
-                       if (!strcasecmp(cat, "general")) {
-                               ast_log(LOG_NOTICE, "ignoring the cat general\n");
-                               cat = ast_category_browse(cfg, cat);
-                               continue;
-                       }
+               if (!strcasecmp(cat, "general"))
+                       continue;
 
-                       struct ast_manager_user *amu = malloc(sizeof(struct ast_manager_user));
-                       memset(amu, 0, sizeof(struct ast_manager_user));
-                       struct ast_variable *var;
-                       var = ast_variable_browse(cfg, cat);
-
-                       while (var) {
-                               if (!strcasecmp(var->name, "secret")) {
-                                       if (amu->secret)
-                                               free(amu->secret);
-                                       amu->secret=strdup(var->value);
-                               } else if (!strcasecmp(var->name, "deny") ) {
-                                       if (amu->deny)
-                                               free(amu->deny);
-                                       amu->deny=strdup(var->value);
-                               } else if (!strcasecmp(var->name, "permit") ) {
-                                       if (amu->permit)
-                                               free(amu->permit);
-                                       amu->permit=strdup(var->value);
-                               }  else if (!strcasecmp(var->name, "read") ) {
-                                       if (amu->read)
-                                               free(amu->read);
-                                       amu->read=strdup(var->value);
-                               }  else if (!strcasecmp(var->name, "write") ) {
-                                       if (amu->write)
-                                               free(amu->write);
-                                       amu->write=strdup(var->value);
-                               }  else if (!strcasecmp(var->name, "displayconnects") ) {
-                                       amu->displayconnects=ast_true(var->value);
-                               } else {
-                                       ast_log(LOG_DEBUG, "%s is unknown.\n",var->name);
-                               }
-                               var = var->next;
-                       }
+               /* Look for an existing entry, if none found - create one and add it to the list */
+               if (!(user = ast_get_manager_by_name_locked(cat))) {
+                       if (!(user = ast_calloc(1, sizeof(*user))))
+                               break;
+                       /* Copy name over */
+                       ast_copy_string(user->username, cat, sizeof(user->username));
+                       /* Insert into list */
+                       AST_LIST_INSERT_TAIL(&users, user, list);
+               }
 
-                       ast_copy_string(amu->username,cat,sizeof(amu->username));
+               /* Make sure we keep this user and don't destroy it during cleanup */
+               user->keep = 1;
+
+               var = ast_variable_browse(cfg, cat);
+               while (var) {
+                       if (!strcasecmp(var->name, "secret")) {
+                               if (user->secret)
+                                       free(user->secret);
+                               user->secret = ast_strdup(var->value);
+                       } else if (!strcasecmp(var->name, "deny") ) {
+                               if (user->deny)
+                                       free(user->deny);
+                               user->deny = ast_strdup(var->value);
+                       } else if (!strcasecmp(var->name, "permit") ) {
+                               if (user->permit)
+                                       free(user->permit);
+                               user->permit = ast_strdup(var->value);
+                       }  else if (!strcasecmp(var->name, "read") ) {
+                               if (user->read)
+                                       free(user->read);
+                               user->read = ast_strdup(var->value);
+                       }  else if (!strcasecmp(var->name, "write") ) {
+                               if (user->write)
+                                       free(user->write);
+                               user->write = ast_strdup(var->value);
+                       }  else if (!strcasecmp(var->name, "displayconnects") )
+                               user->displayconnects = ast_true(var->value);
+                       else
+                               ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
+                       var = var->next;
+               }
+       }
 
-                       ast_log(LOG_DEBUG, "Adding %s\n",amu->username);
-                       ast_manager_user_add(amu);
-                       amu=NULL;
-                       cat = ast_category_browse(cfg, cat);
+       /* Perform cleanup - essentially prune out old users that no longer exist */
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
+               if (user->keep) {
+                       user->keep = 0;
+                       continue;
                }
+               /* We do not need to keep this user so take them out of the list */
+               AST_LIST_REMOVE_CURRENT(&users, list);
+               /* Free their memory now */
+               if (user->secret)
+                       free(user->secret);
+               if (user->deny)
+                       free(user->deny);
+               if (user->permit)
+                       free(user->permit);
+               if (user->read)
+                       free(user->read);
+               if (user->write)
+                       free(user->write);
+               free(user);
+       }
+       AST_LIST_TRAVERSE_SAFE_END
+
+       AST_LIST_UNLOCK(&users);
 
        ast_config_destroy(cfg);
-
+       
        if (webenabled && enabled) {
                if (!webregged) {
                        ast_http_uri_link(&rawmanuri);
@@ -2794,4 +2814,3 @@ int reload_manager(void)
        manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
        return init_manager();
 }
-