CI: Various updates to buildAsterisk.sh
[asterisk/asterisk.git] / main / manager.c
index 079dab7..3e41198 100644 (file)
                                <parameter name="DNID">
                                        <para>Dialed number identifier</para>
                                </parameter>
+                               <parameter name="EffectiveConnectedLineNum">
+                               </parameter>
+                               <parameter name="EffectiveConnectedLineName">
+                               </parameter>
                                <parameter name="TimeToHangup">
                                        <para>Absolute lifetime of the channel</para>
                                </parameter>
                                <parameter name="BridgeID">
                                        <para>Identifier of the bridge the channel is in, may be empty if not in one</para>
                                </parameter>
-                               <parameter name="Linkedid">
-                               </parameter>
                                <parameter name="Application">
                                        <para>Application currently executing on the channel</para>
                                </parameter>
@@ -1525,6 +1527,8 @@ static void acl_change_stasis_subscribe(void)
        if (!acl_change_sub) {
                acl_change_sub = stasis_subscribe(ast_security_topic(),
                        acl_change_stasis_cb, NULL);
+               stasis_subscription_accept_message_type(acl_change_sub, ast_named_acl_change_type());
+               stasis_subscription_set_filter(acl_change_sub, STASIS_SUBSCRIPTION_FILTER_SELECTIVE);
        }
 }
 
@@ -1768,7 +1772,12 @@ void manager_json_to_ast_str(struct ast_json *obj, const char *key,
 {
        struct ast_json_iter *i;
 
-       if (!obj || (!res && !(*res) && (!(*res = ast_str_create(1024))))) {
+       /* If obj or res is not given, just return */
+       if (!obj || !res) {
+               return;
+       }
+
+       if (!*res && !(*res = ast_str_create(1024))) {
                return;
        }
 
@@ -1799,11 +1808,14 @@ void manager_json_to_ast_str(struct ast_json *obj, const char *key,
        }
 }
 
-
 struct ast_str *ast_manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb)
 {
        struct ast_str *res = ast_str_create(1024);
-       manager_json_to_ast_str(blob, NULL, &res, exclusion_cb);
+
+       if (!ast_json_is_null(blob)) {
+          manager_json_to_ast_str(blob, NULL, &res, exclusion_cb);
+       }
+
        return res;
 }
 
@@ -2212,8 +2224,8 @@ static struct mansession_session *build_mansession(const struct ast_sockaddr *ad
                return NULL;
        }
 
-       newsession->whitefilters = ao2_container_alloc(1, NULL, NULL);
-       newsession->blackfilters = ao2_container_alloc(1, NULL, NULL);
+       newsession->whitefilters = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
+       newsession->blackfilters = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
        if (!newsession->whitefilters || !newsession->blackfilters) {
                ao2_ref(newsession, -1);
                return NULL;
@@ -2315,9 +2327,9 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
 {
        struct manager_action *cur;
        struct ast_str *authority;
-       int num, l, which;
+       int num;
+       int l;
        const char *auth_str;
-       char *ret = NULL;
 #ifdef AST_XML_DOCS
        char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64];
        char arguments_title[64], privilege_title[64], final_response_title[64], list_responses_title[64];
@@ -2332,22 +2344,23 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
                return NULL;
        case CLI_GENERATE:
                l = strlen(a->word);
-               which = 0;
                AST_RWLIST_RDLOCK(&actions);
                AST_RWLIST_TRAVERSE(&actions, cur, list) {
-                       if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
-                               ret = ast_strdup(cur->action);
-                               break;  /* make sure we exit even if ast_strdup() returns NULL */
+                       if (!strncasecmp(a->word, cur->action, l)) {
+                               if (ast_cli_completion_add(ast_strdup(cur->action))) {
+                                       break;
+                               }
                        }
                }
                AST_RWLIST_UNLOCK(&actions);
-               return ret;
+               return NULL;
        }
-       authority = ast_str_alloca(MAX_AUTH_PERM_STRING);
        if (a->argc < 4) {
                return CLI_SHOWUSAGE;
        }
 
+       authority = ast_str_alloca(MAX_AUTH_PERM_STRING);
+
 #ifdef AST_XML_DOCS
        /* setup the titles */
        term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
@@ -2375,6 +2388,22 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
                                        char *seealso = ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1);
                                        char *privilege = ast_xmldoc_printable(S_OR(auth_str, "Not available"), 1);
                                        char *responses = ast_xmldoc_printable("None", 1);
+
+                                       if (!syntax || !synopsis || !description || !arguments
+                                                       || !seealso || !privilege || !responses) {
+                                               ast_free(syntax);
+                                               ast_free(synopsis);
+                                               ast_free(description);
+                                               ast_free(arguments);
+                                               ast_free(seealso);
+                                               ast_free(privilege);
+                                               ast_free(responses);
+                                               ast_cli(a->fd, "Allocation failure.\n");
+                                               AST_RWLIST_UNLOCK(&actions);
+
+                                               return CLI_FAILURE;
+                                       }
+
                                        ast_cli(a->fd, "%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s",
                                                syntax_title, syntax,
                                                synopsis_title, synopsis,
@@ -2402,6 +2431,14 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
                                                ast_cli(a->fd, "Event: %s\n", cur->final_response->name);
                                                print_event_instance(a, cur->final_response);
                                        }
+
+                                       ast_free(syntax);
+                                       ast_free(synopsis);
+                                       ast_free(description);
+                                       ast_free(arguments);
+                                       ast_free(seealso);
+                                       ast_free(privilege);
+                                       ast_free(responses);
                                } else
 #endif
                                {
@@ -2446,8 +2483,7 @@ static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        struct ast_manager_user *user = NULL;
-       int l, which;
-       char *ret = NULL;
+       int l;
        struct ast_str *rauthority = ast_str_alloca(MAX_AUTH_PERM_STRING);
        struct ast_str *wauthority = ast_str_alloca(MAX_AUTH_PERM_STRING);
        struct ast_variable *v;
@@ -2461,19 +2497,19 @@ static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli
                return NULL;
        case CLI_GENERATE:
                l = strlen(a->word);
-               which = 0;
                if (a->pos != 3) {
                        return NULL;
                }
                AST_RWLIST_RDLOCK(&users);
                AST_RWLIST_TRAVERSE(&users, user, list) {
-                       if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
-                               ret = ast_strdup(user->username);
-                               break;
+                       if (!strncasecmp(a->word, user->username, l)) {
+                               if (ast_cli_completion_add(ast_strdup(user->username))) {
+                                       break;
+                               }
                        }
                }
                AST_RWLIST_UNLOCK(&users);
-               return ret;
+               return NULL;
        }
 
        if (a->argc != 4) {
@@ -2675,6 +2711,8 @@ static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_c
        return CLI_SUCCESS;
 }
 
+static int reload_module(void);
+
 /*! \brief CLI command manager reload */
 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -2691,7 +2729,7 @@ static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_
        if (a->argc > 2) {
                return CLI_SHOWUSAGE;
        }
-       reload_manager();
+       reload_module();
        return CLI_SUCCESS;
 }
 
@@ -2768,6 +2806,34 @@ const char *astman_get_header(const struct message *m, char *var)
 }
 
 /*!
+ * \brief Append additional headers into the message structure from params.
+ *
+ * \note You likely want to initialize m->hdrcount to 0 before calling this.
+ */
+static void astman_append_headers(struct message *m, const struct ast_variable *params)
+{
+       const struct ast_variable *v;
+
+       for (v = params; v && m->hdrcount < ARRAY_LEN(m->headers); v = v->next) {
+               if (ast_asprintf((char**)&m->headers[m->hdrcount], "%s: %s", v->name, v->value) > -1) {
+                       ++m->hdrcount;
+               }
+       }
+}
+
+/*!
+ * \brief Free headers inside message structure, but not the message structure itself.
+ */
+static void astman_free_headers(struct message *m)
+{
+       while (m->hdrcount) {
+               --m->hdrcount;
+               ast_free((void *) m->headers[m->hdrcount]);
+               m->headers[m->hdrcount] = NULL;
+       }
+}
+
+/*!
  * \internal
  * \brief Process one "Variable:" header value string.
  *
@@ -2896,34 +2962,41 @@ int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
        }
 
        action = astman_get_header(&m, "Action");
-       if (strcasecmp(action, "login")) {
+
+       do {
+               if (!strcasecmp(action, "login")) {
+                       break;
+               }
+
                act_found = action_find(action);
-               if (act_found) {
-                       /*
-                        * we have to simulate a session for this action request
-                        * to be able to pass it down for processing
-                        * This is necessary to meet the previous design of manager.c
-                        */
-                       s.hook = hook;
+               if (!act_found) {
+                       break;
+               }
 
-                       ao2_lock(act_found);
-                       if (act_found->registered && act_found->func) {
-                               if (act_found->module) {
-                                       ast_module_ref(act_found->module);
-                               }
-                               ao2_unlock(act_found);
+               /*
+                * we have to simulate a session for this action request
+                * to be able to pass it down for processing
+                * This is necessary to meet the previous design of manager.c
+                */
+               s.hook = hook;
+
+               ret = -1;
+               ao2_lock(act_found);
+               if (act_found->registered && act_found->func) {
+                       struct ast_module *mod_ref = ast_module_running_ref(act_found->module);
+
+                       ao2_unlock(act_found);
+                       /* If the action is in a module it must be running. */
+                       if (!act_found->module || mod_ref) {
                                ret = act_found->func(&s, &m);
-                               ao2_lock(act_found);
-                               if (act_found->module) {
-                                       ast_module_unref(act_found->module);
-                               }
-                       } else {
-                               ret = -1;
+                               ast_module_unref(mod_ref);
                        }
+               } else {
                        ao2_unlock(act_found);
-                       ao2_t_ref(act_found, -1, "done with found action object");
                }
-       }
+               ao2_t_ref(act_found, -1, "done with found action object");
+       } while (0);
+
        ast_free(dup_str);
        return ret;
 }
@@ -4589,7 +4662,6 @@ static void generate_status(struct mansession *s, struct ast_channel *chan, char
                "EffectiveConnectedLineName: %s\r\n"
                "TimeToHangup: %ld\r\n"
                "BridgeID: %s\r\n"
-               "Linkedid: %s\r\n"
                "Application: %s\r\n"
                "Data: %s\r\n"
                "Nativeformats: %s\r\n"
@@ -4610,7 +4682,6 @@ static void generate_status(struct mansession *s, struct ast_channel *chan, char
                S_COR(effective_id.name.valid, effective_id.name.str, "<unknown>"),
                (long)ast_channel_whentohangup(chan)->tv_sec,
                bridge ? bridge->uniqueid : "",
-               ast_channel_linkedid(chan),
                ast_channel_appl(chan),
                ast_channel_data(chan),
                ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf),
@@ -4704,10 +4775,13 @@ static int action_status(struct mansession *s, const struct message *m)
 
 static int action_sendtext(struct mansession *s, const struct message *m)
 {
-       struct ast_channel *c = NULL;
+       struct ast_channel *c;
        const char *name = astman_get_header(m, "Channel");
        const char *textmsg = astman_get_header(m, "Message");
-       int res = 0;
+       struct ast_control_read_action_payload *frame_payload;
+       int payload_size;
+       int frame_size;
+       int res;
 
        if (ast_strlen_zero(name)) {
                astman_send_error(s, m, "No channel specified");
@@ -4719,13 +4793,29 @@ static int action_sendtext(struct mansession *s, const struct message *m)
                return 0;
        }
 
-       if (!(c = ast_channel_get_by_name(name))) {
+       c = ast_channel_get_by_name(name);
+       if (!c) {
                astman_send_error(s, m, "No such channel");
                return 0;
        }
 
-       res = ast_sendtext(c, textmsg);
-       c = ast_channel_unref(c);
+       payload_size = strlen(textmsg) + 1;
+       frame_size = payload_size + sizeof(*frame_payload);
+
+       frame_payload = ast_malloc(frame_size);
+       if (!frame_payload) {
+               ast_channel_unref(c);
+               astman_send_error(s, m, "Failure");
+               return 0;
+       }
+
+       frame_payload->action = AST_FRAME_READ_ACTION_SEND_TEXT;
+       frame_payload->payload_size = payload_size;
+       memcpy(frame_payload->payload, textmsg, payload_size);
+       res = ast_queue_control_data(c, AST_CONTROL_READ_ACTION, frame_payload, frame_size);
+
+       ast_free(frame_payload);
+       ast_channel_unref(c);
 
        if (res >= 0) {
                astman_send_ack(s, m, "Success");
@@ -6160,7 +6250,7 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
        int numchans = 0;
        struct ao2_container *channels;
        struct ao2_iterator it_chans;
-       struct stasis_message *msg;
+       struct ast_channel_snapshot *cs;
 
        if (!ast_strlen_zero(actionid)) {
                snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
@@ -6168,17 +6258,12 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
                idText[0] = '\0';
        }
 
-       channels = stasis_cache_dump(ast_channel_cache_by_name(), ast_channel_snapshot_type());
-       if (!channels) {
-               astman_send_error(s, m, "Could not get cached channels");
-               return 0;
-       }
+       channels = ast_channel_cache_by_name();
 
        astman_send_listack(s, m, "Channels will follow", "start");
 
        it_chans = ao2_iterator_init(channels, 0);
-       for (; (msg = ao2_iterator_next(&it_chans)); ao2_ref(msg, -1)) {
-               struct ast_channel_snapshot *cs = stasis_message_data(msg);
+       for (; (cs = ao2_iterator_next(&it_chans)); ao2_ref(cs, -1)) {
                struct ast_str *built = ast_manager_build_channel_state_string_prefix(cs, "");
                char durbuf[16] = "";
 
@@ -6186,10 +6271,10 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
                        continue;
                }
 
-               if (!ast_tvzero(cs->creationtime)) {
+               if (!ast_tvzero(cs->base->creationtime)) {
                        int duration, durh, durm, durs;
 
-                       duration = (int)(ast_tvdiff_ms(ast_tvnow(), cs->creationtime) / 1000);
+                       duration = (int)(ast_tvdiff_ms(ast_tvnow(), cs->base->creationtime) / 1000);
                        durh = duration / 3600;
                        durm = (duration % 3600) / 60;
                        durs = duration % 60;
@@ -6207,10 +6292,10 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
                        "\r\n",
                        idText,
                        ast_str_buffer(built),
-                       cs->appl,
-                       cs->data,
+                       cs->dialplan->appl,
+                       cs->dialplan->data,
                        durbuf,
-                       cs->bridgeid);
+                       cs->bridge->id);
 
                numchans++;
 
@@ -6417,21 +6502,21 @@ static int process_message(struct mansession *s, const struct message *m)
                if ((s->session->writeperm & act_found->authority)
                        || act_found->authority == 0) {
                        /* We have the authority to execute the action. */
+                       ret = -1;
                        ao2_lock(act_found);
                        if (act_found->registered && act_found->func) {
-                               ast_debug(1, "Running action '%s'\n", act_found->action);
-                               if (act_found->module) {
-                                       ast_module_ref(act_found->module);
-                               }
+                               struct ast_module *mod_ref = ast_module_running_ref(act_found->module);
+
                                ao2_unlock(act_found);
-                               ret = act_found->func(s, m);
-                               acted = 1;
-                               ao2_lock(act_found);
-                               if (act_found->module) {
-                                       ast_module_unref(act_found->module);
+                               if (mod_ref || !act_found->module) {
+                                       ast_debug(1, "Running action '%s'\n", act_found->action);
+                                       ret = act_found->func(s, m);
+                                       acted = 1;
+                                       ast_module_unref(mod_ref);
                                }
+                       } else {
+                               ao2_unlock(act_found);
                        }
-                       ao2_unlock(act_found);
                }
                if (!acted) {
                        /*
@@ -6598,7 +6683,6 @@ static int do_message(struct mansession *s)
        struct message m = { 0 };
        char header_buf[sizeof(s->session->inbuf)] = { '\0' };
        int res;
-       int idx;
        int hdr_loss;
        time_t now;
 
@@ -6666,10 +6750,8 @@ static int do_message(struct mansession *s)
                }
        }
 
-       /* Free AMI request headers. */
-       for (idx = 0; idx < m.hdrcount; ++idx) {
-               ast_free((void *) m.headers[idx]);
-       }
+       astman_free_headers(&m);
+
        return res;
 }
 
@@ -7552,7 +7634,8 @@ static void xml_translate(struct ast_str **out, char *in, struct ast_variable *g
                        if (xml) {
                                ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
                        }
-                       vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
+                       vco = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 37,
+                               variable_count_hash_fn, NULL, variable_count_cmp_fn);
                        inobj = 1;
                }
 
@@ -7632,7 +7715,7 @@ static void process_output(struct mansession *s, struct ast_str **out, struct as
 
        fd = ast_iostream_get_fd(s->stream);
 
-       l = lseek(fd, SEEK_CUR, 0);
+       l = lseek(fd, 0, SEEK_CUR);
        if (l > 0) {
                if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0))) {
                        ast_log(LOG_WARNING, "mmap failed.  Manager output was not processed\n");
@@ -7663,13 +7746,10 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
        uint32_t ident;
        int fd;
        int blastaway = 0;
-       struct ast_variable *v;
        struct ast_variable *params = get_params;
        char template[] = "/tmp/ast-http-XXXXXX";       /* template for temporary file */
        struct ast_str *http_header = NULL, *out = NULL;
        struct message m = { 0 };
-       unsigned int idx;
-       size_t hdrlen;
 
        if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
                ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
@@ -7752,17 +7832,7 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
                }
        }
 
-       for (v = params; v && m.hdrcount < ARRAY_LEN(m.headers); v = v->next) {
-               hdrlen = strlen(v->name) + strlen(v->value) + 3;
-               m.headers[m.hdrcount] = ast_malloc(hdrlen);
-               if (!m.headers[m.hdrcount]) {
-                       /* Allocation failure */
-                       continue;
-               }
-               snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
-               ast_debug(1, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
-               ++m.hdrcount;
-       }
+       astman_append_headers(&m, params);
 
        if (process_message(&s, &m)) {
                if (session->authenticated) {
@@ -7777,11 +7847,7 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
                session->needdestroy = 1;
        }
 
-       /* Free request headers. */
-       for (idx = 0; idx < m.hdrcount; ++idx) {
-               ast_free((void *) m.headers[idx]);
-               m.headers[idx] = NULL;
-       }
+       astman_free_headers(&m);
 
        ast_str_append(&http_header, 0,
                "Content-type: text/%s\r\n"
@@ -7892,8 +7958,6 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
        struct ast_str *http_header = NULL, *out = NULL;
        size_t result_size;
        struct message m = { 0 };
-       unsigned int idx;
-       size_t hdrlen;
        int fd;
 
        time_t time_now = time(NULL);
@@ -7965,13 +8029,21 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
 
        /* compute the expected response to compare with what we received */
        {
-               char a2[256];
-               char a2_hash[256];
+               char *a2;
+               /* ast_md5_hash outputs 32 characters plus NULL terminator. */
+               char a2_hash[33];
                char resp[256];
 
                /* XXX Now request method are hardcoded in A2 */
-               snprintf(a2, sizeof(a2), "%s:%s", ast_get_http_method(method), d.uri);
+               if (ast_asprintf(&a2, "%s:%s", ast_get_http_method(method), d.uri) < 0) {
+                       AST_RWLIST_UNLOCK(&users);
+                       ast_http_request_close_on_completion(ser);
+                       ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
+                       return 0;
+               }
+
                ast_md5_hash(a2_hash, a2);
+               ast_free(a2);
 
                if (d.qop) {
                        /* RFC 2617 */
@@ -8108,17 +8180,7 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
                }
        }
 
-       for (v = params; v && m.hdrcount < ARRAY_LEN(m.headers); v = v->next) {
-               hdrlen = strlen(v->name) + strlen(v->value) + 3;
-               m.headers[m.hdrcount] = ast_malloc(hdrlen);
-               if (!m.headers[m.hdrcount]) {
-                       /* Allocation failure */
-                       continue;
-               }
-               snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
-               ast_verb(4, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
-               ++m.hdrcount;
-       }
+       astman_append_headers(&m, params);
 
        if (process_message(&s, &m)) {
                if (u_displayconnects) {
@@ -8128,13 +8190,9 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
                session->needdestroy = 1;
        }
 
-       /* Free request headers. */
-       for (idx = 0; idx < m.hdrcount; ++idx) {
-               ast_free((void *) m.headers[idx]);
-               m.headers[idx] = NULL;
-       }
+       astman_free_headers(&m);
 
-       result_size = lseek(ast_iostream_get_fd(s.stream), SEEK_CUR, 0); /* Calculate approx. size of result */
+       result_size = lseek(ast_iostream_get_fd(s.stream), 0, SEEK_CUR); /* Calculate approx. size of result */
 
        http_header = ast_str_create(80);
        out = ast_str_create(result_size * 2 + 512);
@@ -8453,7 +8511,7 @@ static char *handle_manager_show_settings(struct ast_cli_entry *e, int cmd, stru
        ast_cli(a->fd, FORMAT, "Manager (AMI):", AST_CLI_YESNO(manager_enabled));
        ast_cli(a->fd, FORMAT, "Web Manager (AMI/HTTP):", AST_CLI_YESNO(webmanager_enabled));
        ast_cli(a->fd, FORMAT, "TCP Bindaddress:", manager_enabled != 0 ? ast_sockaddr_stringify(&ami_desc.local_address) : "Disabled");
-       ast_cli(a->fd, FORMAT2, "HTTP Timeout (minutes):", httptimeout);
+       ast_cli(a->fd, FORMAT2, "HTTP Timeout (seconds):", httptimeout);
        ast_cli(a->fd, FORMAT, "TLS Enable:", AST_CLI_YESNO(ami_tls_cfg.enabled));
        ast_cli(a->fd, FORMAT, "TLS Bindaddress:", ami_tls_cfg.enabled != 0 ? ast_sockaddr_stringify(&amis_desc.local_address) : "Disabled");
        ast_cli(a->fd, FORMAT, "TLS Certfile:", ami_tls_cfg.certfile);
@@ -8603,8 +8661,6 @@ static char *handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct
        struct ao2_iterator it_events;
        struct ast_xml_doc_item *item, *temp;
        int length;
-       int which;
-       char *match = NULL;
 
        if (cmd == CLI_INIT) {
                e->command = "manager show event";
@@ -8621,19 +8677,24 @@ static char *handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct
        }
 
        if (cmd == CLI_GENERATE) {
+               if (a->pos != 3) {
+                       return NULL;
+               }
+
                length = strlen(a->word);
-               which = 0;
                it_events = ao2_iterator_init(events, 0);
                while ((item = ao2_iterator_next(&it_events))) {
-                       if (!strncasecmp(a->word, item->name, length) && ++which > a->n) {
-                               match = ast_strdup(item->name);
-                               ao2_ref(item, -1);
-                               break;
+                       if (!strncasecmp(a->word, item->name, length)) {
+                               if (ast_cli_completion_add(ast_strdup(item->name))) {
+                                       ao2_ref(item, -1);
+                                       break;
+                               }
                        }
                        ao2_ref(item, -1);
                }
                ao2_iterator_destroy(&it_events);
-               return match;
+
+               return NULL;
        }
 
        if (a->argc != 4) {
@@ -8931,8 +8992,6 @@ static int __init_manager(int reload, int by_external_config)
 #endif
                int res;
 
-               ast_register_cleanup(manager_shutdown);
-
                res = STASIS_MESSAGE_TYPE_INIT(ast_manager_get_generic_type);
                if (res != 0) {
                        return -1;
@@ -9004,7 +9063,7 @@ static int __init_manager(int reload, int by_external_config)
 #endif
 
                /* If you have a NULL hash fn, you only need a single bucket */
-               sessions = ao2_container_alloc(1, NULL, mansession_cmp_fn);
+               sessions = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, mansession_cmp_fn);
                if (!sessions) {
                        return -1;
                }
@@ -9260,8 +9319,8 @@ static int __init_manager(int reload, int by_external_config)
                        /* Default allowmultiplelogin from [general] */
                        user->allowmultiplelogin = allowmultiplelogin;
                        user->writetimeout = 100;
-                       user->whitefilters = ao2_container_alloc(1, NULL, NULL);
-                       user->blackfilters = ao2_container_alloc(1, NULL, NULL);
+                       user->whitefilters = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
+                       user->blackfilters = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
                        if (!user->whitefilters || !user->blackfilters) {
                                manager_free_user(user);
                                break;
@@ -9415,12 +9474,19 @@ static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub,
        __init_manager(1, 1);
 }
 
-int init_manager(void)
+static int unload_module(void)
 {
-       return __init_manager(0, 0);
+       return 0;
 }
 
-int reload_manager(void)
+static int load_module(void)
+{
+       ast_register_cleanup(manager_shutdown);
+
+       return __init_manager(0, 0) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS;
+}
+
+static int reload_module(void)
 {
        return __init_manager(1, 0);
 }
@@ -9465,23 +9531,16 @@ struct ast_datastore *astman_datastore_find(struct mansession *s, const struct a
 }
 
 int ast_str_append_event_header(struct ast_str **fields_string,
-                                       const char *header, const char *value)
+       const char *header, const char *value)
 {
-       struct ast_str *working_str = *fields_string;
-
-       if (!working_str) {
-               working_str = ast_str_create(128);
-               if (!working_str) {
+       if (!*fields_string) {
+               *fields_string = ast_str_create(128);
+               if (!*fields_string) {
                        return -1;
                }
-               *fields_string = working_str;
        }
 
-       ast_str_append(&working_str, 0,
-               "%s: %s\r\n",
-               header, value);
-
-       return 0;
+       return (ast_str_append(fields_string, 0, "%s: %s\r\n", header, value) < 0) ? -1 : 0;
 }
 
 static void manager_event_blob_dtor(void *obj)
@@ -9524,3 +9583,12 @@ ast_manager_event_blob_create(
 
        return ev;
 }
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk Manager Interface",
+       .support_level = AST_MODULE_SUPPORT_CORE,
+       .load = load_module,
+       .unload = unload_module,
+       .reload = reload_module,
+       .load_pri = AST_MODPRI_CORE,
+       .requires = "extconfig,acl,http",
+);