2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief The Asterisk Management Interface - AMI
23 * \author Mark Spencer <markster@digium.com>
25 * \extref OpenSSL http://www.openssl.org - for AMI/SSL
27 * At the moment this file contains a number of functions, namely:
29 * - data structures storing AMI state
30 * - AMI-related API functions, used by internal asterisk components
31 * - handlers for AMI-related CLI functions
32 * - handlers for AMI functions (available through the AMI socket)
33 * - the code for the main AMI listener thread and individual session threads
34 * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
39 /*! \addtogroup Group_AMI AMI functions
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48 #include "asterisk/_private.h"
49 #include "asterisk/paths.h" /* use various ast_config_AST_* */
55 #include "asterisk/channel.h"
56 #include "asterisk/file.h"
57 #include "asterisk/manager.h"
58 #include "asterisk/module.h"
59 #include "asterisk/config.h"
60 #include "asterisk/callerid.h"
61 #include "asterisk/lock.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/app.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/md5.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/http.h"
69 #include "asterisk/version.h"
70 #include "asterisk/threadstorage.h"
71 #include "asterisk/linkedlists.h"
72 #include "asterisk/version.h"
73 #include "asterisk/term.h"
74 #include "asterisk/astobj2.h"
77 * Linked list of events.
78 * Global events are appended to the list by append_event().
79 * The usecount is the number of stored pointers to the element,
80 * excluding the list pointers. So an element that is only in
81 * the list has a usecount of 0, not 1.
83 * Clients have a pointer to the last event processed, and for each
84 * of these clients we track the usecount of the elements.
85 * If we have a pointer to an entry in the list, it is safe to navigate
86 * it forward because elements will not be deleted, but only appended.
87 * The worst that can happen is seeing the pointer still NULL.
89 * When the usecount of an element drops to 0, and the element is the
90 * first in the list, we can remove it. Removal is done within the
91 * main thread, which is woken up for the purpose.
93 * For simplicity of implementation, we make sure the list is never empty.
96 int usecount; /*!< # of clients who still need the event */
98 unsigned int seq; /*!< sequence number */
99 AST_LIST_ENTRY(eventqent) eq_next;
100 char eventdata[1]; /*!< really variable size, allocated by append_event() */
103 static AST_LIST_HEAD_STATIC(all_events, eventqent);
105 static int displayconnects = 1;
106 static int allowmultiplelogin = 1;
107 static int timestampevents;
108 static int httptimeout = 60;
109 static int manager_enabled = 0;
110 static int webmanager_enabled = 0;
112 static int block_sockets;
113 static int num_sessions;
115 static int manager_debug; /*!< enable some debugging code in the manager */
118 * Descriptor for a manager session, either on the AMI socket or over HTTP.
121 * AMI session have managerid == 0; the entry is created upon a connect,
122 * and destroyed with the socket.
123 * HTTP sessions have managerid != 0, the value is used as a search key
124 * to lookup sessions (using the mansession_id cookie).
126 static const char *command_blacklist[] = {
132 pthread_t ms_t; /*!< Execution thread, basically useless */
133 ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
134 /* XXX need to document which fields it is protecting */
135 struct sockaddr_in sin; /*!< address we are connecting from */
136 FILE *f; /*!< fdopen() on the underlying fd */
137 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
138 int inuse; /*!< number of HTTP sessions using this entry */
139 int needdestroy; /*!< Whether an HTTP session should be destroyed */
140 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
141 unsigned long managerid; /*!< Unique manager identifier, 0 for AMI sessions */
142 time_t sessionstart; /*!< Session start time */
143 time_t sessiontimeout; /*!< Session timeout if HTTP */
144 char username[80]; /*!< Logged in username */
145 char challenge[10]; /*!< Authentication challenge */
146 int authenticated; /*!< Authentication status */
147 int readperm; /*!< Authorization for reading */
148 int writeperm; /*!< Authorization for writing */
149 char inbuf[1025]; /*!< Buffer */
150 /* we use the extra byte to add a '\0' and simplify parsing */
151 int inlen; /*!< number of buffered bytes */
152 int send_events; /*!< XXX what ? */
153 struct eventqent *last_ev; /*!< last event processed. */
154 int writetimeout; /*!< Timeout for ast_carefulwrite() */
155 AST_LIST_ENTRY(mansession) list;
158 #define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
160 static AST_LIST_HEAD_STATIC(sessions, mansession);
162 /*! \brief user descriptor, as read from the config file.
164 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
165 * lines which are not supported here, and readperm/writeperm/writetimeout
168 struct ast_manager_user {
171 struct ast_ha *ha; /*!< ACL setting */
172 int readperm; /*! Authorization for reading */
173 int writeperm; /*! Authorization for writing */
174 int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
175 int displayconnects; /*!< XXX unused */
176 int keep; /*!< mark entries created on a reload */
177 AST_RWLIST_ENTRY(ast_manager_user) list;
180 /*! \brief list of users found in the config file */
181 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
183 /*! \brief list of actions registered */
184 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
186 /*! \brief list of hooks registered */
187 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
189 /*! \brief Add a custom hook to be called when an event is fired */
190 void ast_manager_register_hook(struct manager_custom_hook *hook)
192 AST_RWLIST_WRLOCK(&manager_hooks);
193 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
194 AST_RWLIST_UNLOCK(&manager_hooks);
198 /*! \brief Delete a custom hook to be called when an event is fired */
199 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
201 AST_RWLIST_WRLOCK(&manager_hooks);
202 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
203 AST_RWLIST_UNLOCK(&manager_hooks);
208 * Event list management functions.
209 * We assume that the event list always has at least one element,
210 * and the delete code will not remove the last entry even if the
214 static time_t __deb(time_t start, const char *msg)
216 time_t now = time(NULL);
217 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
218 if (start != 0 && now - start > 5)
219 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
223 static void LOCK_EVENTS(void)
225 time_t start = __deb(0, "about to lock events");
226 AST_LIST_LOCK(&all_events);
227 __deb(start, "done lock events");
230 static void UNLOCK_EVENTS(void)
232 __deb(0, "about to unlock events");
233 AST_LIST_UNLOCK(&all_events);
236 static void LOCK_SESS(void)
238 time_t start = __deb(0, "about to lock sessions");
239 AST_LIST_LOCK(&sessions);
240 __deb(start, "done lock sessions");
243 static void UNLOCK_SESS(void)
245 __deb(0, "about to unlock sessions");
246 AST_LIST_UNLOCK(&sessions);
250 int check_manager_enabled()
252 return manager_enabled;
255 int check_webmanager_enabled()
257 return (webmanager_enabled && manager_enabled);
261 * Grab a reference to the last event, update usecount as needed.
262 * Can handle a NULL pointer.
264 static struct eventqent *grab_last(void)
266 struct eventqent *ret;
268 AST_LIST_LOCK(&all_events);
269 ret = AST_LIST_LAST(&all_events);
270 /* the list is never empty now, but may become so when
271 * we optimize it in the future, so be prepared.
274 ast_atomic_fetchadd_int(&ret->usecount, 1);
275 AST_LIST_UNLOCK(&all_events);
280 * Purge unused events. Remove elements from the head
281 * as long as their usecount is 0 and there is a next element.
283 static void purge_events(void)
285 struct eventqent *ev;
287 AST_LIST_LOCK(&all_events);
288 while ( (ev = AST_LIST_FIRST(&all_events)) &&
289 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
290 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
293 AST_LIST_UNLOCK(&all_events);
297 * helper functions to convert back and forth between
298 * string and numeric representation of set of flags
300 static struct permalias {
304 { EVENT_FLAG_SYSTEM, "system" },
305 { EVENT_FLAG_CALL, "call" },
306 { EVENT_FLAG_LOG, "log" },
307 { EVENT_FLAG_VERBOSE, "verbose" },
308 { EVENT_FLAG_COMMAND, "command" },
309 { EVENT_FLAG_AGENT, "agent" },
310 { EVENT_FLAG_USER, "user" },
311 { EVENT_FLAG_CONFIG, "config" },
312 { EVENT_FLAG_DTMF, "dtmf" },
313 { EVENT_FLAG_REPORTING, "reporting" },
314 { EVENT_FLAG_CDR, "cdr" },
315 { EVENT_FLAG_DIALPLAN, "dialplan" },
320 /*! \brief Convert authority code to a list of options */
321 static char *authority_to_str(int authority, struct ast_str **res)
327 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
328 if (authority & perms[i].num) {
329 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
334 if ((*res)->used == 0) /* replace empty string with something sensible */
335 ast_str_append(res, 0, "<none>");
340 /*! Tells you if smallstr exists inside bigstr
341 which is delim by delim and uses no buf or stringsep
342 ast_instring("this|that|more","this",'|') == 1;
344 feel free to move this to app.c -anthm */
345 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
347 const char *val = bigstr, *next;
350 if ((next = strchr(val, delim))) {
351 if (!strncmp(val, smallstr, (next - val)))
356 return !strcmp(smallstr, val);
357 } while (*(val = (next + 1)));
362 static int get_perm(const char *instr)
369 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
370 if (ast_instring(instr, perms[x].label, ','))
378 * A number returns itself, false returns 0, true returns all flags,
379 * other strings return the flags that are set.
381 static int strings_to_mask(const char *string)
385 if (ast_strlen_zero(string))
388 for (p = string; *p; p++)
389 if (*p < '0' || *p > '9')
391 if (!p) /* all digits */
393 if (ast_false(string))
395 if (ast_true(string)) { /* all permissions */
397 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
401 return get_perm(string);
404 static int check_manager_session_inuse(const char *name)
406 struct mansession *session = NULL;
408 AST_LIST_LOCK(&sessions);
409 AST_LIST_TRAVERSE(&sessions, session, list) {
410 if (!strcasecmp(session->username, name))
413 AST_LIST_UNLOCK(&sessions);
415 return session ? 1 : 0;
420 * lookup an entry in the list of registered users.
421 * must be called with the list lock held.
423 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
425 struct ast_manager_user *user = NULL;
427 AST_RWLIST_TRAVERSE(&users, user, list)
428 if (!strcasecmp(user->username, name))
433 /*! \brief Get displayconnects config option.
434 * \param s manager session to get parameter from.
435 * \return displayconnects config option value.
437 static int manager_displayconnects (struct mansession *s)
439 struct ast_manager_user *user = NULL;
442 AST_RWLIST_RDLOCK(&users);
443 if ((user = get_manager_by_name_locked (s->username)))
444 ret = user->displayconnects;
445 AST_RWLIST_UNLOCK(&users);
450 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
452 struct manager_action *cur;
453 struct ast_str *authority;
458 e->command = "manager show command";
460 "Usage: manager show command <actionname>\n"
461 " Shows the detailed description for a specific Asterisk manager interface command.\n";
466 AST_RWLIST_RDLOCK(&actions);
467 AST_RWLIST_TRAVERSE(&actions, cur, list) {
468 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
469 ret = ast_strdup(cur->action);
470 break; /* make sure we exit even if ast_strdup() returns NULL */
473 AST_RWLIST_UNLOCK(&actions);
476 authority = ast_str_alloca(80);
478 return CLI_SHOWUSAGE;
480 AST_RWLIST_RDLOCK(&actions);
481 AST_RWLIST_TRAVERSE(&actions, cur, list) {
482 for (num = 3; num < a->argc; num++) {
483 if (!strcasecmp(cur->action, a->argv[num])) {
484 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
485 cur->action, cur->synopsis,
486 authority_to_str(cur->authority, &authority),
487 S_OR(cur->description, ""));
491 AST_RWLIST_UNLOCK(&actions);
496 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
500 e->command = "manager debug [on|off]";
501 e->usage = "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
507 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
508 else if (a->argc == 3) {
509 if (!strcasecmp(a->argv[2], "on"))
511 else if (!strcasecmp(a->argv[2], "off"))
514 return CLI_SHOWUSAGE;
519 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
521 struct ast_manager_user *user = NULL;
524 struct ast_str *rauthority = ast_str_alloca(80);
525 struct ast_str *wauthority = ast_str_alloca(80);
529 e->command = "manager show user";
531 " Usage: manager show user <user>\n"
532 " Display all information related to the manager user specified.\n";
539 AST_RWLIST_RDLOCK(&users);
540 AST_RWLIST_TRAVERSE(&users, user, list) {
541 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
542 ret = ast_strdup(user->username);
546 AST_RWLIST_UNLOCK(&users);
551 return CLI_SHOWUSAGE;
553 AST_RWLIST_RDLOCK(&users);
555 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
556 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
557 AST_RWLIST_UNLOCK(&users);
568 "displayconnects: %s\n",
569 (user->username ? user->username : "(N/A)"),
570 (user->secret ? "<Set>" : "(N/A)"),
571 (user->ha ? "yes" : "no"),
572 authority_to_str(user->readperm, &rauthority),
573 authority_to_str(user->writeperm, &wauthority),
574 (user->displayconnects ? "yes" : "no"));
576 AST_RWLIST_UNLOCK(&users);
582 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
584 struct ast_manager_user *user = NULL;
588 e->command = "manager show users";
590 "Usage: manager show users\n"
591 " Prints a listing of all managers that are currently configured on that\n"
598 return CLI_SHOWUSAGE;
600 AST_RWLIST_RDLOCK(&users);
602 /* If there are no users, print out something along those lines */
603 if (AST_RWLIST_EMPTY(&users)) {
604 ast_cli(a->fd, "There are no manager users.\n");
605 AST_RWLIST_UNLOCK(&users);
609 ast_cli(a->fd, "\nusername\n--------\n");
611 AST_RWLIST_TRAVERSE(&users, user, list) {
612 ast_cli(a->fd, "%s\n", user->username);
616 AST_RWLIST_UNLOCK(&users);
618 ast_cli(a->fd,"-------------------\n");
619 ast_cli(a->fd,"%d manager users configured.\n", count_amu);
625 /*! \brief CLI command manager list commands */
626 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
628 struct manager_action *cur;
629 struct ast_str *authority;
630 static const char *format = " %-15.15s %-15.15s %-55.55s\n";
633 e->command = "manager show commands";
635 "Usage: manager show commands\n"
636 " Prints a listing of all the available Asterisk manager interface commands.\n";
641 authority = ast_str_alloca(80);
642 ast_cli(a->fd, format, "Action", "Privilege", "Synopsis");
643 ast_cli(a->fd, format, "------", "---------", "--------");
645 AST_RWLIST_RDLOCK(&actions);
646 AST_RWLIST_TRAVERSE(&actions, cur, list)
647 ast_cli(a->fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
648 AST_RWLIST_UNLOCK(&actions);
653 /*! \brief CLI command manager list connected */
654 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
656 struct mansession *s;
657 time_t now = time(NULL);
658 static const char *format = " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n";
659 static const char *format2 = " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n";
663 e->command = "manager show connected";
665 "Usage: manager show connected\n"
666 " Prints a listing of the users that are currently connected to the\n"
667 "Asterisk manager interface.\n";
673 ast_cli(a->fd, format, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
675 AST_LIST_LOCK(&sessions);
676 AST_LIST_TRAVERSE(&sessions, s, list) {
677 ast_cli(a->fd, format2, s->username, ast_inet_ntoa(s->sin.sin_addr), (int)(s->sessionstart), (int)(now - s->sessionstart), s->fd, s->inuse, s->readperm, s->writeperm);
680 AST_LIST_UNLOCK(&sessions);
682 ast_cli(a->fd, "%d users connected.\n", count);
687 /*! \brief CLI command manager list eventq */
688 /* Should change to "manager show connected" */
689 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
694 e->command = "manager show eventq";
696 "Usage: manager show eventq\n"
697 " Prints a listing of all events pending in the Asterisk manger\n"
703 AST_LIST_LOCK(&all_events);
704 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
705 ast_cli(a->fd, "Usecount: %d\n",s->usecount);
706 ast_cli(a->fd, "Category: %d\n", s->category);
707 ast_cli(a->fd, "Event:\n%s", s->eventdata);
709 AST_LIST_UNLOCK(&all_events);
714 /*! \brief CLI command manager reload */
715 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
719 e->command = "manager reload";
721 "Usage: manager reload\n"
722 " Reloads the manager configuration.\n";
728 return CLI_SHOWUSAGE;
734 static struct ast_cli_entry cli_manager[] = {
735 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
736 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
737 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
738 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
739 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
740 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
741 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
742 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
746 * Decrement the usecount for the event; if it goes to zero,
747 * (why check for e->next ?) wakeup the
748 * main thread, which is in charge of freeing the record.
749 * Returns the next record.
751 static struct eventqent *unref_event(struct eventqent *e)
753 ast_atomic_fetchadd_int(&e->usecount, -1);
754 return AST_LIST_NEXT(e, eq_next);
757 static void ref_event(struct eventqent *e)
759 ast_atomic_fetchadd_int(&e->usecount, 1);
763 * destroy a session, leaving the usecount
765 static void free_session(struct mansession *s)
767 struct eventqent *eqe = s->last_ev;
770 ast_mutex_destroy(&s->__lock);
775 static void destroy_session(struct mansession *s)
777 AST_LIST_LOCK(&sessions);
778 AST_LIST_REMOVE(&sessions, s, list);
779 ast_atomic_fetchadd_int(&num_sessions, -1);
781 AST_LIST_UNLOCK(&sessions);
784 const char *astman_get_header(const struct message *m, char *var)
786 int x, l = strlen(var);
788 for (x = 0; x < m->hdrcount; x++) {
789 const char *h = m->headers[x];
790 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
797 struct ast_variable *astman_get_variables(const struct message *m)
800 struct ast_variable *head = NULL, *cur;
802 AST_DECLARE_APP_ARGS(args,
803 AST_APP_ARG(vars)[32];
806 varlen = strlen("Variable: ");
808 for (x = 0; x < m->hdrcount; x++) {
809 char *parse, *var, *val;
811 if (strncasecmp("Variable: ", m->headers[x], varlen))
813 parse = ast_strdupa(m->headers[x] + varlen);
815 AST_STANDARD_APP_ARGS(args, parse);
818 for (y = 0; y < args.argc; y++) {
821 var = val = ast_strdupa(args.vars[y]);
823 if (!val || ast_strlen_zero(var))
825 cur = ast_variable_new(var, val, "");
835 * helper function to send a string to the socket.
836 * Return -1 on error (e.g. buffer full).
838 static int send_string(struct mansession *s, char *string)
840 int len = strlen(string); /* residual length */
842 struct timeval start = ast_tvnow();
848 n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
849 if (n == len /* ok */ || n < 0 /* error */)
851 len -= n; /* skip already written data */
855 n = -1; /* error marker */
856 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
857 if (elapsed > s->writetimeout)
859 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
863 return n < 0 ? -1 : 0;
867 * \brief thread local buffer for astman_append
869 * \note This can not be defined within the astman_append() function
870 * because it declares a couple of functions that get used to
871 * initialize the thread local storage key.
873 AST_THREADSTORAGE(astman_append_buf);
874 /*! \brief initial allocated size for the astman_append_buf */
875 #define ASTMAN_APPEND_BUF_INITSIZE 256
878 * utility functions for creating AMI replies
880 void astman_append(struct mansession *s, const char *fmt, ...)
885 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
889 ast_str_set_va(&buf, 0, fmt, ap);
893 send_string(s, buf->str);
895 ast_verbose("fd == -1 in astman_append, should not happen\n");
898 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
899 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
900 hold the session lock _or_ be running in an action callback (in which case s->busy will
901 be non-zero). In either of these cases, there is no need to lock-protect the session's
902 fd, since no other output will be sent (events will be queued), and no input will
903 be read until either the current action finishes or get_input() obtains the session
907 /*! \brief send a response with an optional message,
908 * and terminate it with an empty line.
909 * m is used only to grab the 'ActionID' field.
911 * Use the explicit constant MSG_MOREDATA to remove the empty line.
912 * XXX MSG_MOREDATA should go to a header file.
914 #define MSG_MOREDATA ((char *)astman_send_response)
915 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
917 const char *id = astman_get_header(m,"ActionID");
919 astman_append(s, "Response: %s\r\n", resp);
920 if (!ast_strlen_zero(id))
921 astman_append(s, "ActionID: %s\r\n", id);
923 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
924 if (msg == MSG_MOREDATA)
927 astman_append(s, "Message: %s\r\n\r\n", msg);
929 astman_append(s, "\r\n");
932 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
934 astman_send_response_full(s, m, resp, msg, NULL);
937 void astman_send_error(struct mansession *s, const struct message *m, char *error)
939 astman_send_response_full(s, m, "Error", error, NULL);
942 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
944 astman_send_response_full(s, m, "Success", msg, NULL);
947 static void astman_start_ack(struct mansession *s, const struct message *m)
949 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
952 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
954 astman_send_response_full(s, m, "Success", msg, listflag);
959 Rather than braindead on,off this now can also accept a specific int mask value
960 or a ',' delim list of mask strings (the same as manager.conf) -anthm
962 static int set_eventmask(struct mansession *s, const char *eventmask)
964 int maskint = strings_to_mask(eventmask);
966 ast_mutex_lock(&s->__lock);
968 s->send_events = maskint;
969 ast_mutex_unlock(&s->__lock);
975 * Here we start with action_ handlers for AMI actions,
976 * and the internal functions used by them.
977 * Generally, the handlers are called action_foo()
980 /* helper function for action_login() */
981 static int authenticate(struct mansession *s, const struct message *m)
983 const char *username = astman_get_header(m, "Username");
984 const char *password = astman_get_header(m, "Secret");
986 struct ast_manager_user *user = NULL;
988 if (ast_strlen_zero(username)) /* missing username */
991 /* locate user in locked state */
992 AST_RWLIST_WRLOCK(&users);
994 if (!(user = get_manager_by_name_locked(username))) {
995 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
996 } else if (user->ha && !ast_apply_ha(user->ha, &(s->sin))) {
997 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
998 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
999 const char *key = astman_get_header(m, "Key");
1000 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) &&
1001 !ast_strlen_zero(password)) {
1004 char md5key[256] = "";
1005 struct MD5Context md5;
1006 unsigned char digest[16];
1009 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1010 MD5Update(&md5, (unsigned char *) password, strlen(password));
1011 MD5Final(digest, &md5);
1012 for (x=0; x<16; x++)
1013 len += sprintf(md5key + len, "%2.2x", digest[x]);
1014 if (!strcmp(md5key, key))
1017 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
1018 S_OR(s->challenge, ""));
1020 } else if (password && user->secret && !strcmp(password, user->secret))
1024 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1025 AST_RWLIST_UNLOCK(&users);
1031 ast_copy_string(s->username, username, sizeof(s->username));
1032 s->readperm = user->readperm;
1033 s->writeperm = user->writeperm;
1034 s->writetimeout = user->writetimeout;
1035 s->sessionstart = time(NULL);
1036 set_eventmask(s, astman_get_header(m, "Events"));
1038 AST_RWLIST_UNLOCK(&users);
1042 /*! \brief Manager PING */
1043 static char mandescr_ping[] =
1044 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1045 " manager connection open.\n"
1046 "Variables: NONE\n";
1048 static int action_ping(struct mansession *s, const struct message *m)
1050 astman_send_response(s, m, "Success", "Ping: Pong\r\n");
1054 static char mandescr_getconfig[] =
1055 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1056 "file by category and contents.\n"
1058 " Filename: Configuration filename (e.g. foo.conf)\n";
1060 static int action_getconfig(struct mansession *s, const struct message *m)
1062 struct ast_config *cfg;
1063 const char *fn = astman_get_header(m, "Filename");
1066 char *category=NULL;
1067 struct ast_variable *v;
1068 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1070 if (ast_strlen_zero(fn)) {
1071 astman_send_error(s, m, "Filename not specified");
1074 if (!(cfg = ast_config_load(fn, config_flags))) {
1075 astman_send_error(s, m, "Config file not found");
1078 astman_start_ack(s, m);
1079 while ((category = ast_category_browse(cfg, category))) {
1081 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1082 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1083 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1086 ast_config_destroy(cfg);
1087 astman_append(s, "\r\n");
1092 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1093 static void json_escape(char *out, const char *in)
1096 if (*in == '\\' || *in == '\"')
1103 static char mandescr_getconfigjson[] =
1104 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1105 "file by category and contents in JSON format. This only makes sense to be used\n"
1106 "using rawman over the HTTP interface.\n"
1108 " Filename: Configuration filename (e.g. foo.conf)\n";
1110 static int action_getconfigjson(struct mansession *s, const struct message *m)
1112 struct ast_config *cfg;
1113 const char *fn = astman_get_header(m, "Filename");
1114 char *category = NULL;
1115 struct ast_variable *v;
1118 unsigned int buf_len = 0;
1119 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1121 if (ast_strlen_zero(fn)) {
1122 astman_send_error(s, m, "Filename not specified");
1126 if (!(cfg = ast_config_load(fn, config_flags))) {
1127 astman_send_error(s, m, "Config file not found");
1132 buf = alloca(buf_len);
1134 astman_start_ack(s, m);
1135 astman_append(s, "JSON: {");
1136 while ((category = ast_category_browse(cfg, category))) {
1138 if (buf_len < 2 * strlen(category) + 1) {
1140 buf = alloca(buf_len);
1142 json_escape(buf, category);
1143 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1146 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1148 astman_append(s, ",");
1149 if (buf_len < 2 * strlen(v->name) + 1) {
1151 buf = alloca(buf_len);
1153 json_escape(buf, v->name);
1154 astman_append(s, "\"%s", buf);
1155 if (buf_len < 2 * strlen(v->value) + 1) {
1157 buf = alloca(buf_len);
1159 json_escape(buf, v->value);
1160 astman_append(s, "%s\"", buf);
1164 astman_append(s, "]");
1166 astman_append(s, "}\r\n\r\n");
1168 ast_config_destroy(cfg);
1173 /* helper function for action_updateconfig */
1174 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1178 const char *action, *cat, *var, *value, *match;
1179 struct ast_category *category;
1180 struct ast_variable *v;
1182 for (x = 0; x < 100000; x++) {
1183 unsigned int object = 0;
1185 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1186 action = astman_get_header(m, hdr);
1187 if (ast_strlen_zero(action))
1189 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1190 cat = astman_get_header(m, hdr);
1191 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1192 var = astman_get_header(m, hdr);
1193 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1194 value = astman_get_header(m, hdr);
1195 if (!ast_strlen_zero(value) && *value == '>') {
1199 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1200 match = astman_get_header(m, hdr);
1201 if (!strcasecmp(action, "newcat")) {
1202 if (!ast_strlen_zero(cat)) {
1203 category = ast_category_new(cat, dfn, 99999);
1205 ast_category_append(cfg, category);
1208 } else if (!strcasecmp(action, "renamecat")) {
1209 if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1210 category = ast_category_get(cfg, cat);
1212 ast_category_rename(category, value);
1214 } else if (!strcasecmp(action, "delcat")) {
1215 if (!ast_strlen_zero(cat))
1216 ast_category_delete(cfg, cat);
1217 } else if (!strcasecmp(action, "update")) {
1218 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1219 ast_variable_update(category, var, value, match, object);
1220 } else if (!strcasecmp(action, "delete")) {
1221 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1222 ast_variable_delete(category, var, match);
1223 } else if (!strcasecmp(action, "append")) {
1224 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1225 (category = ast_category_get(cfg, cat)) &&
1226 (v = ast_variable_new(var, value, dfn))){
1227 if (object || (match && !strcasecmp(match, "object")))
1229 ast_variable_append(category, v);
1235 static char mandescr_updateconfig[] =
1236 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1237 "configuration elements in Asterisk configuration files.\n"
1238 "Variables (X's represent 6 digit number beginning with 000000):\n"
1239 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1240 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1241 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1242 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1243 " Cat-XXXXXX: Category to operate on\n"
1244 " Var-XXXXXX: Variable to work on\n"
1245 " Value-XXXXXX: Value to work on\n"
1246 " Match-XXXXXX: Extra match required to match line\n";
1248 static int action_updateconfig(struct mansession *s, const struct message *m)
1250 struct ast_config *cfg;
1251 const char *sfn = astman_get_header(m, "SrcFilename");
1252 const char *dfn = astman_get_header(m, "DstFilename");
1254 const char *rld = astman_get_header(m, "Reload");
1255 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1257 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1258 astman_send_error(s, m, "Filename not specified");
1261 if (!(cfg = ast_config_load(sfn, config_flags))) {
1262 astman_send_error(s, m, "Config file not found");
1265 handle_updates(s, m, cfg, dfn);
1266 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1267 res = config_text_file_save(dfn, cfg, "Manager");
1268 ast_config_destroy(cfg);
1270 astman_send_error(s, m, "Save of config failed");
1273 astman_send_ack(s, m, NULL);
1274 if (!ast_strlen_zero(rld)) {
1277 ast_module_reload(rld);
1282 /*! \brief Manager WAITEVENT */
1283 static char mandescr_waitevent[] =
1284 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1285 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1286 "session, events will be generated and queued.\n"
1288 " Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1290 static int action_waitevent(struct mansession *s, const struct message *m)
1292 const char *timeouts = astman_get_header(m, "Timeout");
1296 const char *id = astman_get_header(m,"ActionID");
1297 char idText[256] = "";
1299 if (!ast_strlen_zero(id))
1300 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1302 if (!ast_strlen_zero(timeouts)) {
1303 sscanf(timeouts, "%i", &timeout);
1306 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1309 ast_mutex_lock(&s->__lock);
1310 if (s->waiting_thread != AST_PTHREADT_NULL)
1311 pthread_kill(s->waiting_thread, SIGURG);
1313 if (s->managerid) { /* AMI-over-HTTP session */
1315 * Make sure the timeout is within the expire time of the session,
1316 * as the client will likely abort the request if it does not see
1317 * data coming after some amount of time.
1319 time_t now = time(NULL);
1320 int max = s->sessiontimeout - now - 10;
1322 if (max < 0) /* We are already late. Strange but possible. */
1324 if (timeout < 0 || timeout > max)
1326 if (!s->send_events) /* make sure we record events */
1327 s->send_events = -1;
1329 ast_mutex_unlock(&s->__lock);
1331 /* XXX should this go inside the lock ? */
1332 s->waiting_thread = pthread_self(); /* let new events wake up this thread */
1333 ast_debug(1, "Starting waiting for an event!\n");
1335 for (x=0; x < timeout || timeout < 0; x++) {
1336 ast_mutex_lock(&s->__lock);
1339 /* We can have multiple HTTP session point to the same mansession entry.
1340 * The way we deal with it is not very nice: newcomers kick out the previous
1341 * HTTP session. XXX this needs to be improved.
1343 if (s->waiting_thread != pthread_self())
1347 ast_mutex_unlock(&s->__lock);
1350 if (s->managerid == 0) { /* AMI session */
1351 if (ast_wait_for_input(s->fd, 1000))
1353 } else { /* HTTP session */
1357 ast_debug(1, "Finished waiting for an event!\n");
1358 ast_mutex_lock(&s->__lock);
1359 if (s->waiting_thread == pthread_self()) {
1360 struct eventqent *eqe;
1361 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1362 while ( (eqe = NEW_EVENT(s)) ) {
1364 if (((s->readperm & eqe->category) == eqe->category) &&
1365 ((s->send_events & eqe->category) == eqe->category)) {
1366 astman_append(s, "%s", eqe->eventdata);
1368 s->last_ev = unref_event(s->last_ev);
1371 "Event: WaitEventComplete\r\n"
1374 s->waiting_thread = AST_PTHREADT_NULL;
1376 ast_debug(1, "Abandoning event request!\n");
1378 ast_mutex_unlock(&s->__lock);
1382 static char mandescr_listcommands[] =
1383 "Description: Returns the action name and synopsis for every\n"
1384 " action that is available to the user\n"
1385 "Variables: NONE\n";
1387 /*! \note The actionlock is read-locked by the caller of this function */
1388 static int action_listcommands(struct mansession *s, const struct message *m)
1390 struct manager_action *cur;
1391 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1393 astman_start_ack(s, m);
1394 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1395 if (s->writeperm & cur->authority)
1396 astman_append(s, "%s: %s (Priv: %s)\r\n",
1397 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1399 astman_append(s, "\r\n");
1404 static char mandescr_events[] =
1405 "Description: Enable/Disable sending of events to this manager\n"
1408 " EventMask: 'on' if all events should be sent,\n"
1409 " 'off' if no events should be sent,\n"
1410 " 'system,call,log' to select which flags events should have to be sent.\n";
1412 static int action_events(struct mansession *s, const struct message *m)
1414 const char *mask = astman_get_header(m, "EventMask");
1417 res = set_eventmask(s, mask);
1419 astman_send_response(s, m, "Success", "Events: On\r\n");
1421 astman_send_response(s, m, "Success", "Events: Off\r\n");
1426 static char mandescr_logoff[] =
1427 "Description: Logoff this manager session\n"
1428 "Variables: NONE\n";
1430 static int action_logoff(struct mansession *s, const struct message *m)
1432 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1436 static int action_login(struct mansession *s, const struct message *m)
1438 if (authenticate(s, m)) {
1440 astman_send_error(s, m, "Authentication failed");
1443 s->authenticated = 1;
1444 if (manager_displayconnects(s))
1445 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1446 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1447 astman_send_ack(s, m, "Authentication accepted");
1451 static int action_challenge(struct mansession *s, const struct message *m)
1453 const char *authtype = astman_get_header(m, "AuthType");
1455 if (!strcasecmp(authtype, "MD5")) {
1456 if (ast_strlen_zero(s->challenge))
1457 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1458 ast_mutex_lock(&s->__lock);
1459 astman_start_ack(s, m);
1460 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1461 ast_mutex_unlock(&s->__lock);
1463 astman_send_error(s, m, "Must specify AuthType");
1468 static char mandescr_hangup[] =
1469 "Description: Hangup a channel\n"
1471 " Channel: The channel name to be hungup\n";
1473 static int action_hangup(struct mansession *s, const struct message *m)
1475 struct ast_channel *c = NULL;
1476 const char *name = astman_get_header(m, "Channel");
1477 if (ast_strlen_zero(name)) {
1478 astman_send_error(s, m, "No channel specified");
1481 c = ast_get_channel_by_name_locked(name);
1483 astman_send_error(s, m, "No such channel");
1486 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1487 ast_channel_unlock(c);
1488 astman_send_ack(s, m, "Channel Hungup");
1492 static char mandescr_setvar[] =
1493 "Description: Set a global or local channel variable.\n"
1494 "Variables: (Names marked with * are required)\n"
1495 " Channel: Channel to set variable for\n"
1496 " *Variable: Variable name\n"
1499 static int action_setvar(struct mansession *s, const struct message *m)
1501 struct ast_channel *c = NULL;
1502 const char *name = astman_get_header(m, "Channel");
1503 const char *varname = astman_get_header(m, "Variable");
1504 const char *varval = astman_get_header(m, "Value");
1506 if (ast_strlen_zero(varname)) {
1507 astman_send_error(s, m, "No variable specified");
1511 if (!ast_strlen_zero(name)) {
1512 c = ast_get_channel_by_name_locked(name);
1514 astman_send_error(s, m, "No such channel");
1519 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1522 ast_channel_unlock(c);
1524 astman_send_ack(s, m, "Variable Set");
1529 static char mandescr_getvar[] =
1530 "Description: Get the value of a global or local channel variable.\n"
1531 "Variables: (Names marked with * are required)\n"
1532 " Channel: Channel to read variable from\n"
1533 " *Variable: Variable name\n"
1534 " ActionID: Optional Action id for message matching.\n";
1536 static int action_getvar(struct mansession *s, const struct message *m)
1538 struct ast_channel *c = NULL;
1539 const char *name = astman_get_header(m, "Channel");
1540 const char *varname = astman_get_header(m, "Variable");
1542 char workspace[1024] = "";
1544 if (ast_strlen_zero(varname)) {
1545 astman_send_error(s, m, "No variable specified");
1549 if (!ast_strlen_zero(name)) {
1550 c = ast_get_channel_by_name_locked(name);
1552 astman_send_error(s, m, "No such channel");
1557 if (varname[strlen(varname) - 1] == ')') {
1558 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1561 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1565 ast_channel_unlock(c);
1566 astman_start_ack(s, m);
1567 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1573 /*! \brief Manager "status" command to show channels */
1574 /* Needs documentation... */
1575 static int action_status(struct mansession *s, const struct message *m)
1577 const char *name = astman_get_header(m,"Channel");
1578 struct ast_channel *c;
1580 struct timeval now = ast_tvnow();
1581 long elapsed_seconds = 0;
1583 int all = ast_strlen_zero(name); /* set if we want all channels */
1584 const char *id = astman_get_header(m,"ActionID");
1585 char idText[256] = "";
1587 if (!ast_strlen_zero(id))
1588 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1591 c = ast_channel_walk_locked(NULL);
1593 c = ast_get_channel_by_name_locked(name);
1595 astman_send_error(s, m, "No such channel");
1599 astman_send_ack(s, m, "Channel status will follow");
1601 /* if we look by name, we break after the first iteration */
1605 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1610 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1614 "Privilege: Call\r\n"
1616 "CallerIDNum: %s\r\n"
1617 "CallerIDName: %s\r\n"
1618 "Accountcode: %s\r\n"
1619 "ChannelState: %d\r\n"
1620 "ChannelStateDesc: %s\r\n"
1630 S_OR(c->cid.cid_num, ""),
1631 S_OR(c->cid.cid_name, ""),
1634 ast_state2str(c->_state), c->context,
1635 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1639 "Privilege: Call\r\n"
1641 "CallerIDNum: %s\r\n"
1642 "CallerIDName: %s\r\n"
1650 S_OR(c->cid.cid_num, "<unknown>"),
1651 S_OR(c->cid.cid_name, "<unknown>"),
1653 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1655 ast_channel_unlock(c);
1658 c = ast_channel_walk_locked(c);
1661 "Event: StatusComplete\r\n"
1664 "\r\n",idText, channels);
1668 static char mandescr_sendtext[] =
1669 "Description: Sends A Text Message while in a call.\n"
1670 "Variables: (Names marked with * are required)\n"
1671 " *Channel: Channel to send message to\n"
1672 " *Message: Message to send\n"
1673 " ActionID: Optional Action id for message matching.\n";
1675 static int action_sendtext(struct mansession *s, const struct message *m)
1677 struct ast_channel *c = NULL;
1678 const char *name = astman_get_header(m, "Channel");
1679 const char *textmsg = astman_get_header(m, "Message");
1682 if (ast_strlen_zero(name)) {
1683 astman_send_error(s, m, "No channel specified");
1687 if (ast_strlen_zero(textmsg)) {
1688 astman_send_error(s, m, "No Message specified");
1692 c = ast_get_channel_by_name_locked(name);
1694 astman_send_error(s, m, "No such channel");
1698 res = ast_sendtext(c, textmsg);
1699 ast_channel_unlock(c);
1702 astman_send_ack(s, m, "Success");
1704 astman_send_error(s, m, "Failure");
1709 static char mandescr_redirect[] =
1710 "Description: Redirect (transfer) a call.\n"
1711 "Variables: (Names marked with * are required)\n"
1712 " *Channel: Channel to redirect\n"
1713 " ExtraChannel: Second call leg to transfer (optional)\n"
1714 " *Exten: Extension to transfer to\n"
1715 " *Context: Context to transfer to\n"
1716 " *Priority: Priority to transfer to\n"
1717 " ActionID: Optional Action id for message matching.\n";
1719 /*! \brief action_redirect: The redirect manager command */
1720 static int action_redirect(struct mansession *s, const struct message *m)
1722 const char *name = astman_get_header(m, "Channel");
1723 const char *name2 = astman_get_header(m, "ExtraChannel");
1724 const char *exten = astman_get_header(m, "Exten");
1725 const char *context = astman_get_header(m, "Context");
1726 const char *priority = astman_get_header(m, "Priority");
1727 struct ast_channel *chan, *chan2 = NULL;
1731 if (ast_strlen_zero(name)) {
1732 astman_send_error(s, m, "Channel not specified");
1735 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1736 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1737 astman_send_error(s, m, "Invalid priority\n");
1741 /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1742 chan = ast_get_channel_by_name_locked(name);
1745 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1746 astman_send_error(s, m, buf);
1749 if (ast_check_hangup(chan)) {
1750 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1751 ast_channel_unlock(chan);
1754 if (!ast_strlen_zero(name2))
1755 chan2 = ast_get_channel_by_name_locked(name2);
1756 if (chan2 && ast_check_hangup(chan2)) {
1757 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1758 ast_channel_unlock(chan);
1759 ast_channel_unlock(chan2);
1762 res = ast_async_goto(chan, context, exten, pi);
1764 if (!ast_strlen_zero(name2)) {
1766 res = ast_async_goto(chan2, context, exten, pi);
1770 astman_send_ack(s, m, "Dual Redirect successful");
1772 astman_send_error(s, m, "Secondary redirect failed");
1774 astman_send_ack(s, m, "Redirect successful");
1776 astman_send_error(s, m, "Redirect failed");
1778 ast_channel_unlock(chan);
1780 ast_channel_unlock(chan2);
1784 static char mandescr_command[] =
1785 "Description: Run a CLI command.\n"
1786 "Variables: (Names marked with * are required)\n"
1787 " *Command: Asterisk CLI command to run\n"
1788 " ActionID: Optional Action id for message matching.\n";
1790 /*! \brief Manager command "command" - execute CLI command */
1791 static int action_command(struct mansession *s, const struct message *m)
1793 const char *cmd = astman_get_header(m, "Command");
1794 const char *id = astman_get_header(m, "ActionID");
1795 char *buf, *final_buf;
1796 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1797 int fd = mkstemp(template), i = 0;
1800 for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
1801 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
1802 astman_send_error(s, m, "Command blacklisted");
1807 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1808 if (!ast_strlen_zero(id))
1809 astman_append(s, "ActionID: %s\r\n", id);
1810 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1811 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
1812 l = lseek(fd, 0, SEEK_END); /* how many chars available */
1814 /* This has a potential to overflow the stack. Hence, use the heap. */
1815 buf = ast_calloc(1, l + 1);
1816 final_buf = ast_calloc(1, l + 1);
1818 lseek(fd, 0, SEEK_SET);
1822 term_strip(final_buf, buf, l);
1823 final_buf[l] = '\0';
1825 astman_append(s, S_OR(final_buf, buf));
1830 astman_append(s, "--END COMMAND--\r\n\r\n");
1832 ast_free(final_buf);
1836 /* helper function for originate */
1837 struct fast_originate_helper {
1838 char tech[AST_MAX_EXTENSION];
1839 char data[AST_MAX_EXTENSION];
1841 char app[AST_MAX_APP];
1842 char appdata[AST_MAX_EXTENSION];
1843 char cid_name[AST_MAX_EXTENSION];
1844 char cid_num[AST_MAX_EXTENSION];
1845 char context[AST_MAX_CONTEXT];
1846 char exten[AST_MAX_EXTENSION];
1847 char idtext[AST_MAX_EXTENSION];
1848 char account[AST_MAX_ACCOUNT_CODE];
1850 struct ast_variable *vars;
1853 static void *fast_originate(void *data)
1855 struct fast_originate_helper *in = data;
1858 struct ast_channel *chan = NULL;
1859 char requested_channel[AST_CHANNEL_NAME];
1861 if (!ast_strlen_zero(in->app)) {
1862 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1863 S_OR(in->cid_num, NULL),
1864 S_OR(in->cid_name, NULL),
1865 in->vars, in->account, &chan);
1867 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1868 S_OR(in->cid_num, NULL),
1869 S_OR(in->cid_name, NULL),
1870 in->vars, in->account, &chan);
1874 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
1875 /* Tell the manager what happened with the channel */
1876 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1884 "CallerIDNum: %s\r\n"
1885 "CallerIDName: %s\r\n",
1886 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
1887 chan ? chan->uniqueid : "<null>",
1888 S_OR(in->cid_num, "<unknown>"),
1889 S_OR(in->cid_name, "<unknown>")
1892 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1894 ast_channel_unlock(chan);
1899 static char mandescr_originate[] =
1900 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1901 " Application/Data\n"
1902 "Variables: (Names marked with * are required)\n"
1903 " *Channel: Channel name to call\n"
1904 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1905 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1906 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1907 " Application: Application to use\n"
1908 " Data: Data to use (requires 'Application')\n"
1909 " Timeout: How long to wait for call to be answered (in ms)\n"
1910 " CallerID: Caller ID to be set on the outgoing channel\n"
1911 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1912 " Account: Account code\n"
1913 " Async: Set to 'true' for fast origination\n";
1915 static int action_originate(struct mansession *s, const struct message *m)
1917 const char *name = astman_get_header(m, "Channel");
1918 const char *exten = astman_get_header(m, "Exten");
1919 const char *context = astman_get_header(m, "Context");
1920 const char *priority = astman_get_header(m, "Priority");
1921 const char *timeout = astman_get_header(m, "Timeout");
1922 const char *callerid = astman_get_header(m, "CallerID");
1923 const char *account = astman_get_header(m, "Account");
1924 const char *app = astman_get_header(m, "Application");
1925 const char *appdata = astman_get_header(m, "Data");
1926 const char *async = astman_get_header(m, "Async");
1927 const char *id = astman_get_header(m, "ActionID");
1928 struct ast_variable *vars = astman_get_variables(m);
1930 char *l = NULL, *n = NULL;
1940 astman_send_error(s, m, "Channel not specified");
1943 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1944 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1945 astman_send_error(s, m, "Invalid priority\n");
1949 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1950 astman_send_error(s, m, "Invalid timeout\n");
1953 ast_copy_string(tmp, name, sizeof(tmp));
1955 data = strchr(tmp, '/');
1957 astman_send_error(s, m, "Invalid channel\n");
1961 ast_copy_string(tmp2, callerid, sizeof(tmp2));
1962 ast_callerid_parse(tmp2, &n, &l);
1964 if (ast_strlen_zero(n))
1968 ast_shrink_phone_number(l);
1969 if (ast_strlen_zero(l))
1972 if (ast_true(async)) {
1973 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1977 if (!ast_strlen_zero(id))
1978 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1979 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1980 ast_copy_string(fast->data, data, sizeof(fast->data));
1981 ast_copy_string(fast->app, app, sizeof(fast->app));
1982 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1984 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1986 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1988 ast_copy_string(fast->context, context, sizeof(fast->context));
1989 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1990 ast_copy_string(fast->account, account, sizeof(fast->account));
1992 fast->priority = pi;
1993 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
1999 } else if (!ast_strlen_zero(app)) {
2000 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2002 if (exten && context && pi)
2003 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2005 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2010 astman_send_ack(s, m, "Originate successfully queued");
2012 astman_send_error(s, m, "Originate failed");
2016 /*! \brief Help text for manager command mailboxstatus
2018 static char mandescr_mailboxstatus[] =
2019 "Description: Checks a voicemail account for status.\n"
2020 "Variables: (Names marked with * are required)\n"
2021 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2022 " ActionID: Optional ActionID for message matching.\n"
2023 "Returns number of messages.\n"
2024 " Message: Mailbox Status\n"
2025 " Mailbox: <mailboxid>\n"
2026 " Waiting: <count>\n"
2029 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2031 const char *mailbox = astman_get_header(m, "Mailbox");
2034 if (ast_strlen_zero(mailbox)) {
2035 astman_send_error(s, m, "Mailbox not specified");
2038 ret = ast_app_has_voicemail(mailbox, NULL);
2039 astman_start_ack(s, m);
2040 astman_append(s, "Message: Mailbox Status\r\n"
2042 "Waiting: %d\r\n\r\n", mailbox, ret);
2046 static char mandescr_mailboxcount[] =
2047 "Description: Checks a voicemail account for new messages.\n"
2048 "Variables: (Names marked with * are required)\n"
2049 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2050 " ActionID: Optional ActionID for message matching.\n"
2051 "Returns number of new and old messages.\n"
2052 " Message: Mailbox Message Count\n"
2053 " Mailbox: <mailboxid>\n"
2054 " NewMessages: <count>\n"
2055 " OldMessages: <count>\n"
2057 static int action_mailboxcount(struct mansession *s, const struct message *m)
2059 const char *mailbox = astman_get_header(m, "Mailbox");
2060 int newmsgs = 0, oldmsgs = 0;
2062 if (ast_strlen_zero(mailbox)) {
2063 astman_send_error(s, m, "Mailbox not specified");
2066 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2067 astman_start_ack(s, m);
2068 astman_append(s, "Message: Mailbox Message Count\r\n"
2070 "NewMessages: %d\r\n"
2071 "OldMessages: %d\r\n"
2073 mailbox, newmsgs, oldmsgs);
2077 static char mandescr_extensionstate[] =
2078 "Description: Report the extension state for given extension.\n"
2079 " If the extension has a hint, will use devicestate to check\n"
2080 " the status of the device connected to the extension.\n"
2081 "Variables: (Names marked with * are required)\n"
2082 " *Exten: Extension to check state on\n"
2083 " *Context: Context for extension\n"
2084 " ActionId: Optional ID for this transaction\n"
2085 "Will return an \"Extension Status\" message.\n"
2086 "The response will include the hint for the extension and the status.\n";
2088 static int action_extensionstate(struct mansession *s, const struct message *m)
2090 const char *exten = astman_get_header(m, "Exten");
2091 const char *context = astman_get_header(m, "Context");
2092 char hint[256] = "";
2094 if (ast_strlen_zero(exten)) {
2095 astman_send_error(s, m, "Extension not specified");
2098 if (ast_strlen_zero(context))
2099 context = "default";
2100 status = ast_extension_state(NULL, context, exten);
2101 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2102 astman_start_ack(s, m);
2103 astman_append(s, "Message: Extension Status\r\n"
2107 "Status: %d\r\n\r\n",
2108 exten, context, hint, status);
2112 static char mandescr_timeout[] =
2113 "Description: Hangup a channel after a certain time.\n"
2114 "Variables: (Names marked with * are required)\n"
2115 " *Channel: Channel name to hangup\n"
2116 " *Timeout: Maximum duration of the call (sec)\n"
2117 "Acknowledges set time with 'Timeout Set' message\n";
2119 static int action_timeout(struct mansession *s, const struct message *m)
2121 struct ast_channel *c;
2122 const char *name = astman_get_header(m, "Channel");
2123 int timeout = atoi(astman_get_header(m, "Timeout"));
2125 if (ast_strlen_zero(name)) {
2126 astman_send_error(s, m, "No channel specified");
2130 astman_send_error(s, m, "No timeout specified");
2133 c = ast_get_channel_by_name_locked(name);
2135 astman_send_error(s, m, "No such channel");
2138 ast_channel_setwhentohangup(c, timeout);
2139 ast_channel_unlock(c);
2140 astman_send_ack(s, m, "Timeout Set");
2145 * Send any applicable events to the client listening on this socket.
2146 * Wait only for a finite time on each event, and drop all events whether
2147 * they are successfully sent or not.
2149 static int process_events(struct mansession *s)
2153 ast_mutex_lock(&s->__lock);
2155 struct eventqent *eqe;
2157 while ( (eqe = NEW_EVENT(s)) ) {
2159 if (!ret && s->authenticated &&
2160 (s->readperm & eqe->category) == eqe->category &&
2161 (s->send_events & eqe->category) == eqe->category) {
2162 if (send_string(s, eqe->eventdata) < 0)
2163 ret = -1; /* don't send more */
2165 s->last_ev = unref_event(s->last_ev);
2168 ast_mutex_unlock(&s->__lock);
2172 static char mandescr_userevent[] =
2173 "Description: Send an event to manager sessions.\n"
2174 "Variables: (Names marked with * are required)\n"
2175 " *UserEvent: EventStringToSend\n"
2176 " Header1: Content1\n"
2177 " HeaderN: ContentN\n";
2179 static int action_userevent(struct mansession *s, const struct message *m)
2181 const char *event = astman_get_header(m, "UserEvent");
2182 char body[2048] = "";
2184 for (x = 0; x < m->hdrcount; x++) {
2185 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2186 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2187 bodylen += strlen(m->headers[x]);
2188 ast_copy_string(body + bodylen, "\r\n", 3);
2193 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2197 static char mandescr_coresettings[] =
2198 "Description: Query for Core PBX settings.\n"
2199 "Variables: (Names marked with * are optional)\n"
2200 " *ActionID: ActionID of this transaction\n";
2202 /*! \brief Show PBX core settings information */
2203 static int action_coresettings(struct mansession *s, const struct message *m)
2205 const char *actionid = astman_get_header(m, "ActionID");
2206 char idText[150] = "";
2208 if (!ast_strlen_zero(actionid))
2209 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2211 astman_append(s, "Response: Success\r\n"
2213 "AMIversion: %s\r\n"
2214 "AsteriskVersion: %s\r\n"
2215 "SystemName: %s\r\n"
2216 "CoreMaxCalls: %d\r\n"
2217 "CoreMaxLoadAvg: %f\r\n"
2218 "CoreRunUser: %s\r\n"
2219 "CoreRunGroup: %s\r\n"
2220 "CoreMaxFilehandles: %d\r\n"
2221 "CoreRealTimeEnabled: %s\r\n"
2222 "CoreCDRenabled: %s\r\n"
2223 "CoreHTTPenabled: %s\r\n"
2228 ast_config_AST_SYSTEM_NAME,
2231 ast_config_AST_RUN_USER,
2232 ast_config_AST_RUN_GROUP,
2234 ast_realtime_enabled() ? "Yes" : "No",
2235 check_cdr_enabled() ? "Yes" : "No",
2236 check_webmanager_enabled() ? "Yes" : "No"
2241 static char mandescr_corestatus[] =
2242 "Description: Query for Core PBX status.\n"
2243 "Variables: (Names marked with * are optional)\n"
2244 " *ActionID: ActionID of this transaction\n";
2246 /*! \brief Show PBX core status information */
2247 static int action_corestatus(struct mansession *s, const struct message *m)
2249 const char *actionid = astman_get_header(m, "ActionID");
2251 char startuptime[150];
2252 char reloadtime[150];
2255 if (!ast_strlen_zero(actionid))
2256 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2258 ast_localtime(&ast_startuptime, &tm, NULL);
2259 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2260 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2261 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2263 astman_append(s, "Response: Success\r\n"
2265 "CoreStartupTime: %s\r\n"
2266 "CoreReloadTime: %s\r\n"
2267 "CoreCurrentCalls: %d\r\n"
2272 ast_active_channels()
2277 static char mandescr_reload[] =
2278 "Description: Send a reload event.\n"
2279 "Variables: (Names marked with * are optional)\n"
2280 " *ActionID: ActionID of this transaction\n"
2281 " *Module: Name of the module to reload\n";
2283 /*! \brief Send a reload event */
2284 static int action_reload(struct mansession *s, const struct message *m)
2286 const char *actionid = astman_get_header(m, "ActionID");
2287 const char *module = astman_get_header(m, "Module");
2288 int res = ast_module_reload(S_OR(module, NULL));
2289 char idText[80] = "";
2291 if (!ast_strlen_zero(actionid))
2292 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2294 astman_append(s, "Response: Success\r\n%s", idText);
2296 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2300 static char mandescr_coreshowchannels[] =
2301 "Description: List currently defined channels and some information\n"
2304 " ActionID: Optional Action id for message matching.\n";
2306 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2307 * and some information about them. */
2308 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2310 const char *actionid = astman_get_header(m, "ActionID");
2311 char actionidtext[256] = "";
2312 struct ast_channel *c = NULL;
2314 int duration, durh, durm, durs;
2316 if (!ast_strlen_zero(actionid))
2317 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2319 astman_send_listack(s, m, "Channels will follow", "start");
2321 while ((c = ast_channel_walk_locked(c)) != NULL) {
2322 struct ast_channel *bc = ast_bridged_channel(c);
2323 char durbuf[10] = "";
2325 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2326 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2327 durh = duration / 3600;
2328 durm = (duration % 3600) / 60;
2329 durs = duration % 60;
2330 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2339 "ChannelState: %d\r\n"
2340 "ChannelStateDesc: %s\r\n"
2341 "Application: %s\r\n"
2342 "ApplicationData: %s\r\n"
2343 "CallerIDnum: %s\r\n"
2345 "AccountCode: %s\r\n"
2346 "BridgedChannel: %s\r\n"
2347 "BridgedUniqueID: %s\r\n"
2348 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2349 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2350 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2351 ast_channel_unlock(c);
2356 "Event: CoreShowChannelsComplete\r\n"
2357 "EventList: Complete\r\n"
2360 "\r\n", numchans, actionidtext);
2365 static char mandescr_modulecheck[] =
2366 "Description: Checks if Asterisk module is loaded\n"
2368 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2369 " Module: <name> Asterisk module name (not including extension)\n"
2371 "Will return Success/Failure\n"
2372 "For success returns, the module revision number is included.\n";
2374 /* Manager function to check if module is loaded */
2375 static int manager_modulecheck(struct mansession *s, const struct message *m)
2378 const char *module = astman_get_header(m, "Module");
2379 const char *id = astman_get_header(m,"ActionID");
2380 char idText[BUFSIZ];
2381 const char *version;
2382 char filename[BUFSIZ/2];
2385 snprintf(filename, sizeof(filename), module);
2386 if ((cut = strchr(filename, '.'))) {
2389 cut = filename + strlen(filename);
2391 sprintf(cut, ".so");
2392 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2393 res = ast_module_check(filename);
2395 astman_send_error(s, m, "Module not loaded");
2399 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2400 version = ast_file_version_find(filename);
2402 if (!ast_strlen_zero(id))
2403 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2404 astman_append(s, "Response: Success\r\n%s", idText);
2405 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2409 static char mandescr_moduleload[] =
2410 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2412 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2413 " Module: <name> Asterisk module name (including .so extension)\n"
2414 " or subsystem identifier:\n"
2415 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2416 " LoadType: load | unload | reload\n"
2417 " The operation to be done on module\n"
2418 " If no module is specified for a reload loadtype, all modules are reloaded";
2420 static int manager_moduleload(struct mansession *s, const struct message *m)
2423 const char *module = astman_get_header(m, "Module");
2424 const char *loadtype = astman_get_header(m, "LoadType");
2426 if (!loadtype || strlen(loadtype) == 0)
2427 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2428 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2429 astman_send_error(s, m, "Need module name");
2431 if (!strcasecmp(loadtype, "load")) {
2432 res = ast_load_resource(module);
2434 astman_send_error(s, m, "Could not load module.");
2436 astman_send_ack(s, m, "Module loaded.");
2437 } else if (!strcasecmp(loadtype, "unload")) {
2438 res = ast_unload_resource(module, AST_FORCE_SOFT);
2440 astman_send_error(s, m, "Could not unload module.");
2442 astman_send_ack(s, m, "Module unloaded.");
2443 } else if (!strcasecmp(loadtype, "reload")) {
2444 if (module != NULL) {
2445 res = ast_module_reload(module);
2447 astman_send_error(s, m, "No such module.");
2449 astman_send_error(s, m, "Module does not support reload action.");
2451 astman_send_ack(s, m, "Module reloaded.");
2453 ast_module_reload(NULL); /* Reload all modules */
2454 astman_send_ack(s, m, "All modules reloaded");
2457 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2462 * Done with the action handlers here, we start with the code in charge
2463 * of accepting connections and serving them.
2464 * accept_thread() forks a new thread for each connection, session_do(),
2465 * which in turn calls get_input() repeatedly until a full message has
2466 * been accumulated, and then invokes process_message() to pass it to
2467 * the appropriate handler.
2471 * Process an AMI message, performing desired action.
2472 * Return 0 on success, -1 on error that require the session to be destroyed.
2474 static int process_message(struct mansession *s, const struct message *m)
2476 char action[80] = "";
2478 struct manager_action *tmp;
2479 const char *user = astman_get_header(m, "Username");
2481 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2482 ast_debug(1, "Manager received command '%s'\n", action);
2484 if (ast_strlen_zero(action)) {
2485 ast_mutex_lock(&s->__lock);
2486 astman_send_error(s, m, "Missing action in request");
2487 ast_mutex_unlock(&s->__lock);
2491 if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2492 ast_mutex_lock(&s->__lock);
2493 astman_send_error(s, m, "Permission denied");
2494 ast_mutex_unlock(&s->__lock);
2498 if (!allowmultiplelogin && !s->authenticated && user &&
2499 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2500 if (check_manager_session_inuse(user)) {
2502 ast_mutex_lock(&s->__lock);
2503 astman_send_error(s, m, "Login Already In Use");
2504 ast_mutex_unlock(&s->__lock);
2509 AST_RWLIST_RDLOCK(&actions);
2510 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2511 if (strcasecmp(action, tmp->action))
2513 if (s->writeperm & tmp->authority)
2514 ret = tmp->func(s, m);
2516 astman_send_error(s, m, "Permission denied");
2519 AST_RWLIST_UNLOCK(&actions);
2523 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2524 ast_mutex_lock(&s->__lock);
2525 astman_send_error(s, m, buf);
2526 ast_mutex_unlock(&s->__lock);
2530 /* Once done with our message, deliver any pending events */
2531 return process_events(s);
2535 * Read one full line (including crlf) from the manager socket.
2537 * \r\n is the only valid terminator for the line.
2538 * (Note that, later, '\0' will be considered as the end-of-line marker,
2539 * so everything between the '\0' and the '\r\n' will not be used).
2540 * Also note that we assume output to have at least "maxlen" space.
2543 static int get_input(struct mansession *s, char *output)
2546 int maxlen = sizeof(s->inbuf) - 1;
2547 char *src = s->inbuf;
2550 * Look for \r\n within the buffer. If found, copy to the output
2551 * buffer and return, trimming the \r\n (not used afterwards).
2553 for (x = 0; x < s->inlen; x++) {
2554 int cr; /* set if we have \r */
2555 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2556 cr = 2; /* Found. Update length to include \r\n */
2557 else if (src[x] == '\n')
2558 cr = 1; /* also accept \n only */
2561 memmove(output, src, x); /*... but trim \r\n */
2562 output[x] = '\0'; /* terminate the string */
2563 x += cr; /* number of bytes used */
2564 s->inlen -= x; /* remaining size */
2565 memmove(src, src + x, s->inlen); /* remove used bytes */
2568 if (s->inlen >= maxlen) {
2569 /* no crlf found, and buffer full - sorry, too long for us */
2570 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2575 /* XXX do we really need this locking ? */
2576 ast_mutex_lock(&s->__lock);
2577 s->waiting_thread = pthread_self();
2578 ast_mutex_unlock(&s->__lock);
2580 res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
2582 ast_mutex_lock(&s->__lock);
2583 s->waiting_thread = AST_PTHREADT_NULL;
2584 ast_mutex_unlock(&s->__lock);
2587 /* If we get a signal from some other thread (typically because
2588 * there are new events queued), return 0 to notify the caller.
2592 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2595 ast_mutex_lock(&s->__lock);
2596 res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2598 res = -1; /* error return */
2601 src[s->inlen] = '\0';
2604 ast_mutex_unlock(&s->__lock);
2608 static int do_message(struct mansession *s)
2610 struct message m = { 0 };
2611 char header_buf[sizeof(s->inbuf)] = { '\0' };
2615 /* Check if any events are pending and do them if needed */
2616 if (process_events(s))
2618 res = get_input(s, header_buf);
2621 } else if (res > 0) {
2622 if (ast_strlen_zero(header_buf))
2623 return process_message(s, &m) ? -1 : 0;
2624 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2625 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2632 /*! \brief The body of the individual manager session.
2633 * Call get_input() to read one line at a time
2634 * (or be woken up on new events), collect the lines in a
2635 * message until found an empty line, and execute the request.
2636 * In any case, deliver events asynchronously through process_events()
2637 * (called from here if no line is available, or at the end of
2638 * process_message(). )
2640 static void *session_do(void *data)
2642 struct server_instance *ser = data;
2643 struct mansession *s = ast_calloc(1, sizeof(*s));
2650 s->writetimeout = 100;
2651 s->waiting_thread = AST_PTHREADT_NULL;
2653 flags = fcntl(ser->fd, F_GETFL);
2654 if (!block_sockets) /* make sure socket is non-blocking */
2655 flags |= O_NONBLOCK;
2657 flags &= ~O_NONBLOCK;
2658 fcntl(ser->fd, F_SETFL, flags);
2660 ast_mutex_init(&s->__lock);
2661 s->send_events = -1;
2662 /* these fields duplicate those in the 'ser' structure */
2665 s->sin = ser->requestor;
2667 AST_LIST_LOCK(&sessions);
2668 AST_LIST_INSERT_HEAD(&sessions, s, list);
2669 ast_atomic_fetchadd_int(&num_sessions, 1);
2670 AST_LIST_UNLOCK(&sessions);
2671 /* Hook to the tail of the event queue */
2672 s->last_ev = grab_last();
2674 astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
2676 if ((res = do_message(s)) < 0)
2679 /* session is over, explain why and terminate */
2680 if (s->authenticated) {
2681 if (manager_displayconnects(s))
2682 ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2683 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2685 if (displayconnects)
2686 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2687 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2690 /* It is possible under certain circumstances for this session thread
2691 to complete its work and exit *before* the thread that created it
2692 has finished executing the ast_pthread_create_background() function.
2693 If this occurs, some versions of glibc appear to act in a buggy
2694 fashion and attempt to write data into memory that it thinks belongs
2695 to the thread but is in fact not owned by the thread (or may have
2696 been freed completely).
2698 Causing this thread to yield to other threads at least one time
2699 appears to work around this bug.
2710 /*! \brief remove at most n_max stale session from the list. */
2711 static void purge_sessions(int n_max)
2713 struct mansession *s;
2714 time_t now = time(NULL);
2716 AST_LIST_LOCK(&sessions);
2717 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2718 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2719 AST_LIST_REMOVE_CURRENT(list);
2720 ast_atomic_fetchadd_int(&num_sessions, -1);
2721 if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
2722 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
2723 s->username, ast_inet_ntoa(s->sin.sin_addr));
2725 free_session(s); /* XXX outside ? */
2730 AST_LIST_TRAVERSE_SAFE_END;
2731 AST_LIST_UNLOCK(&sessions);
2735 * events are appended to a queue from where they
2736 * can be dispatched to clients.
2738 static int append_event(const char *str, int category)
2740 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2741 static int seq; /* sequence number */
2746 /* need to init all fields, because ast_malloc() does not */
2748 tmp->category = category;
2749 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2750 AST_LIST_NEXT(tmp, eq_next) = NULL;
2751 strcpy(tmp->eventdata, str);
2753 AST_LIST_LOCK(&all_events);
2754 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
2755 AST_LIST_UNLOCK(&all_events);
2760 /* XXX see if can be moved inside the function */
2761 AST_THREADSTORAGE(manager_event_buf);
2762 #define MANAGER_EVENT_BUF_INITSIZE 256
2764 /*! \brief manager_event: Send AMI event to client */
2765 int __manager_event(int category, const char *event,
2766 const char *file, int line, const char *func, const char *fmt, ...)
2768 struct mansession *s;
2769 struct manager_custom_hook *hook;
2770 struct ast_str *auth = ast_str_alloca(80);
2771 const char *cat_str;
2774 struct ast_str *buf;
2776 /* Abort if there aren't any manager sessions */
2780 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2783 cat_str = authority_to_str(category, &auth);
2784 ast_str_set(&buf, 0,
2785 "Event: %s\r\nPrivilege: %s\r\n",
2788 if (timestampevents) {
2790 ast_str_append(&buf, 0,
2791 "Timestamp: %ld.%06lu\r\n",
2792 now.tv_sec, (unsigned long) now.tv_usec);
2794 if (manager_debug) {
2796 ast_str_append(&buf, 0,
2797 "SequenceNumber: %d\r\n",
2798 ast_atomic_fetchadd_int(&seq, 1));
2799 ast_str_append(&buf, 0,
2800 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
2804 ast_str_append_va(&buf, 0, fmt, ap);
2807 ast_str_append(&buf, 0, "\r\n");
2809 append_event(buf->str, category);
2811 /* Wake up any sleeping sessions */
2812 AST_LIST_LOCK(&sessions);
2813 AST_LIST_TRAVERSE(&sessions, s, list) {
2814 ast_mutex_lock(&s->__lock);
2815 if (s->waiting_thread != AST_PTHREADT_NULL)
2816 pthread_kill(s->waiting_thread, SIGURG);
2817 ast_mutex_unlock(&s->__lock);
2819 AST_LIST_UNLOCK(&sessions);
2821 AST_RWLIST_RDLOCK(&manager_hooks);
2822 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
2823 hook->helper(category, event, buf->str);
2825 AST_RWLIST_UNLOCK(&manager_hooks);
2831 * support functions to register/unregister AMI action handlers,
2833 int ast_manager_unregister(char *action)
2835 struct manager_action *cur;
2837 AST_RWLIST_WRLOCK(&actions);
2838 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
2839 if (!strcasecmp(action, cur->action)) {
2840 AST_RWLIST_REMOVE_CURRENT(list);
2842 ast_verb(2, "Manager unregistered action %s\n", action);
2846 AST_RWLIST_TRAVERSE_SAFE_END;
2847 AST_RWLIST_UNLOCK(&actions);
2852 static int manager_state_cb(char *context, char *exten, int state, void *data)
2854 /* Notify managers of change */
2856 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
2858 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
2862 static int ast_manager_register_struct(struct manager_action *act)
2864 struct manager_action *cur, *prev = NULL;
2866 AST_RWLIST_WRLOCK(&actions);
2867 AST_RWLIST_TRAVERSE(&actions, cur, list) {
2868 int ret = strcasecmp(cur->action, act->action);
2870 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2871 AST_RWLIST_UNLOCK(&actions);
2874 if (ret > 0) { /* Insert these alphabetically */
2881 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
2883 AST_RWLIST_INSERT_HEAD(&actions, act, list);
2885 ast_verb(2, "Manager registered action %s\n", act->action);
2887 AST_RWLIST_UNLOCK(&actions);
2892 /*! \brief register a new command with manager, including online help. This is
2893 the preferred way to register a manager command */
2894 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2896 struct manager_action *cur = NULL;
2898 if (!(cur = ast_calloc(1, sizeof(*cur))))
2901 cur->action = action;
2902 cur->authority = auth;
2904 cur->synopsis = synopsis;
2905 cur->description = description;
2907 ast_manager_register_struct(cur);
2912 END Doxygen group */
2915 * The following are support functions for AMI-over-http.
2916 * The common entry point is generic_http_callback(),
2917 * which extracts HTTP header and URI fields and reformats
2918 * them into AMI messages, locates a proper session
2919 * (using the mansession_id Cookie or GET variable),
2920 * and calls process_message() as for regular AMI clients.
2921 * When done, the output (which goes to a temporary file)
2922 * is read back into a buffer and reformatted as desired,
2923 * then fed back to the client over the original socket.
2926 enum output_format {
2932 static char *contenttype[] = {
2933 [FORMAT_RAW] = "plain",
2934 [FORMAT_HTML] = "html",
2935 [FORMAT_XML] = "xml",
2939 * locate an http session in the list. The search key (ident) is
2940 * the value of the mansession_id cookie (0 is not valid and means
2941 * a session on the AMI socket).
2943 static struct mansession *find_session(unsigned long ident)
2945 struct mansession *s;
2950 AST_LIST_LOCK(&sessions);
2951 AST_LIST_TRAVERSE(&sessions, s, list) {
2952 ast_mutex_lock(&s->__lock);
2953 if (s->managerid == ident && !s->needdestroy) {
2954 ast_atomic_fetchadd_int(&s->inuse, 1);
2957 ast_mutex_unlock(&s->__lock);
2959 AST_LIST_UNLOCK(&sessions);
2964 int astman_verify_session_readpermissions(unsigned long ident, int perm)
2967 struct mansession *s;
2969 AST_LIST_LOCK(&sessions);
2970 AST_LIST_TRAVERSE(&sessions, s, list) {
2971 ast_mutex_lock(&s->__lock);
2972 if ((s->managerid == ident) && (s->readperm & perm)) {
2974 ast_mutex_unlock(&s->__lock);
2977 ast_mutex_unlock(&s->__lock);
2979 AST_LIST_UNLOCK(&sessions);
2983 int astman_verify_session_writepermissions(unsigned long ident, int perm)
2986 struct mansession *s;
2988 AST_LIST_LOCK(&sessions);
2989 AST_LIST_TRAVERSE(&sessions, s, list) {
2990 ast_mutex_lock(&s->__lock);
2991 if ((s->managerid == ident) && (s->writeperm & perm)) {
2993 ast_mutex_unlock(&s->__lock);
2996 ast_mutex_unlock(&s->__lock);
2998 AST_LIST_UNLOCK(&sessions);
3003 * convert to xml with various conversion:
3004 * mode & 1 -> lowercase;
3005 * mode & 2 -> replace non-alphanumeric chars with underscore
3007 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
3009 /* store in a local buffer to avoid calling ast_str_append too often */
3012 int space = sizeof(buf);
3013 /* repeat until done and nothing to flush */
3014 for ( ; *src || dst != buf ; src++) {
3015 if (*src == '\0' || space < 10) { /* flush */
3017 ast_str_append(out, 0, "%s", buf);
3019 space = sizeof(buf);
3024 if ( (mode & 2) && !isalnum(*src)) {
3031 strcpy(dst, "<");
3036 strcpy(dst, ">");
3041 strcpy(dst, """);
3046 strcpy(dst, "'");
3051 strcpy(dst, "&");
3057 *dst++ = mode ? tolower(*src) : *src;
3063 struct variable_count {
3068 static int compress_char(char c)
3073 else if (c >= 'a' && c <= 'z')
3081 static int variable_count_hash_fn(const void *vvc, const int flags)
3083 const struct variable_count *vc = vvc;
3085 for (i = 0; i < 5; i++) {
3086 if (vc->varname[i] == '\0')
3088 res += compress_char(vc->varname[i]) << (i * 6);
3093 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
3095 /* Due to the simplicity of struct variable_count, it makes no difference
3096 * if you pass in objects or strings, the same operation applies. This is
3097 * due to the fact that the hash occurs on the first element, which means
3098 * the address of both the struct and the string are exactly the same. */
3099 struct variable_count *vc = obj;
3101 return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
3104 /*! \brief Convert the input into XML or HTML.
3105 * The input is supposed to be a sequence of lines of the form
3107 * optionally followed by a blob of unformatted text.
3108 * A blank line is a section separator. Basically, this is a
3109 * mixture of the format of Manager Interface and CLI commands.
3110 * The unformatted text is considered as a single value of a field
3111 * named 'Opaque-data'.
3113 * At the moment the output format is the following (but it may
3114 * change depending on future requirements so don't count too
3115 * much on it when writing applications):
3117 * General: the unformatted text is used as a value of
3118 * XML output: to be completed
3121 * Each section is within <response type="object" id="xxx">
3122 * where xxx is taken from ajaxdest variable or defaults to unknown
3123 * Each row is reported as an attribute Name="value" of an XML
3124 * entity named from the variable ajaxobjtype, default to "generic"
3128 * each Name-value pair is output as a single row of a two-column table.
3129 * Sections (blank lines in the input) are separated by a <HR>
3132 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
3134 struct ast_variable *v;
3135 const char *dest = NULL;
3137 const char *objtype = NULL;
3138 int in_data = 0; /* parsing data */
3140 int xml = (format == FORMAT_XML);
3141 struct variable_count *vc = NULL;
3142 struct ao2_container *vco = NULL;
3144 for (v = vars; v; v = v->next) {
3145 if (!dest && !strcasecmp(v->name, "ajaxdest"))
3147 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
3153 objtype = "generic";
3155 /* we want to stop when we find an empty line */
3157 val = strsep(&in, "\r\n"); /* mark start and end of line */
3158 if (in && *in == '\n') /* remove trailing \n if any */
3160 ast_trim_blanks(val);
3161 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
3162 if (ast_strlen_zero(val)) {
3163 if (in_data) { /* close data */
3164 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3167 ast_str_append(out, 0, xml ? " /></response>\n" :
3168 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3175 /* we expect Name: value lines */
3179 var = strsep(&val, ":");
3180 if (val) { /* found the field name */
3181 val = ast_skip_blanks(val);
3182 ast_trim_blanks(var);
3183 } else { /* field name not found, move to opaque mode */
3185 var = "Opaque-data";
3191 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
3193 ast_str_append(out, 0, "<body>\n");
3194 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
3198 if (!in_data) { /* build appropriate line start */
3199 ast_str_append(out, 0, xml ? " " : "<tr><td>");
3200 if ((vc = ao2_find(vco, var, 0)))
3203 /* Create a new entry for this one */
3204 vc = ao2_alloc(sizeof(*vc), NULL);
3209 xml_copy_escape(out, var, xml ? 1 | 2 : 0);
3211 ast_str_append(out, 0, "-%d", vc->count);
3213 ast_str_append(out, 0, xml ? "='" : "</td><td>");
3214 if (!strcmp(var, "Opaque-data"))
3217 xml_copy_escape(out, val, 0); /* data field */
3219 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3221 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
3224 ast_str_append(out, 0, xml ? " /></response>\n" :
3225 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3230 static struct ast_str *generic_http_callback(enum output_format format,
3231 struct sockaddr_in *requestor, const char *uri,
3232 struct ast_variable *params, int *status,
3233 char **title, int *contentlength)
3235 struct mansession *s = NULL;
3236 unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
3238 struct ast_variable *v;
3239 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
3240 struct ast_str *out = NULL;
3241 struct message m = { 0 };
3245 for (v = params; v; v = v->next) {
3246 if (!strcasecmp(v->name, "mansession_id")) {
3247 sscanf(v->value, "%lx", &ident);
3252 if (!(s = find_session(ident))) {
3253 /* Create new session.
3254 * While it is not in the list we don't need any locking
3256 if (!(s = ast_calloc(1, sizeof(*s)))) {
3258 goto generic_callback_out;
3260 s->sin = *requestor;
3262 s->waiting_thread = AST_PTHREADT_NULL;