Skip to content
Snippets Groups Projects
manager.c 282 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	{ EVENT_FLAG_DTMF, "dtmf" },
    
    	{ EVENT_FLAG_REPORTING, "reporting" },
    
    	{ EVENT_FLAG_DIALPLAN, "dialplan" },
    
    	{ EVENT_FLAG_CC, "cc" },
    
    	{ EVENT_FLAG_AOC, "aoc" },
    
    	{ EVENT_FLAG_TEST, "test" },
    
    	{ EVENT_FLAG_SECURITY, "security" },
    
    	{ EVENT_FLAG_MESSAGE, "message" },
    
    	{ INT_MAX, "all" },
    
    /*! Maximum string length of the AMI authority permission string buildable from perms[]. */
    #define MAX_AUTH_PERM_STRING	150
    
    
    /*! \brief Checks to see if a string which can be used to evaluate functions should be rejected */
    static int function_capable_string_allowed_with_auths(const char *evaluating, int writepermlist)
    {
    	if (!(writepermlist & EVENT_FLAG_SYSTEM)
    		&& (
    			strstr(evaluating, "SHELL") ||       /* NoOp(${SHELL(rm -rf /)})  */
    			strstr(evaluating, "EVAL")           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
    		)) {
    		return 0;
    	}
    	return 1;
    }
    
    
    /*! \brief Convert authority code to a list of options for a user. This will only
     * display those authority codes that have an explicit match on authority */
    static const char *user_authority_to_str(int authority, struct ast_str **res)
    {
    	int i;
    	char *sep = "";
    
    	ast_str_reset(*res);
    	for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
    		if ((authority & perms[i].num) == perms[i].num) {
    			ast_str_append(res, 0, "%s%s", sep, perms[i].label);
    			sep = ",";
    		}
    	}
    
    
    	if (ast_str_strlen(*res) == 0) {
    		/* replace empty string with something sensible */
    
    
    	return ast_str_buffer(*res);
    }
    
    
    /*! \brief Convert authority code to a list of options. Note that the EVENT_FLAG_ALL
     * authority will always be returned. */
    
    static const char *authority_to_str(int authority, struct ast_str **res)
    
    Joshua Colp's avatar
    Joshua Colp committed
    	int i;
    
    	char *sep = "";
    
    	if (authority != EVENT_FLAG_SHUTDOWN) {
    		for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
    			if (authority & perms[i].num) {
    				ast_str_append(res, 0, "%s%s", sep, perms[i].label);
    				sep = ",";
    			}
    
    	if (ast_str_strlen(*res) == 0) {
    		/* replace empty string with something sensible */
    
    		ast_str_append(res, 0, "<none>");
    
    /*! Tells you if smallstr exists inside bigstr
       which is delim by delim and uses no buf or stringsep
       ast_instring("this|that|more","this",'|') == 1;
    
       feel free to move this to app.c -anthm */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
    
    	const char *val = bigstr, *next;
    
    	do {
    		if ((next = strchr(val, delim))) {
    
    			if (!strncmp(val, smallstr, (next - val))) {
    
    			return !strcmp(smallstr, val);
    
    	} while (*(val = (next + 1)));
    
    	return 0;
    
    static int get_perm(const char *instr)
    
    	for (x = 0; x < ARRAY_LEN(perms); x++) {
    
    		if (ast_instring(instr, perms[x].label, ',')) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*!
    
     * A number returns itself, false returns 0, true returns all flags,
     * other strings return the flags that are set.
    
    static int strings_to_mask(const char *string)
    
    	for (p = string; *p; p++) {
    		if (*p < '0' || *p > '9') {
    
    	if (!*p) { /* all digits */
    
    	if (ast_true(string)) {	/* all permissions */
    		int x, ret = 0;
    
    		for (x = 0; x < ARRAY_LEN(perms); x++) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			ret |= perms[x].num;
    
    /*! \brief Unreference manager session object.
         If no more references, then go ahead and delete it */
    static struct mansession_session *unref_mansession(struct mansession_session *s)
    
    	if (manager_debug) {
    
    		ast_debug(1, "Mansession: %p refcount now %d\n", s, refcount - 1);
    
    static void event_filter_destructor(void *obj)
    {
    	regex_t *regex_filter = obj;
    	regfree(regex_filter);
    }
    
    
    static void session_destructor(void *obj)
    {
    	struct mansession_session *session = obj;
    	struct eventqent *eqe = session->last_ev;
    
    	struct ast_datastore *datastore;
    
    	/* Get rid of each of the data stores on the session */
    	while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
    		/* Free the data store */
    		ast_datastore_free(datastore);
    	}
    
    	if (eqe) {
    		ast_atomic_fetchadd_int(&eqe->usecount, -1);
    	}
    
    	if (session->chanvars) {
    		ast_variables_destroy(session->chanvars);
    	}
    
    	if (session->whitefilters) {
    
    		ao2_t_ref(session->whitefilters, -1, "decrement ref for white container, should be last one");
    
    	}
    
    	if (session->blackfilters) {
    
    		ao2_t_ref(session->blackfilters, -1, "decrement ref for black container, should be last one");
    
    }
    
    /*! \brief Allocate manager session structure and add it to the list of sessions */
    
    static struct mansession_session *build_mansession(const struct ast_sockaddr *addr)
    
    	struct ao2_container *sessions;
    
    	struct mansession_session *newsession;
    
    
    	newsession = ao2_alloc(sizeof(*newsession), session_destructor);
    	if (!newsession) {
    
    	newsession->whitefilters = ao2_container_alloc(1, NULL, NULL);
    	newsession->blackfilters = ao2_container_alloc(1, NULL, NULL);
    	if (!newsession->whitefilters || !newsession->blackfilters) {
    
    		ao2_ref(newsession, -1);
    		return NULL;
    	}
    
    
    	newsession->fd = -1;
    	newsession->waiting_thread = AST_PTHREADT_NULL;
    	newsession->writetimeout = 100;
    	newsession->send_events = -1;
    
    	ast_sockaddr_copy(&newsession->addr, addr);
    
    	sessions = ao2_global_obj_ref(mgr_sessions);
    	if (sessions) {
    		ao2_link(sessions, newsession);
    		ao2_ref(sessions, -1);
    	}
    
    }
    
    static int mansession_cmp_fn(void *obj, void *arg, int flags)
    {
    	struct mansession_session *s = obj;
    	char *str = arg;
    	return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
    }
    
    static void session_destroy(struct mansession_session *s)
    {
    
    	struct ao2_container *sessions;
    
    	sessions = ao2_global_obj_ref(mgr_sessions);
    	if (sessions) {
    		ao2_unlink(sessions, s);
    		ao2_ref(sessions, -1);
    	}
    
    static int check_manager_session_inuse(const char *name)
    {
    
    	struct ao2_container *sessions;
    	struct mansession_session *session;
    
    	sessions = ao2_global_obj_ref(mgr_sessions);
    	if (sessions) {
    		session = ao2_find(sessions, (char *) name, 0);
    		ao2_ref(sessions, -1);
    		if (session) {
    			unref_mansession(session);
    			inuse = 1;
    		}
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*!
    
     * lookup an entry in the list of registered users.
     * must be called with the list lock held.
     */
    static struct ast_manager_user *get_manager_by_name_locked(const char *name)
    
    {
    	struct ast_manager_user *user = NULL;
    
    
    	AST_RWLIST_TRAVERSE(&users, user, list) {
    
    		if (!strcasecmp(user->username, name)) {
    
    /*! \brief Get displayconnects config option.
    
     *  \param session manager session to get parameter from.
    
     *  \return displayconnects config option value.
     */
    
    static int manager_displayconnects(struct mansession_session *session)
    
    {
    	struct ast_manager_user *user = NULL;
    	int ret = 0;
    
    
    	AST_RWLIST_RDLOCK(&users);
    
    	if ((user = get_manager_by_name_locked(session->username))) {
    
    		ret = user->displayconnects;
    
    	AST_RWLIST_UNLOCK(&users);
    
    #ifdef AST_XML_DOCS
    
    static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	struct manager_action *cur;
    
    Jason Parker's avatar
    Jason Parker committed
    	struct ast_str *authority;
    	int num, l, which;
    
    Jason Parker's avatar
    Jason Parker committed
    	char *ret = NULL;
    
    	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];
    
    Jason Parker's avatar
    Jason Parker committed
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "manager show command";
    
    			"Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
    
    Jason Parker's avatar
    Jason Parker committed
    			"	Shows the detailed description for a specific Asterisk manager interface command.\n";
    		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 */
    
    Jason Parker's avatar
    Jason Parker committed
    			}
    		}
    
    		AST_RWLIST_UNLOCK(&actions);
    		return ret;
    
    Jason Parker's avatar
    Jason Parker committed
    	}
    
    	authority = ast_str_alloca(MAX_AUTH_PERM_STRING);
    
    Jason Parker's avatar
    Jason Parker committed
    		return CLI_SHOWUSAGE;
    
    #ifdef AST_XML_DOCS
    	/* setup the titles */
    	term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
    	term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40);
    	term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
    	term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40);
    	term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
    
    	term_color(privilege_title, "[Privilege]\n", COLOR_MAGENTA, 0, 40);
    
    	term_color(final_response_title, "[Final Response]\n", COLOR_MAGENTA, 0, 40);
    	term_color(list_responses_title, "[List Responses]\n", COLOR_MAGENTA, 0, 40);
    
    	AST_RWLIST_RDLOCK(&actions);
    	AST_RWLIST_TRAVERSE(&actions, cur, list) {
    
    Jason Parker's avatar
    Jason Parker committed
    		for (num = 3; num < a->argc; num++) {
    			if (!strcasecmp(cur->action, a->argv[num])) {
    
    				auth_str = authority_to_str(cur->authority, &authority);
    
    #ifdef AST_XML_DOCS
    				if (cur->docsrc == AST_XML_DOC) {
    
    Kevin Harwell's avatar
    Kevin Harwell committed
    					char *syntax = ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1);
    					char *synopsis = ast_xmldoc_printable(S_OR(cur->synopsis, "Not available"), 1);
    					char *description = ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1);
    					char *arguments = ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1);
    					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);
    					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",
    
    Kevin Harwell's avatar
    Kevin Harwell committed
    						syntax_title, syntax,
    						synopsis_title, synopsis,
    						description_title, description,
    						arguments_title, arguments,
    						seealso_title, seealso,
    
    						privilege_title, privilege,
    						list_responses_title);
    
    					if (!cur->list_responses) {
    						ast_cli(a->fd, "%s\n\n", responses);
    					} else {
    						struct ast_xml_doc_item *temp;
    						for (temp = cur->list_responses; temp; temp = AST_LIST_NEXT(temp, next)) {
    							ast_cli(a->fd, "Event: %s\n", temp->name);
    							print_event_instance(a, temp);
    						}
    					}
    
    					ast_cli(a->fd, "%s", final_response_title);
    
    					if (!cur->final_response) {
    						ast_cli(a->fd, "%s\n\n", responses);
    					} else {
    						ast_cli(a->fd, "Event: %s\n", cur->final_response->name);
    						print_event_instance(a, cur->final_response);
    					}
    
    					ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
    
    						cur->action, cur->synopsis,
    
    						S_OR(cur->description, ""));
    
    	AST_RWLIST_UNLOCK(&actions);
    
    Jason Parker's avatar
    Jason Parker committed
    	return CLI_SUCCESS;
    
    Jason Parker's avatar
    Jason Parker committed
    static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Jason Parker's avatar
    Jason Parker committed
    	switch (cmd) {
    	case CLI_INIT:
    
    		e->command = "manager set debug [on|off]";
    		e->usage = "Usage: manager set debug [on|off]\n	Show, enable, disable debugging of the manager code.\n";
    
    Jason Parker's avatar
    Jason Parker committed
    		return NULL;
    	case CLI_GENERATE:
    
    Jason Parker's avatar
    Jason Parker committed
    	}
    
    Jason Parker's avatar
    Jason Parker committed
    		ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
    
    	} else if (a->argc == 4) {
    		if (!strcasecmp(a->argv[3], "on")) {
    
    		} else if (!strcasecmp(a->argv[3], "off")) {
    
    Jason Parker's avatar
    Jason Parker committed
    			return CLI_SHOWUSAGE;
    
    Jason Parker's avatar
    Jason Parker committed
    	return CLI_SUCCESS;
    
    Jason Parker's avatar
    Jason Parker committed
    static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    {
    	struct ast_manager_user *user = NULL;
    
    Jason Parker's avatar
    Jason Parker committed
    	int l, which;
    	char *ret = NULL;
    
    	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;
    
    Jason Parker's avatar
    Jason Parker committed
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "manager show user";
    
    Jason Parker's avatar
    Jason Parker committed
    			" Usage: manager show user <user>\n"
    			"        Display all information related to the manager user specified.\n";
    		return NULL;
    	case CLI_GENERATE:
    		l = strlen(a->word);
    		which = 0;
    
    Jason Parker's avatar
    Jason Parker committed
    			return NULL;
    
    Jason Parker's avatar
    Jason Parker committed
    		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;
    			}
    		}
    		AST_RWLIST_UNLOCK(&users);
    		return ret;
    	}
    
    Jason Parker's avatar
    Jason Parker committed
    		return CLI_SHOWUSAGE;
    
    	AST_RWLIST_RDLOCK(&users);
    
    Jason Parker's avatar
    Jason Parker committed
    	if (!(user = get_manager_by_name_locked(a->argv[3]))) {
    		ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
    
    		AST_RWLIST_UNLOCK(&users);
    
    Jason Parker's avatar
    Jason Parker committed
    		return CLI_SUCCESS;
    
    Jason Parker's avatar
    Jason Parker committed
    	ast_cli(a->fd,
    
    		"          username: %s\n"
    		"            secret: %s\n"
    		"               ACL: %s\n"
    		"         read perm: %s\n"
    		"        write perm: %s\n"
    		"   displayconnects: %s\n"
    		"allowmultiplelogin: %s\n",
    
    		S_OR(user->username, "(N/A)"),
    
    		(user->secret ? "<Set>" : "(N/A)"),
    
    		((user->acl && !ast_acl_list_is_empty(user->acl)) ? "yes" : "no"),
    
    		user_authority_to_str(user->readperm, &rauthority),
    		user_authority_to_str(user->writeperm, &wauthority),
    
    		(user->displayconnects ? "yes" : "no"),
    		(user->allowmultiplelogin ? "yes" : "no"));
    	ast_cli(a->fd, "         Variables: \n");
    
    		for (v = user->chanvars ; v ; v = v->next) {
    			ast_cli(a->fd, "                 %s = %s\n", v->name, v->value);
    		}
    
    	AST_RWLIST_UNLOCK(&users);
    
    Jason Parker's avatar
    Jason Parker committed
    	return CLI_SUCCESS;
    
    Jason Parker's avatar
    Jason Parker committed
    static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    {
    	struct ast_manager_user *user = NULL;
    	int count_amu = 0;
    
    Jason Parker's avatar
    Jason Parker committed
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "manager show users";
    
    Jason Parker's avatar
    Jason Parker committed
    			"Usage: manager show users\n"
    			"       Prints a listing of all managers that are currently configured on that\n"
    			" system.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    Jason Parker's avatar
    Jason Parker committed
    		return CLI_SHOWUSAGE;
    
    	AST_RWLIST_RDLOCK(&users);
    
    
    	/* If there are no users, print out something along those lines */
    
    	if (AST_RWLIST_EMPTY(&users)) {
    
    Jason Parker's avatar
    Jason Parker committed
    		ast_cli(a->fd, "There are no manager users.\n");
    
    		AST_RWLIST_UNLOCK(&users);
    
    Jason Parker's avatar
    Jason Parker committed
    		return CLI_SUCCESS;
    
    Jason Parker's avatar
    Jason Parker committed
    	ast_cli(a->fd, "\nusername\n--------\n");
    
    	AST_RWLIST_TRAVERSE(&users, user, list) {
    
    Jason Parker's avatar
    Jason Parker committed
    		ast_cli(a->fd, "%s\n", user->username);
    
    	AST_RWLIST_UNLOCK(&users);
    
    	ast_cli(a->fd,"-------------------\n"
    		      "%d manager users configured.\n", count_amu);
    
    Jason Parker's avatar
    Jason Parker committed
    	return CLI_SUCCESS;
    
    /*! \brief  CLI command  manager list commands */
    
    Jason Parker's avatar
    Jason Parker committed
    static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct manager_action *cur;
    
    	int name_len = 1;
    	int space_remaining;
    #define HSMC_FORMAT "  %-*.*s  %-.*s\n"
    
    Jason Parker's avatar
    Jason Parker committed
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "manager show commands";
    
    Jason Parker's avatar
    Jason Parker committed
    			"Usage: manager show commands\n"
    			"	Prints a listing of all the available Asterisk manager interface commands.\n";
    		return NULL;
    	case CLI_GENERATE:
    
    	AST_RWLIST_RDLOCK(&actions);
    
    	AST_RWLIST_TRAVERSE(&actions, cur, list) {
    
    		int incoming_len = strlen(cur->action);
    		if (incoming_len > name_len) {
    			name_len = incoming_len;
    		}
    	}
    
    	space_remaining = MGR_SHOW_TERMINAL_WIDTH - name_len - 4;
    	if (space_remaining < 0) {
    		space_remaining = 0;
    	}
    
    	ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "Action", space_remaining, "Synopsis");
    	ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "------", space_remaining, "--------");
    
    	AST_RWLIST_TRAVERSE(&actions, cur, list) {
    		ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, cur->action, space_remaining, cur->synopsis);
    
    	AST_RWLIST_UNLOCK(&actions);
    
    Jason Parker's avatar
    Jason Parker committed
    	return CLI_SUCCESS;
    
    /*! \brief CLI command manager list connected */
    
    Jason Parker's avatar
    Jason Parker committed
    static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	struct ao2_container *sessions;
    
    	struct mansession_session *session;
    
    #define HSMCONN_FORMAT1 "  %-15.15s  %-55.55s  %-10.10s  %-10.10s  %-8.8s  %-8.8s  %-5.5s  %-5.5s\n"
    #define HSMCONN_FORMAT2 "  %-15.15s  %-55.55s  %-10d  %-10d  %-8d  %-8d  %-5.5d  %-5.5d\n"
    
    Joshua Colp's avatar
    Joshua Colp committed
    	int count = 0;
    
    Jason Parker's avatar
    Jason Parker committed
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "manager show connected";
    
    Jason Parker's avatar
    Jason Parker committed
    			"Usage: manager show connected\n"
    			"	Prints a listing of the users that are currently connected to the\n"
    			"Asterisk manager interface.\n";
    		return NULL;
    	case CLI_GENERATE:
    
    Jason Parker's avatar
    Jason Parker committed
    	}
    
    	ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
    
    	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);
    			ast_cli(a->fd, HSMCONN_FORMAT2, session->username,
    				ast_sockaddr_stringify_addr(&session->addr),
    				(int) (session->sessionstart),
    				(int) (now - session->sessionstart),
    				session->fd,
    				session->inuse,
    				session->readperm,
    				session->writeperm);
    			count++;
    			ao2_unlock(session);
    			unref_mansession(session);
    		}
    		ao2_iterator_destroy(&i);
    
    Joshua Colp's avatar
    Joshua Colp committed
    	}
    
    Jason Parker's avatar
    Jason Parker committed
    	ast_cli(a->fd, "%d users connected.\n", count);
    
    Jason Parker's avatar
    Jason Parker committed
    	return CLI_SUCCESS;
    
    /*! \brief CLI command manager list eventq */
    
    /* Should change to "manager show connected" */
    
    Jason Parker's avatar
    Jason Parker committed
    static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    {
    	struct eventqent *s;
    
    Jason Parker's avatar
    Jason Parker committed
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "manager show eventq";
    
    Jason Parker's avatar
    Jason Parker committed
    			"Usage: manager show eventq\n"
    			"	Prints a listing of all events pending in the Asterisk manger\n"
    			"event queue.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	AST_RWLIST_RDLOCK(&all_events);
    	AST_RWLIST_TRAVERSE(&all_events, s, eq_next) {
    
    		ast_cli(a->fd, "Usecount: %d\n", s->usecount);
    
    Jason Parker's avatar
    Jason Parker committed
    		ast_cli(a->fd, "Category: %d\n", s->category);
    		ast_cli(a->fd, "Event:\n%s", s->eventdata);
    
    Jason Parker's avatar
    Jason Parker committed
    	return CLI_SUCCESS;
    
    /*! \brief CLI command manager reload */
    static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "manager reload";
    		e->usage =
    			"Usage: manager reload\n"
    			"       Reloads the manager configuration.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    static struct eventqent *advance_event(struct eventqent *e)
    
    	AST_RWLIST_RDLOCK(&all_events);
    	if ((next = AST_RWLIST_NEXT(e, eq_next))) {
    		ast_atomic_fetchadd_int(&next->usecount, 1);
    		ast_atomic_fetchadd_int(&e->usecount, -1);
    	}
    	AST_RWLIST_UNLOCK(&all_events);
    	return next;
    
    #define	GET_HEADER_FIRST_MATCH	0
    #define	GET_HEADER_LAST_MATCH	1
    #define	GET_HEADER_SKIP_EMPTY	2
    
    
    /*!
     * \brief Return a matching header value.
     *
     * \details
     * Generic function to return either the first or the last
     * matching header from a list of variables, possibly skipping
     * empty strings.
     *
     * \note At the moment there is only one use of this function in
     * this file, so we make it static.
     *
     * \note Never returns NULL.
    
     */
    static const char *__astman_get_header(const struct message *m, char *var, int mode)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int x, l = strlen(var);
    
    		const char *h = m->headers[x];
    
    		if (!strncasecmp(var, h, l) && h[l] == ':') {
    			const char *value = h + l + 1;
    			value = ast_skip_blanks(value); /* ignore leading spaces in the value */
    
    			/* found a potential candidate */
    
    			if ((mode & GET_HEADER_SKIP_EMPTY) && ast_strlen_zero(value)) {
    
    				continue;	/* not interesting */
    
    			}
    			if (mode & GET_HEADER_LAST_MATCH) {
    
    				result = value;	/* record the last match so far */
    
    /*!
     * \brief Return the first matching variable from an array.
     *
     * \note This is the legacy function and is implemented in
     * therms of __astman_get_header().
     *
     * \note Never returns NULL.
    
     */
    const char *astman_get_header(const struct message *m, char *var)
    {
    	return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
    }
    
    
    /*!
     * \internal
     * \brief Process one "Variable:" header value string.
     *
     * \param head Current list of AMI variables to get new values added.
     * \param hdr_val Header value string to process.
     *
     * \return New variable list head.
     */
    static struct ast_variable *man_do_variable_value(struct ast_variable *head, const char *hdr_val)
    
    	AST_DECLARE_APP_ARGS(args,
    
    		AST_APP_ARG(vars)[64];
    
    	hdr_val = ast_skip_blanks(hdr_val); /* ignore leading spaces in the value */
    	parse = ast_strdupa(hdr_val);
    
    	/* Break the header value string into name=val pair items. */
    	AST_STANDARD_APP_ARGS(args, parse);
    	if (args.argc) {
    		int y;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    
    		/* Process each name=val pair item. */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		for (y = 0; y < args.argc; y++) {
    
    			struct ast_variable *cur;
    			char *var;
    			char *val;
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				continue;
    
    			var = val = args.vars[y];
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			strsep(&val, "=");
    
    
    			/* XXX We may wish to trim whitespace from the strings. */
    
    			if (!val || ast_strlen_zero(var)) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				continue;
    
    
    			/* Create new variable list node and prepend it to the list. */
    
    			if (cur) {
    				cur->next = head;
    				head = cur;
    			}
    		}
    	}
    
    	return head;
    }
    
    struct ast_variable *astman_get_variables(const struct message *m)
    
    {
    	return astman_get_variables_order(m, ORDER_REVERSE);
    }
    
    struct ast_variable *astman_get_variables_order(const struct message *m,
    	enum variable_orders order)
    
    {
    	int varlen;
    	int x;
    	struct ast_variable *head = NULL;
    
    	static const char var_hdr[] = "Variable:";
    
    	/* Process all "Variable:" headers. */
    	varlen = strlen(var_hdr);
    	for (x = 0; x < m->hdrcount; x++) {
    		if (strncasecmp(var_hdr, m->headers[x], varlen)) {
    			continue;
    
    		head = man_do_variable_value(head, m->headers[x] + varlen);
    
    	if (order == ORDER_NATURAL) {
    		head = ast_variables_reverse(head);
    	}
    
    
    /*! \brief access for hooks to send action messages to ami */
    
    David Brooks's avatar
    David Brooks committed
    int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
    {
    	const char *action;
    	int ret = 0;
    
    	struct manager_action *act_found;
    
    David Brooks's avatar
    David Brooks committed
    	struct mansession s = {.session = NULL, };
    	struct message m = { 0 };
    
    	char *dup_str;
    	char *src;
    
    David Brooks's avatar
    David Brooks committed
    	int x = 0;
    	int curlen;
    
    	if (hook == NULL) {
    		return -1;
    	}
    
    
    	/* Create our own copy of the AMI action msg string. */
    	src = dup_str = ast_strdup(msg);
    	if (!dup_str) {
    		return -1;
    	}
    
    
    David Brooks's avatar
    David Brooks committed
    	/* convert msg string to message struct */
    
    	curlen = strlen(src);
    
    David Brooks's avatar
    David Brooks committed
    	for (x = 0; x < curlen; x++) {
    		int cr;	/* set if we have \r */
    		if (src[x] == '\r' && x+1 < curlen && src[x+1] == '\n')
    			cr = 2;	/* Found. Update length to include \r\n */
    		else if (src[x] == '\n')
    			cr = 1;	/* also accept \n only */
    		else
    			continue;
    
    		/* don't keep empty lines */
    		if (x && m.hdrcount < ARRAY_LEN(m.headers)) {
    			/* ... but trim \r\n and terminate the header string */
    			src[x] = '\0';
    			m.headers[m.hdrcount++] = src;
    
    David Brooks's avatar
    David Brooks committed
    		}
    		x += cr;
    		curlen -= x;		/* remaining size */
    		src += x;		/* update pointer */
    		x = -1;			/* reset loop */
    	}
    
    
    	action = astman_get_header(&m, "Action");
    	if (strcasecmp(action, "login")) {
    		act_found = action_find(action);
    		if (act_found) {
    
    David Brooks's avatar
    David Brooks committed
    			/*
    
    			 * 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
    			 */
    
    David Brooks's avatar
    David Brooks committed
    			s.hook = hook;
    			s.f = (void*)1; /* set this to something so our request will make it through all functions that test it*/
    
    
    			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);
    
    				ret = act_found->func(&s, &m);
    
    				ao2_lock(act_found);
    				if (act_found->module) {
    					ast_module_unref(act_found->module);
    				}
    
    			} else {
    				ret = -1;
    			}
    			ao2_unlock(act_found);
    			ao2_t_ref(act_found, -1, "done with found action object");
    
    /*!
     * helper function to send a string to the socket.
     * Return -1 on error (e.g. buffer full).
     */
    static int send_string(struct mansession *s, char *string)
    {
    
    	FILE *f = s->f ? s->f : s->session->f;
    	int fd = s->f ? s->fd : s->session->fd;
    
    
    David Brooks's avatar
    David Brooks committed
    	/* It's a result from one of the hook's action invocation */
    	if (s->hook) {
    		/*
    		 * to send responses, we're using the same function
    		 * as for receiving events. We call the event "HookResponse"
    		 */
    		s->hook->helper(EVENT_FLAG_HOOKRESPONSE, "HookResponse", string);
    		return 0;
    
    	if ((res = ast_careful_fwrite(f, fd, string, strlen(string), s->session->writetimeout))) {
    
    		s->write_error = 1;
    
    /*!
     * \brief thread local buffer for astman_append
     *
     * \note This can not be defined within the astman_append() function
     *       because it declares a couple of functions that get used to
     *       initialize the thread local storage key.
     */
    
    AST_THREADSTORAGE(astman_append_buf);
    
    AST_THREADSTORAGE(userevent_buf);
    
    
    /*! \brief initial allocated size for the astman_append_buf and astman_send_*_va */
    
    #define ASTMAN_APPEND_BUF_INITSIZE   256
    
    /*!
    
     * utility functions for creating AMI replies
     */
    void astman_append(struct mansession *s, const char *fmt, ...)
    {
    
    	struct ast_str *buf;
    
    	if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
    
    	res = ast_str_set_va(&buf, 0, fmt, ap);
    
    	if (res == AST_DYNSTR_BUILD_FAILED) {
    		return;
    	}
    
    	if (s->f != NULL || s->session->f != NULL) {
    
    		send_string(s, ast_str_buffer(buf));
    	} else {
    
    		ast_verbose("fd == -1 in astman_append, should not happen\n");