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/tcptls.h"
69 #include "asterisk/http.h"
70 #include "asterisk/version.h"
71 #include "asterisk/threadstorage.h"
72 #include "asterisk/linkedlists.h"
73 #include "asterisk/version.h"
74 #include "asterisk/term.h"
75 #include "asterisk/astobj2.h"
78 * Linked list of events.
79 * Global events are appended to the list by append_event().
80 * The usecount is the number of stored pointers to the element,
81 * excluding the list pointers. So an element that is only in
82 * the list has a usecount of 0, not 1.
84 * Clients have a pointer to the last event processed, and for each
85 * of these clients we track the usecount of the elements.
86 * If we have a pointer to an entry in the list, it is safe to navigate
87 * it forward because elements will not be deleted, but only appended.
88 * The worst that can happen is seeing the pointer still NULL.
90 * When the usecount of an element drops to 0, and the element is the
91 * first in the list, we can remove it. Removal is done within the
92 * main thread, which is woken up for the purpose.
94 * For simplicity of implementation, we make sure the list is never empty.
97 int usecount; /*!< # of clients who still need the event */
99 unsigned int seq; /*!< sequence number */
100 AST_LIST_ENTRY(eventqent) eq_next;
101 char eventdata[1]; /*!< really variable size, allocated by append_event() */
104 static AST_LIST_HEAD_STATIC(all_events, eventqent);
106 static int displayconnects = 1;
107 static int allowmultiplelogin = 1;
108 static int timestampevents;
109 static int httptimeout = 60;
110 static int manager_enabled = 0;
111 static int webmanager_enabled = 0;
113 static int block_sockets;
114 static int num_sessions;
116 static int manager_debug; /*!< enable some debugging code in the manager */
119 * Descriptor for a manager session, either on the AMI socket or over HTTP.
122 * AMI session have managerid == 0; the entry is created upon a connect,
123 * and destroyed with the socket.
124 * HTTP sessions have managerid != 0, the value is used as a search key
125 * to lookup sessions (using the mansession_id cookie).
127 static const char *command_blacklist[] = {
133 pthread_t ms_t; /*!< Execution thread, basically useless */
134 ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
135 /* XXX need to document which fields it is protecting */
136 struct sockaddr_in sin; /*!< address we are connecting from */
137 FILE *f; /*!< fdopen() on the underlying fd */
138 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
139 int inuse; /*!< number of HTTP sessions using this entry */
140 int needdestroy; /*!< Whether an HTTP session should be destroyed */
141 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
142 unsigned long managerid; /*!< Unique manager identifier, 0 for AMI sessions */
143 time_t sessionstart; /*!< Session start time */
144 time_t sessiontimeout; /*!< Session timeout if HTTP */
145 char username[80]; /*!< Logged in username */
146 char challenge[10]; /*!< Authentication challenge */
147 int authenticated; /*!< Authentication status */
148 int readperm; /*!< Authorization for reading */
149 int writeperm; /*!< Authorization for writing */
150 char inbuf[1025]; /*!< Buffer */
151 /* we use the extra byte to add a '\0' and simplify parsing */
152 int inlen; /*!< number of buffered bytes */
153 int send_events; /*!< XXX what ? */
154 struct eventqent *last_ev; /*!< last event processed. */
155 int writetimeout; /*!< Timeout for ast_carefulwrite() */
156 AST_LIST_ENTRY(mansession) list;
159 #define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
161 static AST_LIST_HEAD_STATIC(sessions, mansession);
163 /*! \brief user descriptor, as read from the config file.
165 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
166 * lines which are not supported here, and readperm/writeperm/writetimeout
169 struct ast_manager_user {
172 struct ast_ha *ha; /*!< ACL setting */
173 int readperm; /*! Authorization for reading */
174 int writeperm; /*! Authorization for writing */
175 int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
176 int displayconnects; /*!< XXX unused */
177 int keep; /*!< mark entries created on a reload */
178 AST_RWLIST_ENTRY(ast_manager_user) list;
181 /*! \brief list of users found in the config file */
182 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
184 /*! \brief list of actions registered */
185 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
187 /*! \brief list of hooks registered */
188 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
190 /*! \brief Add a custom hook to be called when an event is fired */
191 void ast_manager_register_hook(struct manager_custom_hook *hook)
193 AST_RWLIST_WRLOCK(&manager_hooks);
194 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
195 AST_RWLIST_UNLOCK(&manager_hooks);
199 /*! \brief Delete a custom hook to be called when an event is fired */
200 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
202 AST_RWLIST_WRLOCK(&manager_hooks);
203 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
204 AST_RWLIST_UNLOCK(&manager_hooks);
209 * Event list management functions.
210 * We assume that the event list always has at least one element,
211 * and the delete code will not remove the last entry even if the
215 static time_t __deb(time_t start, const char *msg)
217 time_t now = time(NULL);
218 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
219 if (start != 0 && now - start > 5)
220 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
224 static void LOCK_EVENTS(void)
226 time_t start = __deb(0, "about to lock events");
227 AST_LIST_LOCK(&all_events);
228 __deb(start, "done lock events");
231 static void UNLOCK_EVENTS(void)
233 __deb(0, "about to unlock events");
234 AST_LIST_UNLOCK(&all_events);
237 static void LOCK_SESS(void)
239 time_t start = __deb(0, "about to lock sessions");
240 AST_LIST_LOCK(&sessions);
241 __deb(start, "done lock sessions");
244 static void UNLOCK_SESS(void)
246 __deb(0, "about to unlock sessions");
247 AST_LIST_UNLOCK(&sessions);
251 int check_manager_enabled()
253 return manager_enabled;
256 int check_webmanager_enabled()
258 return (webmanager_enabled && manager_enabled);
262 * Grab a reference to the last event, update usecount as needed.
263 * Can handle a NULL pointer.
265 static struct eventqent *grab_last(void)
267 struct eventqent *ret;
269 AST_LIST_LOCK(&all_events);
270 ret = AST_LIST_LAST(&all_events);
271 /* the list is never empty now, but may become so when
272 * we optimize it in the future, so be prepared.
275 ast_atomic_fetchadd_int(&ret->usecount, 1);
276 AST_LIST_UNLOCK(&all_events);
281 * Purge unused events. Remove elements from the head
282 * as long as their usecount is 0 and there is a next element.
284 static void purge_events(void)
286 struct eventqent *ev;
288 AST_LIST_LOCK(&all_events);
289 while ( (ev = AST_LIST_FIRST(&all_events)) &&
290 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
291 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
294 AST_LIST_UNLOCK(&all_events);
298 * helper functions to convert back and forth between
299 * string and numeric representation of set of flags
301 static struct permalias {
305 { EVENT_FLAG_SYSTEM, "system" },
306 { EVENT_FLAG_CALL, "call" },
307 { EVENT_FLAG_LOG, "log" },
308 { EVENT_FLAG_VERBOSE, "verbose" },
309 { EVENT_FLAG_COMMAND, "command" },
310 { EVENT_FLAG_AGENT, "agent" },
311 { EVENT_FLAG_USER, "user" },
312 { EVENT_FLAG_CONFIG, "config" },
313 { EVENT_FLAG_DTMF, "dtmf" },
314 { EVENT_FLAG_REPORTING, "reporting" },
315 { EVENT_FLAG_CDR, "cdr" },
316 { EVENT_FLAG_DIALPLAN, "dialplan" },
321 /*! \brief Convert authority code to a list of options */
322 static char *authority_to_str(int authority, struct ast_str **res)
328 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
329 if (authority & perms[i].num) {
330 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
335 if ((*res)->used == 0) /* replace empty string with something sensible */
336 ast_str_append(res, 0, "<none>");
341 /*! Tells you if smallstr exists inside bigstr
342 which is delim by delim and uses no buf or stringsep
343 ast_instring("this|that|more","this",'|') == 1;
345 feel free to move this to app.c -anthm */
346 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
348 const char *val = bigstr, *next;
351 if ((next = strchr(val, delim))) {
352 if (!strncmp(val, smallstr, (next - val)))
357 return !strcmp(smallstr, val);
358 } while (*(val = (next + 1)));
363 static int get_perm(const char *instr)
370 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
371 if (ast_instring(instr, perms[x].label, ','))
379 * A number returns itself, false returns 0, true returns all flags,
380 * other strings return the flags that are set.
382 static int strings_to_mask(const char *string)
386 if (ast_strlen_zero(string))
389 for (p = string; *p; p++)
390 if (*p < '0' || *p > '9')
392 if (!p) /* all digits */
394 if (ast_false(string))
396 if (ast_true(string)) { /* all permissions */
398 for (x = 0; x<sizeof(perms) / sizeof(perms[0]); x++)
402 return get_perm(string);
405 static int check_manager_session_inuse(const char *name)
407 struct mansession *session = NULL;
409 AST_LIST_LOCK(&sessions);
410 AST_LIST_TRAVERSE(&sessions, session, list) {
411 if (!strcasecmp(session->username, name))
414 AST_LIST_UNLOCK(&sessions);
416 return session ? 1 : 0;
421 * lookup an entry in the list of registered users.
422 * must be called with the list lock held.
424 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
426 struct ast_manager_user *user = NULL;
428 AST_RWLIST_TRAVERSE(&users, user, list)
429 if (!strcasecmp(user->username, name))
434 /*! \brief Get displayconnects config option.
435 * \param s manager session to get parameter from.
436 * \return displayconnects config option value.
438 static int manager_displayconnects (struct mansession *s)
440 struct ast_manager_user *user = NULL;
443 AST_RWLIST_RDLOCK(&users);
444 if ((user = get_manager_by_name_locked (s->username)))
445 ret = user->displayconnects;
446 AST_RWLIST_UNLOCK(&users);
451 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
453 struct manager_action *cur;
454 struct ast_str *authority;
459 e->command = "manager show command";
461 "Usage: manager show command <actionname>\n"
462 " Shows the detailed description for a specific Asterisk manager interface command.\n";
467 AST_RWLIST_RDLOCK(&actions);
468 AST_RWLIST_TRAVERSE(&actions, cur, list) {
469 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
470 ret = ast_strdup(cur->action);
471 break; /* make sure we exit even if ast_strdup() returns NULL */
474 AST_RWLIST_UNLOCK(&actions);
477 authority = ast_str_alloca(80);
479 return CLI_SHOWUSAGE;
481 AST_RWLIST_RDLOCK(&actions);
482 AST_RWLIST_TRAVERSE(&actions, cur, list) {
483 for (num = 3; num < a->argc; num++) {
484 if (!strcasecmp(cur->action, a->argv[num])) {
485 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
486 cur->action, cur->synopsis,
487 authority_to_str(cur->authority, &authority),
488 S_OR(cur->description, ""));
492 AST_RWLIST_UNLOCK(&actions);
497 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
501 e->command = "manager debug [on|off]";
502 e->usage = "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
508 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
509 else if (a->argc == 3) {
510 if (!strcasecmp(a->argv[2], "on"))
512 else if (!strcasecmp(a->argv[2], "off"))
515 return CLI_SHOWUSAGE;
520 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
522 struct ast_manager_user *user = NULL;
525 struct ast_str *rauthority = ast_str_alloca(80);
526 struct ast_str *wauthority = ast_str_alloca(80);
530 e->command = "manager show user";
532 " Usage: manager show user <user>\n"
533 " Display all information related to the manager user specified.\n";
540 AST_RWLIST_RDLOCK(&users);
541 AST_RWLIST_TRAVERSE(&users, user, list) {
542 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
543 ret = ast_strdup(user->username);
547 AST_RWLIST_UNLOCK(&users);
552 return CLI_SHOWUSAGE;
554 AST_RWLIST_RDLOCK(&users);
556 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
557 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
558 AST_RWLIST_UNLOCK(&users);
562 ast_cli(a->fd, "\n");
569 "displayconnects: %s\n",
570 (user->username ? user->username : "(N/A)"),
571 (user->secret ? "<Set>" : "(N/A)"),
572 (user->ha ? "yes" : "no"),
573 authority_to_str(user->readperm, &rauthority),
574 authority_to_str(user->writeperm, &wauthority),
575 (user->displayconnects ? "yes" : "no"));
577 AST_RWLIST_UNLOCK(&users);
583 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
585 struct ast_manager_user *user = NULL;
589 e->command = "manager show users";
591 "Usage: manager show users\n"
592 " Prints a listing of all managers that are currently configured on that\n"
599 return CLI_SHOWUSAGE;
601 AST_RWLIST_RDLOCK(&users);
603 /* If there are no users, print out something along those lines */
604 if (AST_RWLIST_EMPTY(&users)) {
605 ast_cli(a->fd, "There are no manager users.\n");
606 AST_RWLIST_UNLOCK(&users);
610 ast_cli(a->fd, "\nusername\n--------\n");
612 AST_RWLIST_TRAVERSE(&users, user, list) {
613 ast_cli(a->fd, "%s\n", user->username);
617 AST_RWLIST_UNLOCK(&users);
619 ast_cli(a->fd, "-------------------\n");
620 ast_cli(a->fd, "%d manager users configured.\n", count_amu);
626 /*! \brief CLI command manager list commands */
627 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
629 struct manager_action *cur;
630 struct ast_str *authority;
631 static const char *format = " %-15.15s %-15.15s %-55.55s\n";
634 e->command = "manager show commands";
636 "Usage: manager show commands\n"
637 " Prints a listing of all the available Asterisk manager interface commands.\n";
642 authority = ast_str_alloca(80);
643 ast_cli(a->fd, format, "Action", "Privilege", "Synopsis");
644 ast_cli(a->fd, format, "------", "---------", "--------");
646 AST_RWLIST_RDLOCK(&actions);
647 AST_RWLIST_TRAVERSE(&actions, cur, list)
648 ast_cli(a->fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
649 AST_RWLIST_UNLOCK(&actions);
654 /*! \brief CLI command manager list connected */
655 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
657 struct mansession *s;
658 time_t now = time(NULL);
659 static const char *format = " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n";
660 static const char *format2 = " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n";
664 e->command = "manager show connected";
666 "Usage: manager show connected\n"
667 " Prints a listing of the users that are currently connected to the\n"
668 "Asterisk manager interface.\n";
674 ast_cli(a->fd, format, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
676 AST_LIST_LOCK(&sessions);
677 AST_LIST_TRAVERSE(&sessions, s, list) {
678 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);
681 AST_LIST_UNLOCK(&sessions);
683 ast_cli(a->fd, "%d users connected.\n", count);
688 /*! \brief CLI command manager list eventq */
689 /* Should change to "manager show connected" */
690 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
695 e->command = "manager show eventq";
697 "Usage: manager show eventq\n"
698 " Prints a listing of all events pending in the Asterisk manger\n"
704 AST_LIST_LOCK(&all_events);
705 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
706 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
707 ast_cli(a->fd, "Category: %d\n", s->category);
708 ast_cli(a->fd, "Event:\n%s", s->eventdata);
710 AST_LIST_UNLOCK(&all_events);
715 /*! \brief CLI command manager reload */
716 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
720 e->command = "manager reload";
722 "Usage: manager reload\n"
723 " Reloads the manager configuration.\n";
729 return CLI_SHOWUSAGE;
735 static struct ast_cli_entry cli_manager[] = {
736 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
737 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
738 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
739 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
740 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
741 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
742 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
743 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
747 * Decrement the usecount for the event; if it goes to zero,
748 * (why check for e->next ?) wakeup the
749 * main thread, which is in charge of freeing the record.
750 * Returns the next record.
752 static struct eventqent *unref_event(struct eventqent *e)
754 ast_atomic_fetchadd_int(&e->usecount, -1);
755 return AST_LIST_NEXT(e, eq_next);
758 static void ref_event(struct eventqent *e)
760 ast_atomic_fetchadd_int(&e->usecount, 1);
764 * destroy a session, leaving the usecount
766 static void free_session(struct mansession *s)
768 struct eventqent *eqe = s->last_ev;
771 ast_mutex_destroy(&s->__lock);
776 static void destroy_session(struct mansession *s)
778 AST_LIST_LOCK(&sessions);
779 AST_LIST_REMOVE(&sessions, s, list);
780 ast_atomic_fetchadd_int(&num_sessions, -1);
782 AST_LIST_UNLOCK(&sessions);
785 const char *astman_get_header(const struct message *m, char *var)
787 int x, l = strlen(var);
789 for (x = 0; x < m->hdrcount; x++) {
790 const char *h = m->headers[x];
791 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
798 struct ast_variable *astman_get_variables(const struct message *m)
801 struct ast_variable *head = NULL, *cur;
803 AST_DECLARE_APP_ARGS(args,
804 AST_APP_ARG(vars)[32];
807 varlen = strlen("Variable: ");
809 for (x = 0; x < m->hdrcount; x++) {
810 char *parse, *var, *val;
812 if (strncasecmp("Variable: ", m->headers[x], varlen))
814 parse = ast_strdupa(m->headers[x] + varlen);
816 AST_STANDARD_APP_ARGS(args, parse);
819 for (y = 0; y < args.argc; y++) {
822 var = val = ast_strdupa(args.vars[y]);
824 if (!val || ast_strlen_zero(var))
826 cur = ast_variable_new(var, val, "");
836 * helper function to send a string to the socket.
837 * Return -1 on error (e.g. buffer full).
839 static int send_string(struct mansession *s, char *string)
841 int len = strlen(string); /* residual length */
843 struct timeval start = ast_tvnow();
849 n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
850 if (n == len /* ok */ || n < 0 /* error */)
852 len -= n; /* skip already written data */
856 n = -1; /* error marker */
857 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
858 if (elapsed > s->writetimeout)
860 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
864 return n < 0 ? -1 : 0;
868 * \brief thread local buffer for astman_append
870 * \note This can not be defined within the astman_append() function
871 * because it declares a couple of functions that get used to
872 * initialize the thread local storage key.
874 AST_THREADSTORAGE(astman_append_buf);
875 /*! \brief initial allocated size for the astman_append_buf */
876 #define ASTMAN_APPEND_BUF_INITSIZE 256
879 * utility functions for creating AMI replies
881 void astman_append(struct mansession *s, const char *fmt, ...)
886 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
890 ast_str_set_va(&buf, 0, fmt, ap);
894 send_string(s, buf->str);
896 ast_verbose("fd == -1 in astman_append, should not happen\n");
899 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
900 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
901 hold the session lock _or_ be running in an action callback (in which case s->busy will
902 be non-zero). In either of these cases, there is no need to lock-protect the session's
903 fd, since no other output will be sent (events will be queued), and no input will
904 be read until either the current action finishes or get_input() obtains the session
908 /*! \brief send a response with an optional message,
909 * and terminate it with an empty line.
910 * m is used only to grab the 'ActionID' field.
912 * Use the explicit constant MSG_MOREDATA to remove the empty line.
913 * XXX MSG_MOREDATA should go to a header file.
915 #define MSG_MOREDATA ((char *)astman_send_response)
916 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
918 const char *id = astman_get_header(m, "ActionID");
920 astman_append(s, "Response: %s\r\n", resp);
921 if (!ast_strlen_zero(id))
922 astman_append(s, "ActionID: %s\r\n", id);
924 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
925 if (msg == MSG_MOREDATA)
928 astman_append(s, "Message: %s\r\n\r\n", msg);
930 astman_append(s, "\r\n");
933 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
935 astman_send_response_full(s, m, resp, msg, NULL);
938 void astman_send_error(struct mansession *s, const struct message *m, char *error)
940 astman_send_response_full(s, m, "Error", error, NULL);
943 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
945 astman_send_response_full(s, m, "Success", msg, NULL);
948 static void astman_start_ack(struct mansession *s, const struct message *m)
950 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
953 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
955 astman_send_response_full(s, m, "Success", msg, listflag);
960 Rather than braindead on,off this now can also accept a specific int mask value
961 or a ',' delim list of mask strings (the same as manager.conf) -anthm
963 static int set_eventmask(struct mansession *s, const char *eventmask)
965 int maskint = strings_to_mask(eventmask);
967 ast_mutex_lock(&s->__lock);
969 s->send_events = maskint;
970 ast_mutex_unlock(&s->__lock);
976 * Here we start with action_ handlers for AMI actions,
977 * and the internal functions used by them.
978 * Generally, the handlers are called action_foo()
981 /* helper function for action_login() */
982 static int authenticate(struct mansession *s, const struct message *m)
984 const char *username = astman_get_header(m, "Username");
985 const char *password = astman_get_header(m, "Secret");
987 struct ast_manager_user *user = NULL;
989 if (ast_strlen_zero(username)) /* missing username */
992 /* locate user in locked state */
993 AST_RWLIST_WRLOCK(&users);
995 if (!(user = get_manager_by_name_locked(username))) {
996 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
997 } else if (user->ha && !ast_apply_ha(user->ha, &(s->sin))) {
998 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
999 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
1000 const char *key = astman_get_header(m, "Key");
1001 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) && user->secret) {
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 *) user->secret, strlen(user->secret));
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");
1299 if (!ast_strlen_zero(id))
1300 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1304 if (!ast_strlen_zero(timeouts)) {
1305 sscanf(timeouts, "%i", &timeout);
1308 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1311 ast_mutex_lock(&s->__lock);
1312 if (s->waiting_thread != AST_PTHREADT_NULL)
1313 pthread_kill(s->waiting_thread, SIGURG);
1315 if (s->managerid) { /* AMI-over-HTTP session */
1317 * Make sure the timeout is within the expire time of the session,
1318 * as the client will likely abort the request if it does not see
1319 * data coming after some amount of time.
1321 time_t now = time(NULL);
1322 int max = s->sessiontimeout - now - 10;
1324 if (max < 0) /* We are already late. Strange but possible. */
1326 if (timeout < 0 || timeout > max)
1328 if (!s->send_events) /* make sure we record events */
1329 s->send_events = -1;
1331 ast_mutex_unlock(&s->__lock);
1333 /* XXX should this go inside the lock ? */
1334 s->waiting_thread = pthread_self(); /* let new events wake up this thread */
1335 ast_debug(1, "Starting waiting for an event!\n");
1337 for (x = 0; x < timeout || timeout < 0; x++) {
1338 ast_mutex_lock(&s->__lock);
1341 /* We can have multiple HTTP session point to the same mansession entry.
1342 * The way we deal with it is not very nice: newcomers kick out the previous
1343 * HTTP session. XXX this needs to be improved.
1345 if (s->waiting_thread != pthread_self())
1349 ast_mutex_unlock(&s->__lock);
1352 if (s->managerid == 0) { /* AMI session */
1353 if (ast_wait_for_input(s->fd, 1000))
1355 } else { /* HTTP session */
1359 ast_debug(1, "Finished waiting for an event!\n");
1360 ast_mutex_lock(&s->__lock);
1361 if (s->waiting_thread == pthread_self()) {
1362 struct eventqent *eqe;
1363 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1364 while ( (eqe = NEW_EVENT(s)) ) {
1366 if (((s->readperm & eqe->category) == eqe->category) &&
1367 ((s->send_events & eqe->category) == eqe->category)) {
1368 astman_append(s, "%s", eqe->eventdata);
1370 s->last_ev = unref_event(s->last_ev);
1373 "Event: WaitEventComplete\r\n"
1376 s->waiting_thread = AST_PTHREADT_NULL;
1378 ast_debug(1, "Abandoning event request!\n");
1380 ast_mutex_unlock(&s->__lock);
1384 static char mandescr_listcommands[] =
1385 "Description: Returns the action name and synopsis for every\n"
1386 " action that is available to the user\n"
1387 "Variables: NONE\n";
1389 /*! \note The actionlock is read-locked by the caller of this function */
1390 static int action_listcommands(struct mansession *s, const struct message *m)
1392 struct manager_action *cur;
1393 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1395 astman_start_ack(s, m);
1396 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1397 if (s->writeperm & cur->authority || cur->authority == 0)
1398 astman_append(s, "%s: %s (Priv: %s)\r\n",
1399 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1401 astman_append(s, "\r\n");
1406 static char mandescr_events[] =
1407 "Description: Enable/Disable sending of events to this manager\n"
1410 " EventMask: 'on' if all events should be sent,\n"
1411 " 'off' if no events should be sent,\n"
1412 " 'system,call,log' to select which flags events should have to be sent.\n";
1414 static int action_events(struct mansession *s, const struct message *m)
1416 const char *mask = astman_get_header(m, "EventMask");
1419 res = set_eventmask(s, mask);
1421 astman_send_response(s, m, "Success", "Events: On\r\n");
1423 astman_send_response(s, m, "Success", "Events: Off\r\n");
1428 static char mandescr_logoff[] =
1429 "Description: Logoff this manager session\n"
1430 "Variables: NONE\n";
1432 static int action_logoff(struct mansession *s, const struct message *m)
1434 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1438 static int action_login(struct mansession *s, const struct message *m)
1440 if (authenticate(s, m)) {
1442 astman_send_error(s, m, "Authentication failed");
1445 s->authenticated = 1;
1446 if (manager_displayconnects(s))
1447 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1448 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1449 astman_send_ack(s, m, "Authentication accepted");
1453 static int action_challenge(struct mansession *s, const struct message *m)
1455 const char *authtype = astman_get_header(m, "AuthType");
1457 if (!strcasecmp(authtype, "MD5")) {
1458 if (ast_strlen_zero(s->challenge))
1459 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1460 ast_mutex_lock(&s->__lock);
1461 astman_start_ack(s, m);
1462 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1463 ast_mutex_unlock(&s->__lock);
1465 astman_send_error(s, m, "Must specify AuthType");
1470 static char mandescr_hangup[] =
1471 "Description: Hangup a channel\n"
1473 " Channel: The channel name to be hungup\n";
1475 static int action_hangup(struct mansession *s, const struct message *m)
1477 struct ast_channel *c = NULL;
1478 const char *name = astman_get_header(m, "Channel");
1479 if (ast_strlen_zero(name)) {
1480 astman_send_error(s, m, "No channel specified");
1483 c = ast_get_channel_by_name_locked(name);
1485 astman_send_error(s, m, "No such channel");
1488 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1489 ast_channel_unlock(c);
1490 astman_send_ack(s, m, "Channel Hungup");
1494 static char mandescr_setvar[] =
1495 "Description: Set a global or local channel variable.\n"
1496 "Variables: (Names marked with * are required)\n"
1497 " Channel: Channel to set variable for\n"
1498 " *Variable: Variable name\n"
1501 static int action_setvar(struct mansession *s, const struct message *m)
1503 struct ast_channel *c = NULL;
1504 const char *name = astman_get_header(m, "Channel");
1505 const char *varname = astman_get_header(m, "Variable");
1506 const char *varval = astman_get_header(m, "Value");
1508 if (ast_strlen_zero(varname)) {
1509 astman_send_error(s, m, "No variable specified");
1513 if (!ast_strlen_zero(name)) {
1514 c = ast_get_channel_by_name_locked(name);
1516 astman_send_error(s, m, "No such channel");
1521 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1524 ast_channel_unlock(c);
1526 astman_send_ack(s, m, "Variable Set");
1531 static char mandescr_getvar[] =
1532 "Description: Get the value of a global or local channel variable.\n"
1533 "Variables: (Names marked with * are required)\n"
1534 " Channel: Channel to read variable from\n"
1535 " *Variable: Variable name\n"
1536 " ActionID: Optional Action id for message matching.\n";
1538 static int action_getvar(struct mansession *s, const struct message *m)
1540 struct ast_channel *c = NULL;
1541 const char *name = astman_get_header(m, "Channel");
1542 const char *varname = astman_get_header(m, "Variable");
1544 char workspace[1024] = "";
1546 if (ast_strlen_zero(varname)) {
1547 astman_send_error(s, m, "No variable specified");
1551 if (!ast_strlen_zero(name)) {
1552 c = ast_get_channel_by_name_locked(name);
1554 astman_send_error(s, m, "No such channel");
1559 if (varname[strlen(varname) - 1] == ')') {
1560 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1563 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1567 ast_channel_unlock(c);
1568 astman_start_ack(s, m);
1569 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1575 /*! \brief Manager "status" command to show channels */
1576 /* Needs documentation... */
1577 static int action_status(struct mansession *s, const struct message *m)
1579 const char *name = astman_get_header(m, "Channel");
1580 struct ast_channel *c;
1582 struct timeval now = ast_tvnow();
1583 long elapsed_seconds = 0;
1585 int all = ast_strlen_zero(name); /* set if we want all channels */
1586 const char *id = astman_get_header(m, "ActionID");
1589 if (!ast_strlen_zero(id))
1590 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1595 c = ast_channel_walk_locked(NULL);
1597 c = ast_get_channel_by_name_locked(name);
1599 astman_send_error(s, m, "No such channel");
1603 astman_send_ack(s, m, "Channel status will follow");
1605 /* if we look by name, we break after the first iteration */
1609 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1614 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1618 "Privilege: Call\r\n"
1620 "CallerIDNum: %s\r\n"
1621 "CallerIDName: %s\r\n"
1622 "Accountcode: %s\r\n"
1623 "ChannelState: %d\r\n"
1624 "ChannelStateDesc: %s\r\n"
1634 S_OR(c->cid.cid_num, ""),
1635 S_OR(c->cid.cid_name, ""),
1638 ast_state2str(c->_state), c->context,
1639 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1643 "Privilege: Call\r\n"
1645 "CallerIDNum: %s\r\n"
1646 "CallerIDName: %s\r\n"
1654 S_OR(c->cid.cid_num, "<unknown>"),
1655 S_OR(c->cid.cid_name, "<unknown>"),
1657 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1659 ast_channel_unlock(c);
1662 c = ast_channel_walk_locked(c);
1665 "Event: StatusComplete\r\n"
1668 "\r\n", idText, channels);
1672 static char mandescr_sendtext[] =
1673 "Description: Sends A Text Message while in a call.\n"
1674 "Variables: (Names marked with * are required)\n"
1675 " *Channel: Channel to send message to\n"
1676 " *Message: Message to send\n"
1677 " ActionID: Optional Action id for message matching.\n";
1679 static int action_sendtext(struct mansession *s, const struct message *m)
1681 struct ast_channel *c = NULL;
1682 const char *name = astman_get_header(m, "Channel");
1683 const char *textmsg = astman_get_header(m, "Message");
1686 if (ast_strlen_zero(name)) {
1687 astman_send_error(s, m, "No channel specified");
1691 if (ast_strlen_zero(textmsg)) {
1692 astman_send_error(s, m, "No Message specified");
1696 c = ast_get_channel_by_name_locked(name);
1698 astman_send_error(s, m, "No such channel");
1702 res = ast_sendtext(c, textmsg);
1703 ast_channel_unlock(c);
1706 astman_send_ack(s, m, "Success");
1708 astman_send_error(s, m, "Failure");
1713 static char mandescr_redirect[] =
1714 "Description: Redirect (transfer) a call.\n"
1715 "Variables: (Names marked with * are required)\n"
1716 " *Channel: Channel to redirect\n"
1717 " ExtraChannel: Second call leg to transfer (optional)\n"
1718 " *Exten: Extension to transfer to\n"
1719 " *Context: Context to transfer to\n"
1720 " *Priority: Priority to transfer to\n"
1721 " ActionID: Optional Action id for message matching.\n";
1723 /*! \brief action_redirect: The redirect manager command */
1724 static int action_redirect(struct mansession *s, const struct message *m)
1726 const char *name = astman_get_header(m, "Channel");
1727 const char *name2 = astman_get_header(m, "ExtraChannel");
1728 const char *exten = astman_get_header(m, "Exten");
1729 const char *context = astman_get_header(m, "Context");
1730 const char *priority = astman_get_header(m, "Priority");
1731 struct ast_channel *chan, *chan2 = NULL;
1735 if (ast_strlen_zero(name)) {
1736 astman_send_error(s, m, "Channel not specified");
1739 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1740 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1741 astman_send_error(s, m, "Invalid priority\n");
1745 /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1746 chan = ast_get_channel_by_name_locked(name);
1749 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1750 astman_send_error(s, m, buf);
1753 if (ast_check_hangup(chan)) {
1754 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1755 ast_channel_unlock(chan);
1758 if (!ast_strlen_zero(name2))
1759 chan2 = ast_get_channel_by_name_locked(name2);
1760 if (chan2 && ast_check_hangup(chan2)) {
1761 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1762 ast_channel_unlock(chan);
1763 ast_channel_unlock(chan2);
1766 res = ast_async_goto(chan, context, exten, pi);
1768 if (!ast_strlen_zero(name2)) {
1770 res = ast_async_goto(chan2, context, exten, pi);
1774 astman_send_ack(s, m, "Dual Redirect successful");
1776 astman_send_error(s, m, "Secondary redirect failed");
1778 astman_send_ack(s, m, "Redirect successful");
1780 astman_send_error(s, m, "Redirect failed");
1782 ast_channel_unlock(chan);
1784 ast_channel_unlock(chan2);
1788 static char mandescr_command[] =
1789 "Description: Run a CLI command.\n"
1790 "Variables: (Names marked with * are required)\n"
1791 " *Command: Asterisk CLI command to run\n"
1792 " ActionID: Optional Action id for message matching.\n";
1794 /*! \brief Manager command "command" - execute CLI command */
1795 static int action_command(struct mansession *s, const struct message *m)
1797 const char *cmd = astman_get_header(m, "Command");
1798 const char *id = astman_get_header(m, "ActionID");
1799 char *buf, *final_buf;
1800 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1801 int fd = mkstemp(template), i = 0;
1804 for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
1805 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
1806 astman_send_error(s, m, "Command blacklisted");
1811 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1812 if (!ast_strlen_zero(id))
1813 astman_append(s, "ActionID: %s\r\n", id);
1814 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1815 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
1816 l = lseek(fd, 0, SEEK_END); /* how many chars available */
1818 /* This has a potential to overflow the stack. Hence, use the heap. */
1819 buf = ast_calloc(1, l + 1);
1820 final_buf = ast_calloc(1, l + 1);
1822 lseek(fd, 0, SEEK_SET);
1826 term_strip(final_buf, buf, l);
1827 final_buf[l] = '\0';
1829 astman_append(s, S_OR(final_buf, buf));
1834 astman_append(s, "--END COMMAND--\r\n\r\n");
1836 ast_free(final_buf);
1840 /* helper function for originate */
1841 struct fast_originate_helper {
1842 char tech[AST_MAX_EXTENSION];
1843 char data[AST_MAX_EXTENSION];
1845 char app[AST_MAX_APP];
1846 char appdata[AST_MAX_EXTENSION];
1847 char cid_name[AST_MAX_EXTENSION];
1848 char cid_num[AST_MAX_EXTENSION];
1849 char context[AST_MAX_CONTEXT];
1850 char exten[AST_MAX_EXTENSION];
1851 char idtext[AST_MAX_EXTENSION];
1852 char account[AST_MAX_ACCOUNT_CODE];
1854 struct ast_variable *vars;
1857 static void *fast_originate(void *data)
1859 struct fast_originate_helper *in = data;
1862 struct ast_channel *chan = NULL;
1863 char requested_channel[AST_CHANNEL_NAME];
1865 if (!ast_strlen_zero(in->app)) {
1866 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1867 S_OR(in->cid_num, NULL),
1868 S_OR(in->cid_name, NULL),
1869 in->vars, in->account, &chan);
1871 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1872 S_OR(in->cid_num, NULL),
1873 S_OR(in->cid_name, NULL),
1874 in->vars, in->account, &chan);
1878 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
1879 /* Tell the manager what happened with the channel */
1880 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1888 "CallerIDNum: %s\r\n"
1889 "CallerIDName: %s\r\n",
1890 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
1891 chan ? chan->uniqueid : "<null>",
1892 S_OR(in->cid_num, "<unknown>"),
1893 S_OR(in->cid_name, "<unknown>")
1896 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1898 ast_channel_unlock(chan);
1903 static char mandescr_originate[] =
1904 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1905 " Application/Data\n"
1906 "Variables: (Names marked with * are required)\n"
1907 " *Channel: Channel name to call\n"
1908 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1909 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1910 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1911 " Application: Application to use\n"
1912 " Data: Data to use (requires 'Application')\n"
1913 " Timeout: How long to wait for call to be answered (in ms)\n"
1914 " CallerID: Caller ID to be set on the outgoing channel\n"
1915 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1916 " Account: Account code\n"
1917 " Async: Set to 'true' for fast origination\n";
1919 static int action_originate(struct mansession *s, const struct message *m)
1921 const char *name = astman_get_header(m, "Channel");
1922 const char *exten = astman_get_header(m, "Exten");
1923 const char *context = astman_get_header(m, "Context");
1924 const char *priority = astman_get_header(m, "Priority");
1925 const char *timeout = astman_get_header(m, "Timeout");
1926 const char *callerid = astman_get_header(m, "CallerID");
1927 const char *account = astman_get_header(m, "Account");
1928 const char *app = astman_get_header(m, "Application");
1929 const char *appdata = astman_get_header(m, "Data");
1930 const char *async = astman_get_header(m, "Async");
1931 const char *id = astman_get_header(m, "ActionID");
1932 struct ast_variable *vars = astman_get_variables(m);
1934 char *l = NULL, *n = NULL;
1944 astman_send_error(s, m, "Channel not specified");
1947 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1948 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1949 astman_send_error(s, m, "Invalid priority\n");
1953 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1954 astman_send_error(s, m, "Invalid timeout\n");
1957 ast_copy_string(tmp, name, sizeof(tmp));
1959 data = strchr(tmp, '/');
1961 astman_send_error(s, m, "Invalid channel\n");
1965 ast_copy_string(tmp2, callerid, sizeof(tmp2));
1966 ast_callerid_parse(tmp2, &n, &l);
1968 if (ast_strlen_zero(n))
1972 ast_shrink_phone_number(l);
1973 if (ast_strlen_zero(l))
1976 if (ast_true(async)) {
1977 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1981 if (!ast_strlen_zero(id))
1982 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1983 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1984 ast_copy_string(fast->data, data, sizeof(fast->data));
1985 ast_copy_string(fast->app, app, sizeof(fast->app));
1986 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1988 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1990 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1992 ast_copy_string(fast->context, context, sizeof(fast->context));
1993 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1994 ast_copy_string(fast->account, account, sizeof(fast->account));
1996 fast->priority = pi;
1997 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2003 } else if (!ast_strlen_zero(app)) {
2004 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2006 if (exten && context && pi)
2007 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2009 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2014 astman_send_ack(s, m, "Originate successfully queued");
2016 astman_send_error(s, m, "Originate failed");
2020 /*! \brief Help text for manager command mailboxstatus
2022 static char mandescr_mailboxstatus[] =
2023 "Description: Checks a voicemail account for status.\n"
2024 "Variables: (Names marked with * are required)\n"
2025 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2026 " ActionID: Optional ActionID for message matching.\n"
2027 "Returns number of messages.\n"
2028 " Message: Mailbox Status\n"
2029 " Mailbox: <mailboxid>\n"
2030 " Waiting: <count>\n"
2033 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2035 const char *mailbox = astman_get_header(m, "Mailbox");
2038 if (ast_strlen_zero(mailbox)) {
2039 astman_send_error(s, m, "Mailbox not specified");
2042 ret = ast_app_has_voicemail(mailbox, NULL);
2043 astman_start_ack(s, m);
2044 astman_append(s, "Message: Mailbox Status\r\n"
2046 "Waiting: %d\r\n\r\n", mailbox, ret);
2050 static char mandescr_mailboxcount[] =
2051 "Description: Checks a voicemail account for new messages.\n"
2052 "Variables: (Names marked with * are required)\n"
2053 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2054 " ActionID: Optional ActionID for message matching.\n"
2055 "Returns number of new and old messages.\n"
2056 " Message: Mailbox Message Count\n"
2057 " Mailbox: <mailboxid>\n"
2058 " NewMessages: <count>\n"
2059 " OldMessages: <count>\n"
2061 static int action_mailboxcount(struct mansession *s, const struct message *m)
2063 const char *mailbox = astman_get_header(m, "Mailbox");
2064 int newmsgs = 0, oldmsgs = 0;
2066 if (ast_strlen_zero(mailbox)) {
2067 astman_send_error(s, m, "Mailbox not specified");
2070 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2071 astman_start_ack(s, m);
2072 astman_append(s, "Message: Mailbox Message Count\r\n"
2074 "NewMessages: %d\r\n"
2075 "OldMessages: %d\r\n"
2077 mailbox, newmsgs, oldmsgs);
2081 static char mandescr_extensionstate[] =
2082 "Description: Report the extension state for given extension.\n"
2083 " If the extension has a hint, will use devicestate to check\n"
2084 " the status of the device connected to the extension.\n"
2085 "Variables: (Names marked with * are required)\n"
2086 " *Exten: Extension to check state on\n"
2087 " *Context: Context for extension\n"
2088 " ActionId: Optional ID for this transaction\n"
2089 "Will return an \"Extension Status\" message.\n"
2090 "The response will include the hint for the extension and the status.\n";
2092 static int action_extensionstate(struct mansession *s, const struct message *m)
2094 const char *exten = astman_get_header(m, "Exten");
2095 const char *context = astman_get_header(m, "Context");
2096 char hint[256] = "";
2098 if (ast_strlen_zero(exten)) {
2099 astman_send_error(s, m, "Extension not specified");
2102 if (ast_strlen_zero(context))
2103 context = "default";
2104 status = ast_extension_state(NULL, context, exten);
2105 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2106 astman_start_ack(s, m);
2107 astman_append(s, "Message: Extension Status\r\n"
2111 "Status: %d\r\n\r\n",
2112 exten, context, hint, status);
2116 static char mandescr_timeout[] =
2117 "Description: Hangup a channel after a certain time.\n"
2118 "Variables: (Names marked with * are required)\n"
2119 " *Channel: Channel name to hangup\n"
2120 " *Timeout: Maximum duration of the call (sec)\n"
2121 "Acknowledges set time with 'Timeout Set' message\n";
2123 static int action_timeout(struct mansession *s, const struct message *m)
2125 struct ast_channel *c;
2126 const char *name = astman_get_header(m, "Channel");
2127 int timeout = atoi(astman_get_header(m, "Timeout"));
2129 if (ast_strlen_zero(name)) {
2130 astman_send_error(s, m, "No channel specified");
2134 astman_send_error(s, m, "No timeout specified");
2137 c = ast_get_channel_by_name_locked(name);
2139 astman_send_error(s, m, "No such channel");
2142 ast_channel_setwhentohangup(c, timeout);
2143 ast_channel_unlock(c);
2144 astman_send_ack(s, m, "Timeout Set");
2149 * Send any applicable events to the client listening on this socket.
2150 * Wait only for a finite time on each event, and drop all events whether
2151 * they are successfully sent or not.
2153 static int process_events(struct mansession *s)
2157 ast_mutex_lock(&s->__lock);
2159 struct eventqent *eqe;
2161 while ( (eqe = NEW_EVENT(s)) ) {
2163 if (!ret && s->authenticated &&
2164 (s->readperm & eqe->category) == eqe->category &&
2165 (s->send_events & eqe->category) == eqe->category) {
2166 if (send_string(s, eqe->eventdata) < 0)
2167 ret = -1; /* don't send more */
2169 s->last_ev = unref_event(s->last_ev);
2172 ast_mutex_unlock(&s->__lock);
2176 static char mandescr_userevent[] =
2177 "Description: Send an event to manager sessions.\n"
2178 "Variables: (Names marked with * are required)\n"
2179 " *UserEvent: EventStringToSend\n"
2180 " Header1: Content1\n"
2181 " HeaderN: ContentN\n";
2183 static int action_userevent(struct mansession *s, const struct message *m)
2185 const char *event = astman_get_header(m, "UserEvent");
2186 char body[2048] = "";
2188 for (x = 0; x < m->hdrcount; x++) {
2189 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2190 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2191 bodylen += strlen(m->headers[x]);
2192 ast_copy_string(body + bodylen, "\r\n", 3);
2197 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2201 static char mandescr_coresettings[] =
2202 "Description: Query for Core PBX settings.\n"
2203 "Variables: (Names marked with * are optional)\n"
2204 " *ActionID: ActionID of this transaction\n";
2206 /*! \brief Show PBX core settings information */
2207 static int action_coresettings(struct mansession *s, const struct message *m)
2209 const char *actionid = astman_get_header(m, "ActionID");
2212 if (!ast_strlen_zero(actionid))
2213 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2217 astman_append(s, "Response: Success\r\n"
2219 "AMIversion: %s\r\n"
2220 "AsteriskVersion: %s\r\n"
2221 "SystemName: %s\r\n"
2222 "CoreMaxCalls: %d\r\n"
2223 "CoreMaxLoadAvg: %f\r\n"
2224 "CoreRunUser: %s\r\n"
2225 "CoreRunGroup: %s\r\n"
2226 "CoreMaxFilehandles: %d\r\n"
2227 "CoreRealTimeEnabled: %s\r\n"
2228 "CoreCDRenabled: %s\r\n"
2229 "CoreHTTPenabled: %s\r\n"
2234 ast_config_AST_SYSTEM_NAME,
2237 ast_config_AST_RUN_USER,
2238 ast_config_AST_RUN_GROUP,
2240 ast_realtime_enabled() ? "Yes" : "No",
2241 check_cdr_enabled() ? "Yes" : "No",
2242 check_webmanager_enabled() ? "Yes" : "No"
2247 static char mandescr_corestatus[] =
2248 "Description: Query for Core PBX status.\n"
2249 "Variables: (Names marked with * are optional)\n"
2250 " *ActionID: ActionID of this transaction\n";
2252 /*! \brief Show PBX core status information */
2253 static int action_corestatus(struct mansession *s, const struct message *m)
2255 const char *actionid = astman_get_header(m, "ActionID");
2257 char startuptime[150];
2258 char reloadtime[150];
2261 if (!ast_strlen_zero(actionid))
2262 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2266 ast_localtime(&ast_startuptime, &tm, NULL);
2267 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2268 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2269 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2271 astman_append(s, "Response: Success\r\n"
2273 "CoreStartupTime: %s\r\n"
2274 "CoreReloadTime: %s\r\n"
2275 "CoreCurrentCalls: %d\r\n"
2280 ast_active_channels()
2285 static char mandescr_reload[] =
2286 "Description: Send a reload event.\n"
2287 "Variables: (Names marked with * are optional)\n"
2288 " *ActionID: ActionID of this transaction\n"
2289 " *Module: Name of the module to reload\n";
2291 /*! \brief Send a reload event */
2292 static int action_reload(struct mansession *s, const struct message *m)
2294 const char *module = astman_get_header(m, "Module");
2295 int res = ast_module_reload(S_OR(module, NULL));
2298 astman_send_ack(s, m, "Module Reloaded");
2300 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2304 static char mandescr_coreshowchannels[] =
2305 "Description: List currently defined channels and some information\n"
2308 " ActionID: Optional Action id for message matching.\n";
2310 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2311 * and some information about them. */
2312 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2314 const char *actionid = astman_get_header(m, "ActionID");
2315 char actionidtext[256];
2316 struct ast_channel *c = NULL;
2318 int duration, durh, durm, durs;
2320 if (!ast_strlen_zero(actionid))
2321 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2323 actionidtext[0] = '\0';
2325 astman_send_listack(s, m, "Channels will follow", "start");
2327 while ((c = ast_channel_walk_locked(c)) != NULL) {
2328 struct ast_channel *bc = ast_bridged_channel(c);
2329 char durbuf[10] = "";
2331 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2332 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2333 durh = duration / 3600;
2334 durm = (duration % 3600) / 60;
2335 durs = duration % 60;
2336 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2345 "ChannelState: %d\r\n"
2346 "ChannelStateDesc: %s\r\n"
2347 "Application: %s\r\n"
2348 "ApplicationData: %s\r\n"
2349 "CallerIDnum: %s\r\n"
2351 "AccountCode: %s\r\n"
2352 "BridgedChannel: %s\r\n"
2353 "BridgedUniqueID: %s\r\n"
2354 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2355 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2356 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2357 ast_channel_unlock(c);
2362 "Event: CoreShowChannelsComplete\r\n"
2363 "EventList: Complete\r\n"
2366 "\r\n", numchans, actionidtext);
2371 static char mandescr_modulecheck[] =
2372 "Description: Checks if Asterisk module is loaded\n"
2374 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2375 " Module: <name> Asterisk module name (not including extension)\n"
2377 "Will return Success/Failure\n"
2378 "For success returns, the module revision number is included.\n";
2380 /* Manager function to check if module is loaded */
2381 static int manager_modulecheck(struct mansession *s, const struct message *m)
2384 const char *module = astman_get_header(m, "Module");
2385 const char *id = astman_get_header(m, "ActionID");
2386 char idText[BUFSIZ];
2387 const char *version;
2388 char filename[BUFSIZ/2];
2391 snprintf(filename, sizeof(filename), module);
2392 if ((cut = strchr(filename, '.'))) {
2395 cut = filename + strlen(filename);
2397 sprintf(cut, ".so");
2398 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2399 res = ast_module_check(filename);
2401 astman_send_error(s, m, "Module not loaded");
2405 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2406 version = ast_file_version_find(filename);
2408 if (!ast_strlen_zero(id))
2409 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2412 astman_append(s, "Response: Success\r\n%s", idText);
2413 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2417 static char mandescr_moduleload[] =
2418 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2420 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2421 " Module: <name> Asterisk module name (including .so extension)\n"
2422 " or subsystem identifier:\n"
2423 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2424 " LoadType: load | unload | reload\n"
2425 " The operation to be done on module\n"
2426 " If no module is specified for a reload loadtype, all modules are reloaded";
2428 static int manager_moduleload(struct mansession *s, const struct message *m)
2431 const char *module = astman_get_header(m, "Module");
2432 const char *loadtype = astman_get_header(m, "LoadType");
2434 if (!loadtype || strlen(loadtype) == 0)
2435 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2436 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2437 astman_send_error(s, m, "Need module name");
2439 if (!strcasecmp(loadtype, "load")) {
2440 res = ast_load_resource(module);
2442 astman_send_error(s, m, "Could not load module.");
2444 astman_send_ack(s, m, "Module loaded.");
2445 } else if (!strcasecmp(loadtype, "unload")) {
2446 res = ast_unload_resource(module, AST_FORCE_SOFT);
2448 astman_send_error(s, m, "Could not unload module.");
2450 astman_send_ack(s, m, "Module unloaded.");
2451 } else if (!strcasecmp(loadtype, "reload")) {
2452 if (module != NULL) {
2453 res = ast_module_reload(module);
2455 astman_send_error(s, m, "No such module.");
2457 astman_send_error(s, m, "Module does not support reload action.");
2459 astman_send_ack(s, m, "Module reloaded.");
2461 ast_module_reload(NULL); /* Reload all modules */
2462 astman_send_ack(s, m, "All modules reloaded");
2465 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2470 * Done with the action handlers here, we start with the code in charge
2471 * of accepting connections and serving them.
2472 * accept_thread() forks a new thread for each connection, session_do(),
2473 * which in turn calls get_input() repeatedly until a full message has
2474 * been accumulated, and then invokes process_message() to pass it to
2475 * the appropriate handler.
2479 * Process an AMI message, performing desired action.
2480 * Return 0 on success, -1 on error that require the session to be destroyed.
2482 static int process_message(struct mansession *s, const struct message *m)
2484 char action[80] = "";
2486 struct manager_action *tmp;
2487 const char *user = astman_get_header(m, "Username");
2489 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2490 ast_debug(1, "Manager received command '%s'\n", action);
2492 if (ast_strlen_zero(action)) {
2493 ast_mutex_lock(&s->__lock);
2494 astman_send_error(s, m, "Missing action in request");
2495 ast_mutex_unlock(&s->__lock);
2499 if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2500 ast_mutex_lock(&s->__lock);
2501 astman_send_error(s, m, "Permission denied");
2502 ast_mutex_unlock(&s->__lock);
2506 if (!allowmultiplelogin && !s->authenticated && user &&
2507 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2508 if (check_manager_session_inuse(user)) {
2510 ast_mutex_lock(&s->__lock);
2511 astman_send_error(s, m, "Login Already In Use");
2512 ast_mutex_unlock(&s->__lock);
2517 AST_RWLIST_RDLOCK(&actions);
2518 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2519 if (strcasecmp(action, tmp->action))
2521 if (s->writeperm & tmp->authority || tmp->authority == 0)
2522 ret = tmp->func(s, m);
2524 astman_send_error(s, m, "Permission denied");
2527 AST_RWLIST_UNLOCK(&actions);
2531 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2532 ast_mutex_lock(&s->__lock);
2533 astman_send_error(s, m, buf);
2534 ast_mutex_unlock(&s->__lock);
2538 /* Once done with our message, deliver any pending events */
2539 return process_events(s);
2543 * Read one full line (including crlf) from the manager socket.
2545 * \r\n is the only valid terminator for the line.
2546 * (Note that, later, '\0' will be considered as the end-of-line marker,
2547 * so everything between the '\0' and the '\r\n' will not be used).
2548 * Also note that we assume output to have at least "maxlen" space.
2551 static int get_input(struct mansession *s, char *output)
2554 int maxlen = sizeof(s->inbuf) - 1;
2555 char *src = s->inbuf;
2558 * Look for \r\n within the buffer. If found, copy to the output
2559 * buffer and return, trimming the \r\n (not used afterwards).
2561 for (x = 0; x < s->inlen; x++) {
2562 int cr; /* set if we have \r */
2563 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2564 cr = 2; /* Found. Update length to include \r\n */
2565 else if (src[x] == '\n')
2566 cr = 1; /* also accept \n only */
2569 memmove(output, src, x); /*... but trim \r\n */
2570 output[x] = '\0'; /* terminate the string */
2571 x += cr; /* number of bytes used */
2572 s->inlen -= x; /* remaining size */
2573 memmove(src, src + x, s->inlen); /* remove used bytes */
2576 if (s->inlen >= maxlen) {
2577 /* no crlf found, and buffer full - sorry, too long for us */
2578 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2583 /* XXX do we really need this locking ? */
2584 ast_mutex_lock(&s->__lock);
2585 s->waiting_thread = pthread_self();
2586 ast_mutex_unlock(&s->__lock);
2588 res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
2590 ast_mutex_lock(&s->__lock);
2591 s->waiting_thread = AST_PTHREADT_NULL;
2592 ast_mutex_unlock(&s->__lock);
2595 /* If we get a signal from some other thread (typically because
2596 * there are new events queued), return 0 to notify the caller.
2600 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2603 ast_mutex_lock(&s->__lock);
2604 res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2606 res = -1; /* error return */
2609 src[s->inlen] = '\0';
2612 ast_mutex_unlock(&s->__lock);
2616 static int do_message(struct mansession *s)
2618 struct message m = { 0 };
2619 char header_buf[sizeof(s->inbuf)] = { '\0' };
2623 /* Check if any events are pending and do them if needed */
2624 if (process_events(s))
2626 res = get_input(s, header_buf);
2629 } else if (res > 0) {
2630 if (ast_strlen_zero(header_buf))
2631 return process_message(s, &m) ? -1 : 0;
2632 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2633 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2640 /*! \brief The body of the individual manager session.
2641 * Call get_input() to read one line at a time
2642 * (or be woken up on new events), collect the lines in a
2643 * message until found an empty line, and execute the request.
2644 * In any case, deliver events asynchronously through process_events()
2645 * (called from here if no line is available, or at the end of
2646 * process_message(). )
2648 static void *session_do(void *data)
2650 struct server_instance *ser = data;
2651 struct mansession *s = ast_calloc(1, sizeof(*s));
2658 s->writetimeout = 100;
2659 s->waiting_thread = AST_PTHREADT_NULL;
2661 flags = fcntl(ser->fd, F_GETFL);
2662 if (!block_sockets) /* make sure socket is non-blocking */
2663 flags |= O_NONBLOCK;
2665 flags &= ~O_NONBLOCK;
2666 fcntl(ser->fd, F_SETFL, flags);
2668 ast_mutex_init(&s->__lock);
2669 s->send_events = -1;
2670 /* these fields duplicate those in the 'ser' structure */
2673 s->sin = ser->requestor;
2675 AST_LIST_LOCK(&sessions);
2676 AST_LIST_INSERT_HEAD(&sessions, s, list);
2677 ast_atomic_fetchadd_int(&num_sessions, 1);
2678 AST_LIST_UNLOCK(&sessions);
2679 /* Hook to the tail of the event queue */
2680 s->last_ev = grab_last();
2682 astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
2684 if ((res = do_message(s)) < 0)
2687 /* session is over, explain why and terminate */
2688 if (s->authenticated) {
2689 if (manager_displayconnects(s))
2690 ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2691 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2693 if (displayconnects)
2694 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2695 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2698 /* It is possible under certain circumstances for this session thread
2699 to complete its work and exit *before* the thread that created it
2700 has finished executing the ast_pthread_create_background() function.
2701 If this occurs, some versions of glibc appear to act in a buggy
2702 fashion and attempt to write data into memory that it thinks belongs
2703 to the thread but is in fact not owned by the thread (or may have
2704 been freed completely).
2706 Causing this thread to yield to other threads at least one time
2707 appears to work around this bug.
2718 /*! \brief remove at most n_max stale session from the list. */
2719 static void purge_sessions(int n_max)
2721 struct mansession *s;
2722 time_t now = time(NULL);
2724 AST_LIST_LOCK(&sessions);
2725 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2726 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2727 AST_LIST_REMOVE_CURRENT(list);
2728 ast_atomic_fetchadd_int(&num_sessions, -1);
2729 if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
2730 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
2731 s->username, ast_inet_ntoa(s->sin.sin_addr));
2733 free_session(s); /* XXX outside ? */
2738 AST_LIST_TRAVERSE_SAFE_END;
2739 AST_LIST_UNLOCK(&sessions);
2743 * events are appended to a queue from where they
2744 * can be dispatched to clients.
2746 static int append_event(const char *str, int category)
2748 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2749 static int seq; /* sequence number */
2754 /* need to init all fields, because ast_malloc() does not */
2756 tmp->category = category;
2757 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2758 AST_LIST_NEXT(tmp, eq_next) = NULL;
2759 strcpy(tmp->eventdata, str);
2761 AST_LIST_LOCK(&all_events);
2762 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
2763 AST_LIST_UNLOCK(&all_events);
2768 /* XXX see if can be moved inside the function */
2769 AST_THREADSTORAGE(manager_event_buf);
2770 #define MANAGER_EVENT_BUF_INITSIZE 256
2772 /*! \brief manager_event: Send AMI event to client */
2773 int __manager_event(int category, const char *event,
2774 const char *file, int line, const char *func, const char *fmt, ...)
2776 struct mansession *s;
2777 struct manager_custom_hook *hook;
2778 struct ast_str *auth = ast_str_alloca(80);
2779 const char *cat_str;
2782 struct ast_str *buf;
2784 /* Abort if there aren't any manager sessions */
2788 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2791 cat_str = authority_to_str(category, &auth);
2792 ast_str_set(&buf, 0,
2793 "Event: %s\r\nPrivilege: %s\r\n",
2796 if (timestampevents) {
2798 ast_str_append(&buf, 0,
2799 "Timestamp: %ld.%06lu\r\n",
2800 now.tv_sec, (unsigned long) now.tv_usec);
2802 if (manager_debug) {
2804 ast_str_append(&buf, 0,
2805 "SequenceNumber: %d\r\n",
2806 ast_atomic_fetchadd_int(&seq, 1));
2807 ast_str_append(&buf, 0,
2808 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
2812 ast_str_append_va(&buf, 0, fmt, ap);
2815 ast_str_append(&buf, 0, "\r\n");
2817 append_event(buf->str, category);
2819 /* Wake up any sleeping sessions */
2820 AST_LIST_LOCK(&sessions);
2821 AST_LIST_TRAVERSE(&sessions, s, list) {
2822 ast_mutex_lock(&s->__lock);
2823 if (s->waiting_thread != AST_PTHREADT_NULL)
2824 pthread_kill(s->waiting_thread, SIGURG);
2825 ast_mutex_unlock(&s->__lock);
2827 AST_LIST_UNLOCK(&sessions);
2829 AST_RWLIST_RDLOCK(&manager_hooks);
2830 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
2831 hook->helper(category, event, buf->str);
2833 AST_RWLIST_UNLOCK(&manager_hooks);
2839 * support functions to register/unregister AMI action handlers,
2841 int ast_manager_unregister(char *action)
2843 struct manager_action *cur;
2845 AST_RWLIST_WRLOCK(&actions);
2846 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
2847 if (!strcasecmp(action, cur->action)) {
2848 AST_RWLIST_REMOVE_CURRENT(list);
2850 ast_verb(2, "Manager unregistered action %s\n", action);
2854 AST_RWLIST_TRAVERSE_SAFE_END;
2855 AST_RWLIST_UNLOCK(&actions);
2860 static int manager_state_cb(char *context, char *exten, int state, void *data)
2862 /* Notify managers of change */
2864 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
2866 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
2870 static int ast_manager_register_struct(struct manager_action *act)
2872 struct manager_action *cur, *prev = NULL;
2874 AST_RWLIST_WRLOCK(&actions);
2875 AST_RWLIST_TRAVERSE(&actions, cur, list) {
2876 int ret = strcasecmp(cur->action, act->action);
2878 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2879 AST_RWLIST_UNLOCK(&actions);
2882 if (ret > 0) { /* Insert these alphabetically */
2889 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
2891 AST_RWLIST_INSERT_HEAD(&actions, act, list);
2893 ast_verb(2, "Manager registered action %s\n", act->action);
2895 AST_RWLIST_UNLOCK(&actions);
2900 /*! \brief register a new command with manager, including online help. This is
2901 the preferred way to register a manager command */
2902 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2904 struct manager_action *cur = NULL;
2906 if (!(cur = ast_calloc(1, sizeof(*cur))))
2909 cur->action = action;
2910 cur->authority = auth;
2912 cur->synopsis = synopsis;
2913 cur->description = description;
2915 ast_manager_register_struct(cur);
2920 END Doxygen group */
2923 * The following are support functions for AMI-over-http.
2924 * The common entry point is generic_http_callback(),
2925 * which extracts HTTP header and URI fields and reformats
2926 * them into AMI messages, locates a proper session
2927 * (using the mansession_id Cookie or GET variable),
2928 * and calls process_message() as for regular AMI clients.
2929 * When done, the output (which goes to a temporary file)
2930 * is read back into a buffer and reformatted as desired,
2931 * then fed back to the client over the original socket.
2934 enum output_format {
2940 static char *contenttype[] = {
2941 [FORMAT_RAW] = "plain",
2942 [FORMAT_HTML] = "html",
2943 [FORMAT_XML] = "xml",
2947 * locate an http session in the list. The search key (ident) is
2948 * the value of the mansession_id cookie (0 is not valid and means
2949 * a session on the AMI socket).
2951 static struct mansession *find_session(unsigned long ident)
2953 struct mansession *s;
2958 AST_LIST_LOCK(&sessions);
2959 AST_LIST_TRAVERSE(&sessions, s, list) {
2960 ast_mutex_lock(&s->__lock);
2961 if (s->managerid == ident && !s->needdestroy) {
2962 ast_atomic_fetchadd_int(&s->inuse, 1);
2965 ast_mutex_unlock(&s->__lock);
2967 AST_LIST_UNLOCK(&sessions);
2972 int astman_verify_session_readpermissions(unsigned long ident, int perm)
2975 struct mansession *s;
2977 AST_LIST_LOCK(&sessions);
2978 AST_LIST_TRAVERSE(&sessions, s, list) {
2979 ast_mutex_lock(&s->__lock);
2980 if ((s->managerid == ident) && (s->readperm & perm)) {
2982 ast_mutex_unlock(&s->__lock);
2985 ast_mutex_unlock(&s->__lock);
2987 AST_LIST_UNLOCK(&sessions);
2991 int astman_verify_session_writepermissions(unsigned long ident, int perm)
2994 struct mansession *s;
2996 AST_LIST_LOCK(&sessions);
2997 AST_LIST_TRAVERSE(&sessions, s, list) {
2998 ast_mutex_lock(&s->__lock);
2999 if ((s->managerid == ident) && (s->writeperm & perm)) {
3001 ast_mutex_unlock(&s->__lock);
3004 ast_mutex_unlock(&s->__lock);
3006 AST_LIST_UNLOCK(&sessions);
3011 * convert to xml with various conversion:
3012 * mode & 1 -> lowercase;
3013 * mode & 2 -> replace non-alphanumeric chars with underscore
3015 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
3017 /* store in a local buffer to avoid calling ast_str_append too often */
3020 int space = sizeof(buf);
3021 /* repeat until done and nothing to flush */
3022 for ( ; *src || dst != buf ; src++) {
3023 if (*src == '\0' || space < 10) { /* flush */
3025 ast_str_append(out, 0, "%s", buf);
3027 space = sizeof(buf);
3032 if ( (mode & 2) && !isalnum(*src)) {
3039 strcpy(dst, "<");
3044 strcpy(dst, ">");
3049 strcpy(dst, """);
3054 strcpy(dst, "'");
3059 strcpy(dst, "&");
3065 *dst++ = mode ? tolower(*src) : *src;
3071 struct variable_count {
3076 static int compress_char(char c)
3081 else if (c >= 'a' && c <= 'z')
3089 static int variable_count_hash_fn(const void *vvc, const int flags)
3091 const struct variable_count *vc = vvc;
3093 for (i = 0; i < 5; i++) {
3094 if (vc->varname[i] == '\0')
3096 res += compress_char(vc->varname[i]) << (i * 6);
3101 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
3103 /* Due to the simplicity of struct variable_count, it makes no difference
3104 * if you pass in objects or strings, the same operation applies. This is
3105 * due to the fact that the hash occurs on the first element, which means
3106 * the address of both the struct and the string are exactly the same. */
3107 struct variable_count *vc = obj;
3109 return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
3112 /*! \brief Convert the input into XML or HTML.
3113 * The input is supposed to be a sequence of lines of the form
3115 * optionally followed by a blob of unformatted text.
3116 * A blank line is a section separator. Basically, this is a
3117 * mixture of the format of Manager Interface and CLI commands.
3118 * The unformatted text is considered as a single value of a field
3119 * named 'Opaque-data'.
3121 * At the moment the output format is the following (but it may
3122 * change depending on future requirements so don't count too
3123 * much on it when writing applications):
3125 * General: the unformatted text is used as a value of
3126 * XML output: to be completed
3129 * Each section is within <response type="object" id="xxx">
3130 * where xxx is taken from ajaxdest variable or defaults to unknown
3131 * Each row is reported as an attribute Name="value" of an XML
3132 * entity named from the variable ajaxobjtype, default to "generic"
3136 * each Name-value pair is output as a single row of a two-column table.
3137 * Sections (blank lines in the input) are separated by a <HR>
3140 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
3142 struct ast_variable *v;
3143 const char *dest = NULL;
3145 const char *objtype = NULL;
3146 int in_data = 0; /* parsing data */
3148 int xml = (format == FORMAT_XML);
3149 struct variable_count *vc = NULL;
3150 struct ao2_container *vco = NULL;
3152 for (v = vars; v; v = v->next) {
3153 if (!dest && !strcasecmp(v->name, "ajaxdest"))
3155 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
3161 objtype = "generic";
3163 /* we want to stop when we find an empty line */
3165 val = strsep(&in, "\r\n"); /* mark start and end of line */
3166 if (in && *in == '\n') /* remove trailing \n if any */
3168 ast_trim_blanks(val);
3169 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
3170 if (ast_strlen_zero(val)) {
3171 if (in_data) { /* close data */
3172 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3175 ast_str_append(out, 0, xml ? " /></response>\n" :
3176 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3183 /* we expect Name: value lines */
3187 var = strsep(&val, ":");
3188 if (val) { /* found the field name */
3189 val = ast_skip_blanks(val);
3190 ast_trim_blanks(var);
3191 } else { /* field name not found, move to opaque mode */
3193 var = "Opaque-data";
3199 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
3201 ast_str_append(out, 0, "<body>\n");
3202 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
3206 if (!in_data) { /* build appropriate line start */
3207 ast_str_append(out, 0, xml ? " " : "<tr><td>");
3208 if ((vc = ao2_find(vco, var, 0)))
3211 /* Create a new entry for this one */
3212 vc = ao2_alloc(sizeof(*vc), NULL);
3217 xml_copy_escape(out, var, xml ? 1 | 2 : 0);
3219 ast_str_append(out, 0, "-%d", vc->count);
3221 ast_str_append(out, 0, xml ? "='" : "</td><td>");
3222 if (!strcmp(var, "Opaque-data"))
3225 xml_copy_escape(out, val, 0); /* data field */
3227 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3229 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
3232 ast_str_append(out, 0, xml ? " /></response>\n" :
3233 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3238 static struct ast_str *generic_http_callback(enum output_format format,
3239 struct sockaddr_in *requestor, const char *uri,
3240 struct ast_variable *params, int *status,
3241 char **title, int *contentlength)
3243 struct mansession *s = NULL;
3244 unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
3246 struct ast_variable *v;
3247 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
3248 struct ast_str *out = NULL;
3249 struct message m = { 0 };
3253 for (v = params; v; v = v->next) {
3254 if (!strcasecmp(v->name, "mansession_id")) {
3255 sscanf(v->value, "%lx", &ident);
3260 if (!(s = find_session(ident))) {
3261 /* Create new session.
3262 * While it is not in the list we don't need any locking
3264 if (!(s = ast_calloc(1, sizeof(*s)))) {