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/config.h"
59 #include "asterisk/callerid.h"
60 #include "asterisk/lock.h"
61 #include "asterisk/cli.h"
62 #include "asterisk/app.h"
63 #include "asterisk/pbx.h"
64 #include "asterisk/md5.h"
65 #include "asterisk/acl.h"
66 #include "asterisk/utils.h"
67 #include "asterisk/http.h"
68 #include "asterisk/version.h"
69 #include "asterisk/threadstorage.h"
70 #include "asterisk/linkedlists.h"
71 #include "asterisk/version.h"
72 #include "asterisk/term.h"
73 #include "asterisk/astobj2.h"
76 * Linked list of events.
77 * Global events are appended to the list by append_event().
78 * The usecount is the number of stored pointers to the element,
79 * excluding the list pointers. So an element that is only in
80 * the list has a usecount of 0, not 1.
82 * Clients have a pointer to the last event processed, and for each
83 * of these clients we track the usecount of the elements.
84 * If we have a pointer to an entry in the list, it is safe to navigate
85 * it forward because elements will not be deleted, but only appended.
86 * The worst that can happen is seeing the pointer still NULL.
88 * When the usecount of an element drops to 0, and the element is the
89 * first in the list, we can remove it. Removal is done within the
90 * main thread, which is woken up for the purpose.
92 * For simplicity of implementation, we make sure the list is never empty.
95 int usecount; /*!< # of clients who still need the event */
97 unsigned int seq; /*!< sequence number */
98 AST_LIST_ENTRY(eventqent) eq_next;
99 char eventdata[1]; /*!< really variable size, allocated by append_event() */
102 static AST_LIST_HEAD_STATIC(all_events, eventqent);
104 static int displayconnects = 1;
105 static int allowmultiplelogin = 1;
106 static int timestampevents;
107 static int httptimeout = 60;
108 static int manager_enabled = 0;
109 static int webmanager_enabled = 0;
111 static int block_sockets;
112 static int num_sessions;
114 static int manager_debug; /*!< enable some debugging code in the manager */
117 * Descriptor for a manager session, either on the AMI socket or over HTTP.
120 * AMI session have managerid == 0; the entry is created upon a connect,
121 * and destroyed with the socket.
122 * HTTP sessions have managerid != 0, the value is used as a search key
123 * to lookup sessions (using the mansession_id cookie).
125 static const char *command_blacklist[] = {
131 pthread_t ms_t; /*!< Execution thread, basically useless */
132 ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
133 /* XXX need to document which fields it is protecting */
134 struct sockaddr_in sin; /*!< address we are connecting from */
135 FILE *f; /*!< fdopen() on the underlying fd */
136 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
137 int inuse; /*!< number of HTTP sessions using this entry */
138 int needdestroy; /*!< Whether an HTTP session should be destroyed */
139 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
140 unsigned long managerid; /*!< Unique manager identifier, 0 for AMI sessions */
141 time_t sessionstart; /*!< Session start time */
142 time_t sessiontimeout; /*!< Session timeout if HTTP */
143 char username[80]; /*!< Logged in username */
144 char challenge[10]; /*!< Authentication challenge */
145 int authenticated; /*!< Authentication status */
146 int readperm; /*!< Authorization for reading */
147 int writeperm; /*!< Authorization for writing */
148 char inbuf[1025]; /*!< Buffer */
149 /* we use the extra byte to add a '\0' and simplify parsing */
150 int inlen; /*!< number of buffered bytes */
151 int send_events; /*!< XXX what ? */
152 struct eventqent *last_ev; /*!< last event processed. */
153 int writetimeout; /*!< Timeout for ast_carefulwrite() */
154 AST_LIST_ENTRY(mansession) list;
157 #define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
159 static AST_LIST_HEAD_STATIC(sessions, mansession);
161 /*! \brief user descriptor, as read from the config file.
163 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
164 * lines which are not supported here, and readperm/writeperm/writetimeout
167 struct ast_manager_user {
174 int displayconnects; /*!< XXX unused */
175 int keep; /*!< mark entries created on a reload */
176 AST_RWLIST_ENTRY(ast_manager_user) list;
179 /*! \brief list of users found in the config file */
180 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
182 /*! \brief list of actions registered */
183 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
185 /*! \brief list of hooks registered */
186 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
188 /*! \brief Add a custom hook to be called when an event is fired */
189 void ast_manager_register_hook(struct manager_custom_hook *hook)
191 AST_RWLIST_WRLOCK(&manager_hooks);
192 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
193 AST_RWLIST_UNLOCK(&manager_hooks);
197 /*! \brief Delete a custom hook to be called when an event is fired */
198 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
200 AST_RWLIST_WRLOCK(&manager_hooks);
201 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
202 AST_RWLIST_UNLOCK(&manager_hooks);
207 * Event list management functions.
208 * We assume that the event list always has at least one element,
209 * and the delete code will not remove the last entry even if the
213 static time_t __deb(time_t start, const char *msg)
215 time_t now = time(NULL);
216 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
217 if (start != 0 && now - start > 5)
218 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
222 static void LOCK_EVENTS(void)
224 time_t start = __deb(0, "about to lock events");
225 AST_LIST_LOCK(&all_events);
226 __deb(start, "done lock events");
229 static void UNLOCK_EVENTS(void)
231 __deb(0, "about to unlock events");
232 AST_LIST_UNLOCK(&all_events);
235 static void LOCK_SESS(void)
237 time_t start = __deb(0, "about to lock sessions");
238 AST_LIST_LOCK(&sessions);
239 __deb(start, "done lock sessions");
242 static void UNLOCK_SESS(void)
244 __deb(0, "about to unlock sessions");
245 AST_LIST_UNLOCK(&sessions);
249 int check_manager_enabled()
251 return manager_enabled;
254 int check_webmanager_enabled()
256 return (webmanager_enabled && manager_enabled);
260 * Grab a reference to the last event, update usecount as needed.
261 * Can handle a NULL pointer.
263 static struct eventqent *grab_last(void)
265 struct eventqent *ret;
267 AST_LIST_LOCK(&all_events);
268 ret = AST_LIST_LAST(&all_events);
269 /* the list is never empty now, but may become so when
270 * we optimize it in the future, so be prepared.
273 ast_atomic_fetchadd_int(&ret->usecount, 1);
274 AST_LIST_UNLOCK(&all_events);
279 * Purge unused events. Remove elements from the head
280 * as long as their usecount is 0 and there is a next element.
282 static void purge_events(void)
284 struct eventqent *ev;
286 AST_LIST_LOCK(&all_events);
287 while ( (ev = AST_LIST_FIRST(&all_events)) &&
288 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
289 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
292 AST_LIST_UNLOCK(&all_events);
296 * helper functions to convert back and forth between
297 * string and numeric representation of set of flags
299 static struct permalias {
303 { EVENT_FLAG_SYSTEM, "system" },
304 { EVENT_FLAG_CALL, "call" },
305 { EVENT_FLAG_LOG, "log" },
306 { EVENT_FLAG_VERBOSE, "verbose" },
307 { EVENT_FLAG_COMMAND, "command" },
308 { EVENT_FLAG_AGENT, "agent" },
309 { EVENT_FLAG_USER, "user" },
310 { EVENT_FLAG_CONFIG, "config" },
311 { EVENT_FLAG_DTMF, "dtmf" },
312 { EVENT_FLAG_REPORTING, "reporting" },
313 { EVENT_FLAG_CDR, "cdr" },
318 /*! \brief Convert authority code to a list of options */
319 static char *authority_to_str(int authority, struct ast_str **res)
325 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
326 if (authority & perms[i].num) {
327 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
332 if ((*res)->used == 0) /* replace empty string with something sensible */
333 ast_str_append(res, 0, "<none>");
338 /*! Tells you if smallstr exists inside bigstr
339 which is delim by delim and uses no buf or stringsep
340 ast_instring("this|that|more","this",'|') == 1;
342 feel free to move this to app.c -anthm */
343 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
345 const char *val = bigstr, *next;
348 if ((next = strchr(val, delim))) {
349 if (!strncmp(val, smallstr, (next - val)))
354 return !strcmp(smallstr, val);
355 } while (*(val = (next + 1)));
360 static int get_perm(const char *instr)
367 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
368 if (ast_instring(instr, perms[x].label, ','))
376 * A number returns itself, false returns 0, true returns all flags,
377 * other strings return the flags that are set.
379 static int strings_to_mask(const char *string)
383 if (ast_strlen_zero(string))
386 for (p = string; *p; p++)
387 if (*p < '0' || *p > '9')
389 if (!p) /* all digits */
391 if (ast_false(string))
393 if (ast_true(string)) { /* all permissions */
395 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
399 return get_perm(string);
402 static int check_manager_session_inuse(const char *name)
404 struct mansession *session = NULL;
406 AST_LIST_LOCK(&sessions);
407 AST_LIST_TRAVERSE(&sessions, session, list) {
408 if (!strcasecmp(session->username, name))
411 AST_LIST_UNLOCK(&sessions);
413 return session ? 1 : 0;
418 * lookup an entry in the list of registered users.
419 * must be called with the list lock held.
421 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
423 struct ast_manager_user *user = NULL;
425 AST_RWLIST_TRAVERSE(&users, user, list)
426 if (!strcasecmp(user->username, name))
431 /*! \brief Get displayconnects config option.
432 * \param s manager session to get parameter from.
433 * \return displayconnects config option value.
435 static int manager_displayconnects (struct mansession *s)
437 struct ast_manager_user *user = NULL;
440 AST_RWLIST_RDLOCK(&users);
441 if ((user = get_manager_by_name_locked (s->username)))
442 ret = user->displayconnects;
443 AST_RWLIST_UNLOCK(&users);
448 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
450 struct manager_action *cur;
451 struct ast_str *authority;
456 e->command = "manager show command";
458 "Usage: manager show command <actionname>\n"
459 " Shows the detailed description for a specific Asterisk manager interface command.\n";
464 AST_RWLIST_RDLOCK(&actions);
465 AST_RWLIST_TRAVERSE(&actions, cur, list) {
466 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
467 ret = ast_strdup(cur->action);
468 break; /* make sure we exit even if ast_strdup() returns NULL */
471 AST_RWLIST_UNLOCK(&actions);
474 authority = ast_str_alloca(80);
476 return CLI_SHOWUSAGE;
478 AST_RWLIST_RDLOCK(&actions);
479 AST_RWLIST_TRAVERSE(&actions, cur, list) {
480 for (num = 3; num < a->argc; num++) {
481 if (!strcasecmp(cur->action, a->argv[num])) {
482 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
483 cur->action, cur->synopsis,
484 authority_to_str(cur->authority, &authority),
485 S_OR(cur->description, ""));
489 AST_RWLIST_UNLOCK(&actions);
494 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
498 e->command = "manager debug [on|off]";
499 e->usage = "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
505 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
506 else if (a->argc == 3) {
507 if (!strcasecmp(a->argv[2], "on"))
509 else if (!strcasecmp(a->argv[2], "off"))
512 return CLI_SHOWUSAGE;
517 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
519 struct ast_manager_user *user = NULL;
524 e->command = "manager show user";
526 " Usage: manager show user <user>\n"
527 " Display all information related to the manager user specified.\n";
534 AST_RWLIST_RDLOCK(&users);
535 AST_RWLIST_TRAVERSE(&users, user, list) {
536 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
537 ret = ast_strdup(user->username);
541 AST_RWLIST_UNLOCK(&users);
546 return CLI_SHOWUSAGE;
548 AST_RWLIST_RDLOCK(&users);
550 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
551 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
552 AST_RWLIST_UNLOCK(&users);
564 "displayconnects: %s\n",
565 (user->username ? user->username : "(N/A)"),
566 (user->secret ? "<Set>" : "(N/A)"),
567 (user->deny ? user->deny : "(N/A)"),
568 (user->permit ? user->permit : "(N/A)"),
569 (user->read ? user->read : "(N/A)"),
570 (user->write ? user->write : "(N/A)"),
571 (user->displayconnects ? "yes" : "no"));
573 AST_RWLIST_UNLOCK(&users);
579 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
581 struct ast_manager_user *user = NULL;
585 e->command = "manager show users";
587 "Usage: manager show users\n"
588 " Prints a listing of all managers that are currently configured on that\n"
595 return CLI_SHOWUSAGE;
597 AST_RWLIST_RDLOCK(&users);
599 /* If there are no users, print out something along those lines */
600 if (AST_RWLIST_EMPTY(&users)) {
601 ast_cli(a->fd, "There are no manager users.\n");
602 AST_RWLIST_UNLOCK(&users);
606 ast_cli(a->fd, "\nusername\n--------\n");
608 AST_RWLIST_TRAVERSE(&users, user, list) {
609 ast_cli(a->fd, "%s\n", user->username);
613 AST_RWLIST_UNLOCK(&users);
615 ast_cli(a->fd,"-------------------\n");
616 ast_cli(a->fd,"%d manager users configured.\n", count_amu);
622 /*! \brief CLI command manager list commands */
623 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
625 struct manager_action *cur;
626 struct ast_str *authority;
627 static const char *format = " %-15.15s %-15.15s %-55.55s\n";
630 e->command = "manager show commands";
632 "Usage: manager show commands\n"
633 " Prints a listing of all the available Asterisk manager interface commands.\n";
638 authority = ast_str_alloca(80);
639 ast_cli(a->fd, format, "Action", "Privilege", "Synopsis");
640 ast_cli(a->fd, format, "------", "---------", "--------");
642 AST_RWLIST_RDLOCK(&actions);
643 AST_RWLIST_TRAVERSE(&actions, cur, list)
644 ast_cli(a->fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
645 AST_RWLIST_UNLOCK(&actions);
650 /*! \brief CLI command manager list connected */
651 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
653 struct mansession *s;
654 time_t now = time(NULL);
655 static const char *format = " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n";
656 static const char *format2 = " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n";
660 e->command = "manager show connected";
662 "Usage: manager show connected\n"
663 " Prints a listing of the users that are currently connected to the\n"
664 "Asterisk manager interface.\n";
670 ast_cli(a->fd, format, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
672 AST_LIST_LOCK(&sessions);
673 AST_LIST_TRAVERSE(&sessions, s, list) {
674 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);
677 AST_LIST_UNLOCK(&sessions);
679 ast_cli(a->fd, "%d users connected.\n", count);
684 /*! \brief CLI command manager list eventq */
685 /* Should change to "manager show connected" */
686 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
691 e->command = "manager show eventq";
693 "Usage: manager show eventq\n"
694 " Prints a listing of all events pending in the Asterisk manger\n"
700 AST_LIST_LOCK(&all_events);
701 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
702 ast_cli(a->fd, "Usecount: %d\n",s->usecount);
703 ast_cli(a->fd, "Category: %d\n", s->category);
704 ast_cli(a->fd, "Event:\n%s", s->eventdata);
706 AST_LIST_UNLOCK(&all_events);
711 static struct ast_cli_entry cli_manager[] = {
712 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
713 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
714 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
715 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
716 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
717 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
718 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
722 * Decrement the usecount for the event; if it goes to zero,
723 * (why check for e->next ?) wakeup the
724 * main thread, which is in charge of freeing the record.
725 * Returns the next record.
727 static struct eventqent *unref_event(struct eventqent *e)
729 ast_atomic_fetchadd_int(&e->usecount, -1);
730 return AST_LIST_NEXT(e, eq_next);
733 static void ref_event(struct eventqent *e)
735 ast_atomic_fetchadd_int(&e->usecount, 1);
739 * destroy a session, leaving the usecount
741 static void free_session(struct mansession *s)
743 struct eventqent *eqe = s->last_ev;
746 ast_mutex_destroy(&s->__lock);
751 static void destroy_session(struct mansession *s)
753 AST_LIST_LOCK(&sessions);
754 AST_LIST_REMOVE(&sessions, s, list);
755 ast_atomic_fetchadd_int(&num_sessions, -1);
757 AST_LIST_UNLOCK(&sessions);
760 const char *astman_get_header(const struct message *m, char *var)
762 int x, l = strlen(var);
764 for (x = 0; x < m->hdrcount; x++) {
765 const char *h = m->headers[x];
766 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
773 struct ast_variable *astman_get_variables(const struct message *m)
776 struct ast_variable *head = NULL, *cur;
778 AST_DECLARE_APP_ARGS(args,
779 AST_APP_ARG(vars)[32];
782 varlen = strlen("Variable: ");
784 for (x = 0; x < m->hdrcount; x++) {
785 char *parse, *var, *val;
787 if (strncasecmp("Variable: ", m->headers[x], varlen))
789 parse = ast_strdupa(m->headers[x] + varlen);
791 AST_STANDARD_APP_ARGS(args, parse);
794 for (y = 0; y < args.argc; y++) {
797 var = val = ast_strdupa(args.vars[y]);
799 if (!val || ast_strlen_zero(var))
801 cur = ast_variable_new(var, val, "");
811 * helper function to send a string to the socket.
812 * Return -1 on error (e.g. buffer full).
814 static int send_string(struct mansession *s, char *string)
816 int len = strlen(string); /* residual length */
818 struct timeval start = ast_tvnow();
824 n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
825 if (n == len /* ok */ || n < 0 /* error */)
827 len -= n; /* skip already written data */
831 n = -1; /* error marker */
832 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
833 if (elapsed > s->writetimeout)
835 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
839 return n < 0 ? -1 : 0;
843 * \brief thread local buffer for astman_append
845 * \note This can not be defined within the astman_append() function
846 * because it declares a couple of functions that get used to
847 * initialize the thread local storage key.
849 AST_THREADSTORAGE(astman_append_buf);
850 /*! \brief initial allocated size for the astman_append_buf */
851 #define ASTMAN_APPEND_BUF_INITSIZE 256
854 * utility functions for creating AMI replies
856 void astman_append(struct mansession *s, const char *fmt, ...)
861 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
865 ast_str_set_va(&buf, 0, fmt, ap);
869 send_string(s, buf->str);
871 ast_verbose("fd == -1 in astman_append, should not happen\n");
874 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
875 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
876 hold the session lock _or_ be running in an action callback (in which case s->busy will
877 be non-zero). In either of these cases, there is no need to lock-protect the session's
878 fd, since no other output will be sent (events will be queued), and no input will
879 be read until either the current action finishes or get_input() obtains the session
883 /*! \brief send a response with an optional message,
884 * and terminate it with an empty line.
885 * m is used only to grab the 'ActionID' field.
887 * Use the explicit constant MSG_MOREDATA to remove the empty line.
888 * XXX MSG_MOREDATA should go to a header file.
890 #define MSG_MOREDATA ((char *)astman_send_response)
891 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
893 const char *id = astman_get_header(m,"ActionID");
895 astman_append(s, "Response: %s\r\n", resp);
896 if (!ast_strlen_zero(id))
897 astman_append(s, "ActionID: %s\r\n", id);
899 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
900 if (msg == MSG_MOREDATA)
903 astman_append(s, "Message: %s\r\n\r\n", msg);
905 astman_append(s, "\r\n");
908 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
910 astman_send_response_full(s, m, resp, msg, NULL);
913 void astman_send_error(struct mansession *s, const struct message *m, char *error)
915 astman_send_response_full(s, m, "Error", error, NULL);
918 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
920 astman_send_response_full(s, m, "Success", msg, NULL);
923 static void astman_start_ack(struct mansession *s, const struct message *m)
925 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
928 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
930 astman_send_response_full(s, m, "Success", msg, listflag);
935 Rather than braindead on,off this now can also accept a specific int mask value
936 or a ',' delim list of mask strings (the same as manager.conf) -anthm
938 static int set_eventmask(struct mansession *s, const char *eventmask)
940 int maskint = strings_to_mask(eventmask);
942 ast_mutex_lock(&s->__lock);
944 s->send_events = maskint;
945 ast_mutex_unlock(&s->__lock);
951 * Here we start with action_ handlers for AMI actions,
952 * and the internal functions used by them.
953 * Generally, the handlers are called action_foo()
956 /* helper function for action_login() */
957 static int authenticate(struct mansession *s, const struct message *m)
959 const char *user = astman_get_header(m, "Username");
961 struct ast_ha *ha = NULL;
962 char *password = NULL;
963 int readperm = 0, writeperm = 0;
964 struct ast_flags config_flags = { 0 };
966 if (ast_strlen_zero(user)) /* missing username */
971 * XXX there should be no need to scan the config file again here,
972 * suffices to call get_manager_by_name_locked() to fetch
975 struct ast_config *cfg = ast_config_load("manager.conf", config_flags);
977 struct ast_variable *v;
981 while ( (cat = ast_category_browse(cfg, cat)) ) {
982 /* "general" is not a valid user */
983 if (strcasecmp(cat, user) || !strcasecmp(cat, "general"))
985 /* collect parameters for the user's entry */
986 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
987 if (!strcasecmp(v->name, "secret"))
988 password = ast_strdupa(v->value);
989 else if (!strcasecmp(v->name, "read"))
990 readperm = get_perm(v->value);
991 else if (!strcasecmp(v->name, "write"))
992 writeperm = get_perm(v->value);
993 else if (!strcasecmp(v->name, "permit") ||
994 !strcasecmp(v->name, "deny")) {
995 ha = ast_append_ha(v->name, v->value, ha, NULL);
996 } else if (!strcasecmp(v->name, "writetimeout")) {
997 int val = atoi(v->value);
1000 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
1002 s->writetimeout = val;
1008 ast_config_destroy(cfg);
1010 /* Didn't find the user in manager.conf, check users.conf */
1012 cfg = ast_config_load("users.conf", config_flags);
1015 while ( (cat = ast_category_browse(cfg, cat)) ) {
1016 if (!strcasecmp(cat, user) && strcasecmp(cat, "general"))
1020 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1021 ast_config_destroy(cfg);
1024 /* collect parameters for the user's entry from users.conf */
1025 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
1026 if (!strcasecmp(v->name, "secret"))
1027 password = ast_strdupa(v->value);
1028 else if (!strcasecmp(v->name, "read"))
1029 readperm = get_perm(v->value);
1030 else if (!strcasecmp(v->name, "write"))
1031 writeperm = get_perm(v->value);
1032 else if (!strcasecmp(v->name, "permit") ||
1033 !strcasecmp(v->name, "deny")) {
1034 ha = ast_append_ha(v->name, v->value, ha, NULL);
1035 } else if (!strcasecmp(v->name, "writetimeout")) {
1036 int val = atoi(v->value);
1039 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
1041 s->writetimeout = val;
1042 } else if (!strcasecmp(v->name, "hasmanager"))
1043 hasmanager = ast_true(v->value);
1044 else if (!strcasecmp(v->name, "managerread"))
1045 readperm = get_perm(v->value);
1046 else if (!strcasecmp(v->name, "managerwrite"))
1047 writeperm = get_perm(v->value);
1049 ast_config_destroy(cfg);
1051 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1063 int good = ast_apply_ha(ha, &(s->sin));
1066 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1070 if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
1071 const char *key = astman_get_header(m, "Key");
1072 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) &&
1073 !ast_strlen_zero(password)) {
1076 char md5key[256] = "";
1077 struct MD5Context md5;
1078 unsigned char digest[16];
1081 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1082 MD5Update(&md5, (unsigned char *) password, strlen(password));
1083 MD5Final(digest, &md5);
1084 for (x=0; x<16; x++)
1085 len += sprintf(md5key + len, "%2.2x", digest[x]);
1086 if (!strcmp(md5key, key))
1089 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
1090 S_OR(s->challenge, ""));
1093 } else if (password) {
1094 const char *pass = astman_get_header(m, "Secret");
1095 if (!strcmp(password, pass))
1099 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1102 ast_copy_string(s->username, user, sizeof(s->username));
1103 s->readperm = readperm;
1104 s->writeperm = writeperm;
1105 s->sessionstart = time(NULL);
1106 set_eventmask(s, astman_get_header(m, "Events"));
1110 /*! \brief Manager PING */
1111 static char mandescr_ping[] =
1112 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1113 " manager connection open.\n"
1114 "Variables: NONE\n";
1116 static int action_ping(struct mansession *s, const struct message *m)
1118 astman_send_response(s, m, "Success", "Ping: Pong\r\n");
1122 static char mandescr_getconfig[] =
1123 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1124 "file by category and contents.\n"
1126 " Filename: Configuration filename (e.g. foo.conf)\n";
1128 static int action_getconfig(struct mansession *s, const struct message *m)
1130 struct ast_config *cfg;
1131 const char *fn = astman_get_header(m, "Filename");
1134 char *category=NULL;
1135 struct ast_variable *v;
1136 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1138 if (ast_strlen_zero(fn)) {
1139 astman_send_error(s, m, "Filename not specified");
1142 if (!(cfg = ast_config_load(fn, config_flags))) {
1143 astman_send_error(s, m, "Config file not found");
1146 astman_start_ack(s, m);
1147 while ((category = ast_category_browse(cfg, category))) {
1149 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1150 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1151 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1154 ast_config_destroy(cfg);
1155 astman_append(s, "\r\n");
1160 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1161 static void json_escape(char *out, const char *in)
1164 if (*in == '\\' || *in == '\"')
1171 static char mandescr_getconfigjson[] =
1172 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1173 "file by category and contents in JSON format. This only makes sense to be used\n"
1174 "using rawman over the HTTP interface.\n"
1176 " Filename: Configuration filename (e.g. foo.conf)\n";
1178 static int action_getconfigjson(struct mansession *s, const struct message *m)
1180 struct ast_config *cfg;
1181 const char *fn = astman_get_header(m, "Filename");
1182 char *category = NULL;
1183 struct ast_variable *v;
1186 unsigned int buf_len = 0;
1187 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1189 if (ast_strlen_zero(fn)) {
1190 astman_send_error(s, m, "Filename not specified");
1194 if (!(cfg = ast_config_load(fn, config_flags))) {
1195 astman_send_error(s, m, "Config file not found");
1200 buf = alloca(buf_len);
1202 astman_start_ack(s, m);
1203 astman_append(s, "JSON: {");
1204 while ((category = ast_category_browse(cfg, category))) {
1206 if (buf_len < 2 * strlen(category) + 1) {
1208 buf = alloca(buf_len);
1210 json_escape(buf, category);
1211 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1214 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1216 astman_append(s, ",");
1217 if (buf_len < 2 * strlen(v->name) + 1) {
1219 buf = alloca(buf_len);
1221 json_escape(buf, v->name);
1222 astman_append(s, "\"%s", buf);
1223 if (buf_len < 2 * strlen(v->value) + 1) {
1225 buf = alloca(buf_len);
1227 json_escape(buf, v->value);
1228 astman_append(s, "%s\"", buf);
1232 astman_append(s, "]");
1234 astman_append(s, "}\r\n\r\n");
1236 ast_config_destroy(cfg);
1241 /* helper function for action_updateconfig */
1242 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1246 const char *action, *cat, *var, *value, *match;
1247 struct ast_category *category;
1248 struct ast_variable *v;
1250 for (x = 0; x < 100000; x++) {
1251 unsigned int object = 0;
1253 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1254 action = astman_get_header(m, hdr);
1255 if (ast_strlen_zero(action))
1257 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1258 cat = astman_get_header(m, hdr);
1259 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1260 var = astman_get_header(m, hdr);
1261 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1262 value = astman_get_header(m, hdr);
1263 if (!ast_strlen_zero(value) && *value == '>') {
1267 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1268 match = astman_get_header(m, hdr);
1269 if (!strcasecmp(action, "newcat")) {
1270 if (!ast_strlen_zero(cat)) {
1271 category = ast_category_new(cat, dfn, 99999);
1273 ast_category_append(cfg, category);
1276 } else if (!strcasecmp(action, "renamecat")) {
1277 if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1278 category = ast_category_get(cfg, cat);
1280 ast_category_rename(category, value);
1282 } else if (!strcasecmp(action, "delcat")) {
1283 if (!ast_strlen_zero(cat))
1284 ast_category_delete(cfg, cat);
1285 } else if (!strcasecmp(action, "update")) {
1286 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1287 ast_variable_update(category, var, value, match, object);
1288 } else if (!strcasecmp(action, "delete")) {
1289 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1290 ast_variable_delete(category, var, match);
1291 } else if (!strcasecmp(action, "append")) {
1292 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1293 (category = ast_category_get(cfg, cat)) &&
1294 (v = ast_variable_new(var, value, dfn))){
1295 if (object || (match && !strcasecmp(match, "object")))
1297 ast_variable_append(category, v);
1303 static char mandescr_updateconfig[] =
1304 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1305 "configuration elements in Asterisk configuration files.\n"
1306 "Variables (X's represent 6 digit number beginning with 000000):\n"
1307 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1308 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1309 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1310 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1311 " Cat-XXXXXX: Category to operate on\n"
1312 " Var-XXXXXX: Variable to work on\n"
1313 " Value-XXXXXX: Value to work on\n"
1314 " Match-XXXXXX: Extra match required to match line\n";
1316 static int action_updateconfig(struct mansession *s, const struct message *m)
1318 struct ast_config *cfg;
1319 const char *sfn = astman_get_header(m, "SrcFilename");
1320 const char *dfn = astman_get_header(m, "DstFilename");
1322 const char *rld = astman_get_header(m, "Reload");
1323 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1325 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1326 astman_send_error(s, m, "Filename not specified");
1329 if (!(cfg = ast_config_load(sfn, config_flags))) {
1330 astman_send_error(s, m, "Config file not found");
1333 handle_updates(s, m, cfg, dfn);
1334 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1335 res = config_text_file_save(dfn, cfg, "Manager");
1336 ast_config_destroy(cfg);
1338 astman_send_error(s, m, "Save of config failed");
1341 astman_send_ack(s, m, NULL);
1342 if (!ast_strlen_zero(rld)) {
1345 ast_module_reload(rld);
1350 /*! \brief Manager WAITEVENT */
1351 static char mandescr_waitevent[] =
1352 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1353 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1354 "session, events will be generated and queued.\n"
1356 " Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1358 static int action_waitevent(struct mansession *s, const struct message *m)
1360 const char *timeouts = astman_get_header(m, "Timeout");
1364 const char *id = astman_get_header(m,"ActionID");
1365 char idText[256] = "";
1367 if (!ast_strlen_zero(id))
1368 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1370 if (!ast_strlen_zero(timeouts)) {
1371 sscanf(timeouts, "%i", &timeout);
1374 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1377 ast_mutex_lock(&s->__lock);
1378 if (s->waiting_thread != AST_PTHREADT_NULL)
1379 pthread_kill(s->waiting_thread, SIGURG);
1381 if (s->managerid) { /* AMI-over-HTTP session */
1383 * Make sure the timeout is within the expire time of the session,
1384 * as the client will likely abort the request if it does not see
1385 * data coming after some amount of time.
1387 time_t now = time(NULL);
1388 int max = s->sessiontimeout - now - 10;
1390 if (max < 0) /* We are already late. Strange but possible. */
1392 if (timeout < 0 || timeout > max)
1394 if (!s->send_events) /* make sure we record events */
1395 s->send_events = -1;
1397 ast_mutex_unlock(&s->__lock);
1399 /* XXX should this go inside the lock ? */
1400 s->waiting_thread = pthread_self(); /* let new events wake up this thread */
1401 ast_debug(1, "Starting waiting for an event!\n");
1403 for (x=0; x < timeout || timeout < 0; x++) {
1404 ast_mutex_lock(&s->__lock);
1407 /* We can have multiple HTTP session point to the same mansession entry.
1408 * The way we deal with it is not very nice: newcomers kick out the previous
1409 * HTTP session. XXX this needs to be improved.
1411 if (s->waiting_thread != pthread_self())
1415 ast_mutex_unlock(&s->__lock);
1418 if (s->managerid == 0) { /* AMI session */
1419 if (ast_wait_for_input(s->fd, 1000))
1421 } else { /* HTTP session */
1425 ast_debug(1, "Finished waiting for an event!\n");
1426 ast_mutex_lock(&s->__lock);
1427 if (s->waiting_thread == pthread_self()) {
1428 struct eventqent *eqe;
1429 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1430 while ( (eqe = NEW_EVENT(s)) ) {
1432 if (((s->readperm & eqe->category) == eqe->category) &&
1433 ((s->send_events & eqe->category) == eqe->category)) {
1434 astman_append(s, "%s", eqe->eventdata);
1436 s->last_ev = unref_event(s->last_ev);
1439 "Event: WaitEventComplete\r\n"
1442 s->waiting_thread = AST_PTHREADT_NULL;
1444 ast_debug(1, "Abandoning event request!\n");
1446 ast_mutex_unlock(&s->__lock);
1450 static char mandescr_listcommands[] =
1451 "Description: Returns the action name and synopsis for every\n"
1452 " action that is available to the user\n"
1453 "Variables: NONE\n";
1455 /*! \note The actionlock is read-locked by the caller of this function */
1456 static int action_listcommands(struct mansession *s, const struct message *m)
1458 struct manager_action *cur;
1459 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1461 astman_start_ack(s, m);
1462 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1463 if ((s->writeperm & cur->authority) == cur->authority)
1464 astman_append(s, "%s: %s (Priv: %s)\r\n",
1465 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1467 astman_append(s, "\r\n");
1472 static char mandescr_events[] =
1473 "Description: Enable/Disable sending of events to this manager\n"
1476 " EventMask: 'on' if all events should be sent,\n"
1477 " 'off' if no events should be sent,\n"
1478 " 'system,call,log' to select which flags events should have to be sent.\n";
1480 static int action_events(struct mansession *s, const struct message *m)
1482 const char *mask = astman_get_header(m, "EventMask");
1485 res = set_eventmask(s, mask);
1487 astman_send_response(s, m, "Success", "Events: On\r\n");
1489 astman_send_response(s, m, "Success", "Events: Off\r\n");
1494 static char mandescr_logoff[] =
1495 "Description: Logoff this manager session\n"
1496 "Variables: NONE\n";
1498 static int action_logoff(struct mansession *s, const struct message *m)
1500 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1504 static int action_login(struct mansession *s, const struct message *m)
1506 if (authenticate(s, m)) {
1508 astman_send_error(s, m, "Authentication failed");
1511 s->authenticated = 1;
1512 if (manager_displayconnects(s))
1513 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1514 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1515 astman_send_ack(s, m, "Authentication accepted");
1519 static int action_challenge(struct mansession *s, const struct message *m)
1521 const char *authtype = astman_get_header(m, "AuthType");
1523 if (!strcasecmp(authtype, "MD5")) {
1524 if (ast_strlen_zero(s->challenge))
1525 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1526 ast_mutex_lock(&s->__lock);
1527 astman_start_ack(s, m);
1528 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1529 ast_mutex_unlock(&s->__lock);
1531 astman_send_error(s, m, "Must specify AuthType");
1536 static char mandescr_hangup[] =
1537 "Description: Hangup a channel\n"
1539 " Channel: The channel name to be hungup\n";
1541 static int action_hangup(struct mansession *s, const struct message *m)
1543 struct ast_channel *c = NULL;
1544 const char *name = astman_get_header(m, "Channel");
1545 if (ast_strlen_zero(name)) {
1546 astman_send_error(s, m, "No channel specified");
1549 c = ast_get_channel_by_name_locked(name);
1551 astman_send_error(s, m, "No such channel");
1554 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1555 ast_channel_unlock(c);
1556 astman_send_ack(s, m, "Channel Hungup");
1560 static char mandescr_setvar[] =
1561 "Description: Set a global or local channel variable.\n"
1562 "Variables: (Names marked with * are required)\n"
1563 " Channel: Channel to set variable for\n"
1564 " *Variable: Variable name\n"
1567 static int action_setvar(struct mansession *s, const struct message *m)
1569 struct ast_channel *c = NULL;
1570 const char *name = astman_get_header(m, "Channel");
1571 const char *varname = astman_get_header(m, "Variable");
1572 const char *varval = astman_get_header(m, "Value");
1574 if (ast_strlen_zero(varname)) {
1575 astman_send_error(s, m, "No variable specified");
1579 if (!ast_strlen_zero(name)) {
1580 c = ast_get_channel_by_name_locked(name);
1582 astman_send_error(s, m, "No such channel");
1587 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1590 ast_channel_unlock(c);
1592 astman_send_ack(s, m, "Variable Set");
1597 static char mandescr_getvar[] =
1598 "Description: Get the value of a global or local channel variable.\n"
1599 "Variables: (Names marked with * are required)\n"
1600 " Channel: Channel to read variable from\n"
1601 " *Variable: Variable name\n"
1602 " ActionID: Optional Action id for message matching.\n";
1604 static int action_getvar(struct mansession *s, const struct message *m)
1606 struct ast_channel *c = NULL;
1607 const char *name = astman_get_header(m, "Channel");
1608 const char *varname = astman_get_header(m, "Variable");
1610 char workspace[1024] = "";
1612 if (ast_strlen_zero(varname)) {
1613 astman_send_error(s, m, "No variable specified");
1617 if (!ast_strlen_zero(name)) {
1618 c = ast_get_channel_by_name_locked(name);
1620 astman_send_error(s, m, "No such channel");
1625 if (varname[strlen(varname) - 1] == ')') {
1626 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1629 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1633 ast_channel_unlock(c);
1634 astman_start_ack(s, m);
1635 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1641 /*! \brief Manager "status" command to show channels */
1642 /* Needs documentation... */
1643 static int action_status(struct mansession *s, const struct message *m)
1645 const char *name = astman_get_header(m,"Channel");
1646 struct ast_channel *c;
1648 struct timeval now = ast_tvnow();
1649 long elapsed_seconds = 0;
1651 int all = ast_strlen_zero(name); /* set if we want all channels */
1652 const char *id = astman_get_header(m,"ActionID");
1653 char idText[256] = "";
1655 if (!ast_strlen_zero(id))
1656 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1659 c = ast_channel_walk_locked(NULL);
1661 c = ast_get_channel_by_name_locked(name);
1663 astman_send_error(s, m, "No such channel");
1667 astman_send_ack(s, m, "Channel status will follow");
1669 /* if we look by name, we break after the first iteration */
1673 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1678 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1682 "Privilege: Call\r\n"
1684 "CallerIDNum: %s\r\n"
1685 "CallerIDName: %s\r\n"
1686 "Accountcode: %s\r\n"
1687 "ChannelState: %d\r\n"
1688 "ChannelStateDesc: %s\r\n"
1698 S_OR(c->cid.cid_num, ""),
1699 S_OR(c->cid.cid_name, ""),
1702 ast_state2str(c->_state), c->context,
1703 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1707 "Privilege: Call\r\n"
1709 "CallerIDNum: %s\r\n"
1710 "CallerIDName: %s\r\n"
1718 S_OR(c->cid.cid_num, "<unknown>"),
1719 S_OR(c->cid.cid_name, "<unknown>"),
1721 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1723 ast_channel_unlock(c);
1726 c = ast_channel_walk_locked(c);
1729 "Event: StatusComplete\r\n"
1732 "\r\n",idText, channels);
1736 static char mandescr_sendtext[] =
1737 "Description: Sends A Text Message while in a call.\n"
1738 "Variables: (Names marked with * are required)\n"
1739 " *Channel: Channel to send message to\n"
1740 " *Message: Message to send\n"
1741 " ActionID: Optional Action id for message matching.\n";
1743 static int action_sendtext(struct mansession *s, const struct message *m)
1745 struct ast_channel *c = NULL;
1746 const char *name = astman_get_header(m, "Channel");
1747 const char *textmsg = astman_get_header(m, "Message");
1750 if (ast_strlen_zero(name)) {
1751 astman_send_error(s, m, "No channel specified");
1755 if (ast_strlen_zero(textmsg)) {
1756 astman_send_error(s, m, "No Message specified");
1760 c = ast_get_channel_by_name_locked(name);
1762 astman_send_error(s, m, "No such channel");
1766 res = ast_sendtext(c, textmsg);
1767 ast_channel_unlock(c);
1770 astman_send_ack(s, m, "Success");
1772 astman_send_error(s, m, "Failure");
1777 static char mandescr_redirect[] =
1778 "Description: Redirect (transfer) a call.\n"
1779 "Variables: (Names marked with * are required)\n"
1780 " *Channel: Channel to redirect\n"
1781 " ExtraChannel: Second call leg to transfer (optional)\n"
1782 " *Exten: Extension to transfer to\n"
1783 " *Context: Context to transfer to\n"
1784 " *Priority: Priority to transfer to\n"
1785 " ActionID: Optional Action id for message matching.\n";
1787 /*! \brief action_redirect: The redirect manager command */
1788 static int action_redirect(struct mansession *s, const struct message *m)
1790 const char *name = astman_get_header(m, "Channel");
1791 const char *name2 = astman_get_header(m, "ExtraChannel");
1792 const char *exten = astman_get_header(m, "Exten");
1793 const char *context = astman_get_header(m, "Context");
1794 const char *priority = astman_get_header(m, "Priority");
1795 struct ast_channel *chan, *chan2 = NULL;
1799 if (ast_strlen_zero(name)) {
1800 astman_send_error(s, m, "Channel not specified");
1803 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1804 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1805 astman_send_error(s, m, "Invalid priority\n");
1809 /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1810 chan = ast_get_channel_by_name_locked(name);
1813 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1814 astman_send_error(s, m, buf);
1817 if (ast_check_hangup(chan)) {
1818 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1819 ast_channel_unlock(chan);
1822 if (!ast_strlen_zero(name2))
1823 chan2 = ast_get_channel_by_name_locked(name2);
1824 if (chan2 && ast_check_hangup(chan2)) {
1825 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1826 ast_channel_unlock(chan);
1827 ast_channel_unlock(chan2);
1830 res = ast_async_goto(chan, context, exten, pi);
1832 if (!ast_strlen_zero(name2)) {
1834 res = ast_async_goto(chan2, context, exten, pi);
1838 astman_send_ack(s, m, "Dual Redirect successful");
1840 astman_send_error(s, m, "Secondary redirect failed");
1842 astman_send_ack(s, m, "Redirect successful");
1844 astman_send_error(s, m, "Redirect failed");
1846 ast_channel_unlock(chan);
1848 ast_channel_unlock(chan2);
1852 static char mandescr_command[] =
1853 "Description: Run a CLI command.\n"
1854 "Variables: (Names marked with * are required)\n"
1855 " *Command: Asterisk CLI command to run\n"
1856 " ActionID: Optional Action id for message matching.\n";
1858 /*! \brief Manager command "command" - execute CLI command */
1859 static int action_command(struct mansession *s, const struct message *m)
1861 const char *cmd = astman_get_header(m, "Command");
1862 const char *id = astman_get_header(m, "ActionID");
1863 char *buf, *final_buf;
1864 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1865 int fd = mkstemp(template), i = 0;
1868 for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
1869 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
1870 astman_send_error(s, m, "Command blacklisted");
1875 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1876 if (!ast_strlen_zero(id))
1877 astman_append(s, "ActionID: %s\r\n", id);
1878 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1879 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
1880 l = lseek(fd, 0, SEEK_END); /* how many chars available */
1882 /* This has a potential to overflow the stack. Hence, use the heap. */
1883 buf = ast_calloc(1, l + 1);
1884 final_buf = ast_calloc(1, l + 1);
1886 lseek(fd, 0, SEEK_SET);
1890 term_strip(final_buf, buf, l);
1891 final_buf[l] = '\0';
1893 astman_append(s, S_OR(final_buf, buf));
1898 astman_append(s, "--END COMMAND--\r\n\r\n");
1900 ast_free(final_buf);
1904 /* helper function for originate */
1905 struct fast_originate_helper {
1906 char tech[AST_MAX_EXTENSION];
1907 char data[AST_MAX_EXTENSION];
1909 char app[AST_MAX_APP];
1910 char appdata[AST_MAX_EXTENSION];
1911 char cid_name[AST_MAX_EXTENSION];
1912 char cid_num[AST_MAX_EXTENSION];
1913 char context[AST_MAX_CONTEXT];
1914 char exten[AST_MAX_EXTENSION];
1915 char idtext[AST_MAX_EXTENSION];
1916 char account[AST_MAX_ACCOUNT_CODE];
1918 struct ast_variable *vars;
1921 static void *fast_originate(void *data)
1923 struct fast_originate_helper *in = data;
1926 struct ast_channel *chan = NULL;
1927 char requested_channel[AST_CHANNEL_NAME];
1929 if (!ast_strlen_zero(in->app)) {
1930 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1931 S_OR(in->cid_num, NULL),
1932 S_OR(in->cid_name, NULL),
1933 in->vars, in->account, &chan);
1935 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1936 S_OR(in->cid_num, NULL),
1937 S_OR(in->cid_name, NULL),
1938 in->vars, in->account, &chan);
1942 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
1943 /* Tell the manager what happened with the channel */
1944 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1952 "CallerIDNum: %s\r\n"
1953 "CallerIDName: %s\r\n",
1954 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
1955 chan ? chan->uniqueid : "<null>",
1956 S_OR(in->cid_num, "<unknown>"),
1957 S_OR(in->cid_name, "<unknown>")
1960 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1962 ast_channel_unlock(chan);
1967 static char mandescr_originate[] =
1968 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1969 " Application/Data\n"
1970 "Variables: (Names marked with * are required)\n"
1971 " *Channel: Channel name to call\n"
1972 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1973 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1974 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1975 " Application: Application to use\n"
1976 " Data: Data to use (requires 'Application')\n"
1977 " Timeout: How long to wait for call to be answered (in ms)\n"
1978 " CallerID: Caller ID to be set on the outgoing channel\n"
1979 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1980 " Account: Account code\n"
1981 " Async: Set to 'true' for fast origination\n";
1983 static int action_originate(struct mansession *s, const struct message *m)
1985 const char *name = astman_get_header(m, "Channel");
1986 const char *exten = astman_get_header(m, "Exten");
1987 const char *context = astman_get_header(m, "Context");
1988 const char *priority = astman_get_header(m, "Priority");
1989 const char *timeout = astman_get_header(m, "Timeout");
1990 const char *callerid = astman_get_header(m, "CallerID");
1991 const char *account = astman_get_header(m, "Account");
1992 const char *app = astman_get_header(m, "Application");
1993 const char *appdata = astman_get_header(m, "Data");
1994 const char *async = astman_get_header(m, "Async");
1995 const char *id = astman_get_header(m, "ActionID");
1996 struct ast_variable *vars = astman_get_variables(m);
1998 char *l = NULL, *n = NULL;
2008 astman_send_error(s, m, "Channel not specified");
2011 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2012 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2013 astman_send_error(s, m, "Invalid priority\n");
2017 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2018 astman_send_error(s, m, "Invalid timeout\n");
2021 ast_copy_string(tmp, name, sizeof(tmp));
2023 data = strchr(tmp, '/');
2025 astman_send_error(s, m, "Invalid channel\n");
2029 ast_copy_string(tmp2, callerid, sizeof(tmp2));
2030 ast_callerid_parse(tmp2, &n, &l);
2032 if (ast_strlen_zero(n))
2036 ast_shrink_phone_number(l);
2037 if (ast_strlen_zero(l))
2040 if (ast_true(async)) {
2041 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2045 if (!ast_strlen_zero(id))
2046 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2047 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2048 ast_copy_string(fast->data, data, sizeof(fast->data));
2049 ast_copy_string(fast->app, app, sizeof(fast->app));
2050 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2052 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2054 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2056 ast_copy_string(fast->context, context, sizeof(fast->context));
2057 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2058 ast_copy_string(fast->account, account, sizeof(fast->account));
2060 fast->priority = pi;
2061 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2067 } else if (!ast_strlen_zero(app)) {
2068 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2070 if (exten && context && pi)
2071 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2073 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2078 astman_send_ack(s, m, "Originate successfully queued");
2080 astman_send_error(s, m, "Originate failed");
2084 /*! \brief Help text for manager command mailboxstatus
2086 static char mandescr_mailboxstatus[] =
2087 "Description: Checks a voicemail account for status.\n"
2088 "Variables: (Names marked with * are required)\n"
2089 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2090 " ActionID: Optional ActionID for message matching.\n"
2091 "Returns number of messages.\n"
2092 " Message: Mailbox Status\n"
2093 " Mailbox: <mailboxid>\n"
2094 " Waiting: <count>\n"
2097 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2099 const char *mailbox = astman_get_header(m, "Mailbox");
2102 if (ast_strlen_zero(mailbox)) {
2103 astman_send_error(s, m, "Mailbox not specified");
2106 ret = ast_app_has_voicemail(mailbox, NULL);
2107 astman_start_ack(s, m);
2108 astman_append(s, "Message: Mailbox Status\r\n"
2110 "Waiting: %d\r\n\r\n", mailbox, ret);
2114 static char mandescr_mailboxcount[] =
2115 "Description: Checks a voicemail account for new messages.\n"
2116 "Variables: (Names marked with * are required)\n"
2117 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2118 " ActionID: Optional ActionID for message matching.\n"
2119 "Returns number of new and old messages.\n"
2120 " Message: Mailbox Message Count\n"
2121 " Mailbox: <mailboxid>\n"
2122 " NewMessages: <count>\n"
2123 " OldMessages: <count>\n"
2125 static int action_mailboxcount(struct mansession *s, const struct message *m)
2127 const char *mailbox = astman_get_header(m, "Mailbox");
2128 int newmsgs = 0, oldmsgs = 0;
2130 if (ast_strlen_zero(mailbox)) {
2131 astman_send_error(s, m, "Mailbox not specified");
2134 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2135 astman_start_ack(s, m);
2136 astman_append(s, "Message: Mailbox Message Count\r\n"
2138 "NewMessages: %d\r\n"
2139 "OldMessages: %d\r\n"
2141 mailbox, newmsgs, oldmsgs);
2145 static char mandescr_extensionstate[] =
2146 "Description: Report the extension state for given extension.\n"
2147 " If the extension has a hint, will use devicestate to check\n"
2148 " the status of the device connected to the extension.\n"
2149 "Variables: (Names marked with * are required)\n"
2150 " *Exten: Extension to check state on\n"
2151 " *Context: Context for extension\n"
2152 " ActionId: Optional ID for this transaction\n"
2153 "Will return an \"Extension Status\" message.\n"
2154 "The response will include the hint for the extension and the status.\n";
2156 static int action_extensionstate(struct mansession *s, const struct message *m)
2158 const char *exten = astman_get_header(m, "Exten");
2159 const char *context = astman_get_header(m, "Context");
2160 char hint[256] = "";
2162 if (ast_strlen_zero(exten)) {
2163 astman_send_error(s, m, "Extension not specified");
2166 if (ast_strlen_zero(context))
2167 context = "default";
2168 status = ast_extension_state(NULL, context, exten);
2169 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2170 astman_start_ack(s, m);
2171 astman_append(s, "Message: Extension Status\r\n"
2175 "Status: %d\r\n\r\n",
2176 exten, context, hint, status);
2180 static char mandescr_timeout[] =
2181 "Description: Hangup a channel after a certain time.\n"
2182 "Variables: (Names marked with * are required)\n"
2183 " *Channel: Channel name to hangup\n"
2184 " *Timeout: Maximum duration of the call (sec)\n"
2185 "Acknowledges set time with 'Timeout Set' message\n";
2187 static int action_timeout(struct mansession *s, const struct message *m)
2189 struct ast_channel *c;
2190 const char *name = astman_get_header(m, "Channel");
2191 int timeout = atoi(astman_get_header(m, "Timeout"));
2193 if (ast_strlen_zero(name)) {
2194 astman_send_error(s, m, "No channel specified");
2198 astman_send_error(s, m, "No timeout specified");
2201 c = ast_get_channel_by_name_locked(name);
2203 astman_send_error(s, m, "No such channel");
2206 ast_channel_setwhentohangup(c, timeout);
2207 ast_channel_unlock(c);
2208 astman_send_ack(s, m, "Timeout Set");
2213 * Send any applicable events to the client listening on this socket.
2214 * Wait only for a finite time on each event, and drop all events whether
2215 * they are successfully sent or not.
2217 static int process_events(struct mansession *s)
2221 ast_mutex_lock(&s->__lock);
2223 struct eventqent *eqe;
2225 while ( (eqe = NEW_EVENT(s)) ) {
2227 if (!ret && s->authenticated &&
2228 (s->readperm & eqe->category) == eqe->category &&
2229 (s->send_events & eqe->category) == eqe->category) {
2230 if (send_string(s, eqe->eventdata) < 0)
2231 ret = -1; /* don't send more */
2233 s->last_ev = unref_event(s->last_ev);
2236 ast_mutex_unlock(&s->__lock);
2240 static char mandescr_userevent[] =
2241 "Description: Send an event to manager sessions.\n"
2242 "Variables: (Names marked with * are required)\n"
2243 " *UserEvent: EventStringToSend\n"
2244 " Header1: Content1\n"
2245 " HeaderN: ContentN\n";
2247 static int action_userevent(struct mansession *s, const struct message *m)
2249 const char *event = astman_get_header(m, "UserEvent");
2250 char body[2048] = "";
2252 for (x = 0; x < m->hdrcount; x++) {
2253 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2254 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2255 bodylen += strlen(m->headers[x]);
2256 ast_copy_string(body + bodylen, "\r\n", 3);
2261 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2265 static char mandescr_coresettings[] =
2266 "Description: Query for Core PBX settings.\n"
2267 "Variables: (Names marked with * are optional)\n"
2268 " *ActionID: ActionID of this transaction\n";
2270 /*! \brief Show PBX core settings information */
2271 static int action_coresettings(struct mansession *s, const struct message *m)
2273 const char *actionid = astman_get_header(m, "ActionID");
2274 char idText[150] = "";
2276 if (!ast_strlen_zero(actionid))
2277 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2279 astman_append(s, "Response: Success\r\n"
2281 "AMIversion: %s\r\n"
2282 "AsteriskVersion: %s\r\n"
2283 "SystemName: %s\r\n"
2284 "CoreMaxCalls: %d\r\n"
2285 "CoreMaxLoadAvg: %f\r\n"
2286 "CoreRunUser: %s\r\n"
2287 "CoreRunGroup: %s\r\n"
2288 "CoreMaxFilehandles: %d\r\n"
2289 "CoreRealTimeEnabled: %s\r\n"
2290 "CoreCDRenabled: %s\r\n"
2291 "CoreHTTPenabled: %s\r\n"
2296 ast_config_AST_SYSTEM_NAME,
2299 ast_config_AST_RUN_USER,
2300 ast_config_AST_RUN_GROUP,
2302 ast_realtime_enabled() ? "Yes" : "No",
2303 check_cdr_enabled() ? "Yes" : "No",
2304 check_webmanager_enabled() ? "Yes" : "No"
2309 static char mandescr_corestatus[] =
2310 "Description: Query for Core PBX status.\n"
2311 "Variables: (Names marked with * are optional)\n"
2312 " *ActionID: ActionID of this transaction\n";
2314 /*! \brief Show PBX core status information */
2315 static int action_corestatus(struct mansession *s, const struct message *m)
2317 const char *actionid = astman_get_header(m, "ActionID");
2319 char startuptime[150];
2320 char reloadtime[150];
2323 if (!ast_strlen_zero(actionid))
2324 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2326 ast_localtime(&ast_startuptime, &tm, NULL);
2327 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2328 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2329 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2331 astman_append(s, "Response: Success\r\n"
2333 "CoreStartupTime: %s\r\n"
2334 "CoreReloadTime: %s\r\n"
2335 "CoreCurrentCalls: %d\r\n"
2340 ast_active_channels()
2345 static char mandescr_reload[] =
2346 "Description: Send a reload event.\n"
2347 "Variables: (Names marked with * are optional)\n"
2348 " *ActionID: ActionID of this transaction\n"
2349 " *Module: Name of the module to reload\n";
2351 /*! \brief Send a reload event */
2352 static int action_reload(struct mansession *s, const struct message *m)
2354 const char *actionid = astman_get_header(m, "ActionID");
2355 const char *module = astman_get_header(m, "Module");
2356 int res = ast_module_reload(S_OR(module, NULL));
2357 char idText[80] = "";
2359 if (!ast_strlen_zero(actionid))
2360 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2362 astman_append(s, "Response: Success\r\n%s", idText);
2364 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2368 static char mandescr_coreshowchannels[] =
2369 "Description: List currently defined channels and some information\n"
2372 " ActionID: Optional Action id for message matching.\n";
2374 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2375 * and some information about them. */
2376 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2378 const char *actionid = astman_get_header(m, "ActionID");
2379 char actionidtext[256] = "";
2380 struct ast_channel *c = NULL;
2382 int duration, durh, durm, durs;
2384 if (!ast_strlen_zero(actionid))
2385 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2387 astman_send_listack(s, m, "Channels will follow", "start");
2389 while ((c = ast_channel_walk_locked(c)) != NULL) {
2390 struct ast_channel *bc = ast_bridged_channel(c);
2391 char durbuf[10] = "";
2393 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2394 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2395 durh = duration / 3600;
2396 durm = (duration % 3600) / 60;
2397 durs = duration % 60;
2398 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2407 "ChannelState: %d\r\n"
2408 "ChannelStateDesc: %s\r\n"
2409 "Application: %s\r\n"
2410 "ApplicationData: %s\r\n"
2411 "CallerIDnum: %s\r\n"
2413 "AccountCode: %s\r\n"
2414 "BridgedChannel: %s\r\n"
2415 "BridgedUniqueID: %s\r\n"
2416 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2417 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2418 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2419 ast_channel_unlock(c);
2424 "Event: CoreShowChannelsComplete\r\n"
2425 "EventList: Complete\r\n"
2428 "\r\n", numchans, actionidtext);
2435 * Done with the action handlers here, we start with the code in charge
2436 * of accepting connections and serving them.
2437 * accept_thread() forks a new thread for each connection, session_do(),
2438 * which in turn calls get_input() repeatedly until a full message has
2439 * been accumulated, and then invokes process_message() to pass it to
2440 * the appropriate handler.
2444 * Process an AMI message, performing desired action.
2445 * Return 0 on success, -1 on error that require the session to be destroyed.
2447 static int process_message(struct mansession *s, const struct message *m)
2449 char action[80] = "";
2451 struct manager_action *tmp;
2452 const char *user = astman_get_header(m, "Username");
2454 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2455 ast_debug(1, "Manager received command '%s'\n", action);
2457 if (ast_strlen_zero(action)) {
2458 ast_mutex_lock(&s->__lock);
2459 astman_send_error(s, m, "Missing action in request");
2460 ast_mutex_unlock(&s->__lock);
2464 if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2465 ast_mutex_lock(&s->__lock);
2466 astman_send_error(s, m, "Permission denied");
2467 ast_mutex_unlock(&s->__lock);
2471 if (!allowmultiplelogin && !s->authenticated && user &&
2472 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2473 if (check_manager_session_inuse(user)) {
2475 ast_mutex_lock(&s->__lock);
2476 astman_send_error(s, m, "Login Already In Use");
2477 ast_mutex_unlock(&s->__lock);
2482 AST_RWLIST_RDLOCK(&actions);
2483 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2484 if (strcasecmp(action, tmp->action))
2486 if ((s->writeperm & tmp->authority) == tmp->authority)
2487 ret = tmp->func(s, m);
2489 astman_send_error(s, m, "Permission denied");
2492 AST_RWLIST_UNLOCK(&actions);
2496 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2497 ast_mutex_lock(&s->__lock);
2498 astman_send_error(s, m, buf);
2499 ast_mutex_unlock(&s->__lock);
2503 /* Once done with our message, deliver any pending events */
2504 return process_events(s);
2508 * Read one full line (including crlf) from the manager socket.
2510 * \r\n is the only valid terminator for the line.
2511 * (Note that, later, '\0' will be considered as the end-of-line marker,
2512 * so everything between the '\0' and the '\r\n' will not be used).
2513 * Also note that we assume output to have at least "maxlen" space.
2516 static int get_input(struct mansession *s, char *output)
2519 int maxlen = sizeof(s->inbuf) - 1;
2520 char *src = s->inbuf;
2523 * Look for \r\n within the buffer. If found, copy to the output
2524 * buffer and return, trimming the \r\n (not used afterwards).
2526 for (x = 0; x < s->inlen; x++) {
2527 int cr; /* set if we have \r */
2528 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2529 cr = 2; /* Found. Update length to include \r\n */
2530 else if (src[x] == '\n')
2531 cr = 1; /* also accept \n only */
2534 memmove(output, src, x); /*... but trim \r\n */
2535 output[x] = '\0'; /* terminate the string */
2536 x += cr; /* number of bytes used */
2537 s->inlen -= x; /* remaining size */
2538 memmove(src, src + x, s->inlen); /* remove used bytes */
2541 if (s->inlen >= maxlen) {
2542 /* no crlf found, and buffer full - sorry, too long for us */
2543 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2548 /* XXX do we really need this locking ? */
2549 ast_mutex_lock(&s->__lock);
2550 s->waiting_thread = pthread_self();
2551 ast_mutex_unlock(&s->__lock);
2553 res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
2555 ast_mutex_lock(&s->__lock);
2556 s->waiting_thread = AST_PTHREADT_NULL;
2557 ast_mutex_unlock(&s->__lock);
2560 /* If we get a signal from some other thread (typically because
2561 * there are new events queued), return 0 to notify the caller.
2565 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2568 ast_mutex_lock(&s->__lock);
2569 res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2571 res = -1; /* error return */
2574 src[s->inlen] = '\0';
2577 ast_mutex_unlock(&s->__lock);
2581 static int do_message(struct mansession *s)
2583 struct message m = { 0 };
2584 char header_buf[sizeof(s->inbuf)] = { '\0' };
2588 /* Check if any events are pending and do them if needed */
2589 if (process_events(s))
2591 res = get_input(s, header_buf);
2594 } else if (res > 0) {
2595 if (ast_strlen_zero(header_buf))
2596 return process_message(s, &m) ? -1 : 0;
2597 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2598 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2605 /*! \brief The body of the individual manager session.
2606 * Call get_input() to read one line at a time
2607 * (or be woken up on new events), collect the lines in a
2608 * message until found an empty line, and execute the request.
2609 * In any case, deliver events asynchronously through process_events()
2610 * (called from here if no line is available, or at the end of
2611 * process_message(). )
2613 static void *session_do(void *data)
2615 struct server_instance *ser = data;
2616 struct mansession *s = ast_calloc(1, sizeof(*s));
2623 s->writetimeout = 100;
2624 s->waiting_thread = AST_PTHREADT_NULL;
2626 flags = fcntl(ser->fd, F_GETFL);
2627 if (!block_sockets) /* make sure socket is non-blocking */
2628 flags |= O_NONBLOCK;
2630 flags &= ~O_NONBLOCK;
2631 fcntl(ser->fd, F_SETFL, flags);
2633 ast_mutex_init(&s->__lock);
2634 s->send_events = -1;
2635 /* these fields duplicate those in the 'ser' structure */
2638 s->sin = ser->requestor;
2640 AST_LIST_LOCK(&sessions);
2641 AST_LIST_INSERT_HEAD(&sessions, s, list);
2642 ast_atomic_fetchadd_int(&num_sessions, 1);
2643 AST_LIST_UNLOCK(&sessions);
2644 /* Hook to the tail of the event queue */
2645 s->last_ev = grab_last();
2647 astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
2649 if ((res = do_message(s)) < 0)
2652 /* session is over, explain why and terminate */
2653 if (s->authenticated) {
2654 if (manager_displayconnects(s))
2655 ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2656 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2658 if (displayconnects)
2659 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2660 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2669 /*! \brief remove at most n_max stale session from the list. */
2670 static void purge_sessions(int n_max)
2672 struct mansession *s;
2673 time_t now = time(NULL);
2675 AST_LIST_LOCK(&sessions);
2676 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2677 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2678 AST_LIST_REMOVE_CURRENT(list);
2679 ast_atomic_fetchadd_int(&num_sessions, -1);
2680 if (s->authenticated && (option_verbose > 1) && manager_displayconnects(s)) {
2681 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
2682 s->username, ast_inet_ntoa(s->sin.sin_addr));
2684 free_session(s); /* XXX outside ? */
2689 AST_LIST_TRAVERSE_SAFE_END;
2690 AST_LIST_UNLOCK(&sessions);
2694 * events are appended to a queue from where they
2695 * can be dispatched to clients.
2697 static int append_event(const char *str, int category)
2699 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2700 static int seq; /* sequence number */
2705 /* need to init all fields, because ast_malloc() does not */
2707 tmp->category = category;
2708 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2709 AST_LIST_NEXT(tmp, eq_next) = NULL;
2710 strcpy(tmp->eventdata, str);
2712 AST_LIST_LOCK(&all_events);
2713 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
2714 AST_LIST_UNLOCK(&all_events);
2719 /* XXX see if can be moved inside the function */
2720 AST_THREADSTORAGE(manager_event_buf);
2721 #define MANAGER_EVENT_BUF_INITSIZE 256
2723 /*! \brief manager_event: Send AMI event to client */
2724 int __manager_event(int category, const char *event,
2725 const char *file, int line, const char *func, const char *fmt, ...)
2727 struct mansession *s;
2728 struct manager_custom_hook *hook;
2729 struct ast_str *auth = ast_str_alloca(80);
2730 const char *cat_str;
2733 struct ast_str *buf;
2735 /* Abort if there aren't any manager sessions */
2739 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2742 cat_str = authority_to_str(category, &auth);
2743 ast_str_set(&buf, 0,
2744 "Event: %s\r\nPrivilege: %s\r\n",
2747 if (timestampevents) {
2749 ast_str_append(&buf, 0,
2750 "Timestamp: %ld.%06lu\r\n",
2751 now.tv_sec, (unsigned long) now.tv_usec);
2753 if (manager_debug) {
2755 ast_str_append(&buf, 0,
2756 "SequenceNumber: %d\r\n",
2757 ast_atomic_fetchadd_int(&seq, 1));
2758 ast_str_append(&buf, 0,
2759 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
2763 ast_str_append_va(&buf, 0, fmt, ap);
2766 ast_str_append(&buf, 0, "\r\n");
2768 append_event(buf->str, category);
2770 /* Wake up any sleeping sessions */
2771 AST_LIST_LOCK(&sessions);
2772 AST_LIST_TRAVERSE(&sessions, s, list) {
2773 ast_mutex_lock(&s->__lock);
2774 if (s->waiting_thread != AST_PTHREADT_NULL)
2775 pthread_kill(s->waiting_thread, SIGURG);
2776 ast_mutex_unlock(&s->__lock);
2778 AST_LIST_UNLOCK(&sessions);
2780 AST_RWLIST_RDLOCK(&manager_hooks);
2781 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
2782 hook->helper(category, event, buf->str);
2784 AST_RWLIST_UNLOCK(&manager_hooks);
2790 * support functions to register/unregister AMI action handlers,
2792 int ast_manager_unregister(char *action)
2794 struct manager_action *cur;
2796 AST_RWLIST_WRLOCK(&actions);
2797 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
2798 if (!strcasecmp(action, cur->action)) {
2799 AST_RWLIST_REMOVE_CURRENT(list);
2801 ast_verb(2, "Manager unregistered action %s\n", action);
2805 AST_RWLIST_TRAVERSE_SAFE_END;
2806 AST_RWLIST_UNLOCK(&actions);
2811 static int manager_state_cb(char *context, char *exten, int state, void *data)
2813 /* Notify managers of change */
2815 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
2817 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
2821 static int ast_manager_register_struct(struct manager_action *act)
2823 struct manager_action *cur, *prev = NULL;
2825 AST_RWLIST_WRLOCK(&actions);
2826 AST_RWLIST_TRAVERSE(&actions, cur, list) {
2827 int ret = strcasecmp(cur->action, act->action);
2829 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2830 AST_RWLIST_UNLOCK(&actions);
2833 if (ret > 0) { /* Insert these alphabetically */
2840 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
2842 AST_RWLIST_INSERT_HEAD(&actions, act, list);
2844 ast_verb(2, "Manager registered action %s\n", act->action);
2846 AST_RWLIST_UNLOCK(&actions);
2851 /*! \brief register a new command with manager, including online help. This is
2852 the preferred way to register a manager command */
2853 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2855 struct manager_action *cur = NULL;
2857 if (!(cur = ast_calloc(1, sizeof(*cur))))
2860 cur->action = action;
2861 cur->authority = auth;
2863 cur->synopsis = synopsis;
2864 cur->description = description;
2866 ast_manager_register_struct(cur);
2871 END Doxygen group */
2874 * The following are support functions for AMI-over-http.
2875 * The common entry point is generic_http_callback(),
2876 * which extracts HTTP header and URI fields and reformats
2877 * them into AMI messages, locates a proper session
2878 * (using the mansession_id Cookie or GET variable),
2879 * and calls process_message() as for regular AMI clients.
2880 * When done, the output (which goes to a temporary file)
2881 * is read back into a buffer and reformatted as desired,
2882 * then fed back to the client over the original socket.
2885 enum output_format {
2891 static char *contenttype[] = {
2892 [FORMAT_RAW] = "plain",
2893 [FORMAT_HTML] = "html",
2894 [FORMAT_XML] = "xml",
2898 * locate an http session in the list. The search key (ident) is
2899 * the value of the mansession_id cookie (0 is not valid and means
2900 * a session on the AMI socket).
2902 static struct mansession *find_session(unsigned long ident)
2904 struct mansession *s;
2909 AST_LIST_LOCK(&sessions);
2910 AST_LIST_TRAVERSE(&sessions, s, list) {
2911 ast_mutex_lock(&s->__lock);
2912 if (s->managerid == ident && !s->needdestroy) {
2913 ast_atomic_fetchadd_int(&s->inuse, 1);
2916 ast_mutex_unlock(&s->__lock);
2918 AST_LIST_UNLOCK(&sessions);
2923 int astman_verify_session_readpermissions(unsigned long ident, int perm)
2926 struct mansession *s;
2928 AST_LIST_LOCK(&sessions);
2929 AST_LIST_TRAVERSE(&sessions, s, list) {
2930 ast_mutex_lock(&s->__lock);
2931 if ((s->managerid == ident) && (s->readperm & perm)) {
2933 ast_mutex_unlock(&s->__lock);
2936 ast_mutex_unlock(&s->__lock);
2938 AST_LIST_UNLOCK(&sessions);
2942 int astman_verify_session_writepermissions(unsigned long ident, int perm)
2945 struct mansession *s;
2947 AST_LIST_LOCK(&sessions);
2948 AST_LIST_TRAVERSE(&sessions, s, list) {
2949 ast_mutex_lock(&s->__lock);
2950 if ((s->managerid == ident) && (s->writeperm & perm)) {
2952 ast_mutex_unlock(&s->__lock);
2955 ast_mutex_unlock(&s->__lock);
2957 AST_LIST_UNLOCK(&sessions);
2962 * convert to xml with various conversion:
2963 * mode & 1 -> lowercase;
2964 * mode & 2 -> replace non-alphanumeric chars with underscore
2966 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
2968 /* store in a local buffer to avoid calling ast_str_append too often */
2971 int space = sizeof(buf);
2972 /* repeat until done and nothing to flush */
2973 for ( ; *src || dst != buf ; src++) {
2974 if (*src == '\0' || space < 10) { /* flush */
2976 ast_str_append(out, 0, "%s", buf);
2978 space = sizeof(buf);
2983 if ( (mode & 2) && !isalnum(*src)) {
2990 strcpy(dst, "<");
2995 strcpy(dst, ">");
3000 strcpy(dst, """);
3005 strcpy(dst, "'");
3010 strcpy(dst, "&");
3016 *dst++ = mode ? tolower(*src) : *src;
3022 struct variable_count {
3027 static int compress_char(char c)
3032 else if (c >= 'a' && c <= 'z')
3040 static int variable_count_hash_fn(const void *vvc, const int flags)
3042 const struct variable_count *vc = vvc;
3044 for (i = 0; i < 5; i++) {
3045 if (vc->varname[i] == '\0')
3047 res += compress_char(vc->varname[i]) << (i * 6);
3052 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
3054 /* Due to the simplicity of struct variable_count, it makes no difference
3055 * if you pass in objects or strings, the same operation applies. This is
3056 * due to the fact that the hash occurs on the first element, which means
3057 * the address of both the struct and the string are exactly the same. */
3058 struct variable_count *vc = obj;
3060 return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
3063 /*! \brief Convert the input into XML or HTML.
3064 * The input is supposed to be a sequence of lines of the form
3066 * optionally followed by a blob of unformatted text.
3067 * A blank line is a section separator. Basically, this is a
3068 * mixture of the format of Manager Interface and CLI commands.
3069 * The unformatted text is considered as a single value of a field
3070 * named 'Opaque-data'.
3072 * At the moment the output format is the following (but it may
3073 * change depending on future requirements so don't count too
3074 * much on it when writing applications):
3076 * General: the unformatted text is used as a value of
3077 * XML output: to be completed
3080 * Each section is within <response type="object" id="xxx">
3081 * where xxx is taken from ajaxdest variable or defaults to unknown
3082 * Each row is reported as an attribute Name="value" of an XML
3083 * entity named from the variable ajaxobjtype, default to "generic"
3087 * each Name-value pair is output as a single row of a two-column table.
3088 * Sections (blank lines in the input) are separated by a <HR>
3091 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
3093 struct ast_variable *v;
3094 const char *dest = NULL;
3096 const char *objtype = NULL;
3097 int in_data = 0; /* parsing data */
3099 int xml = (format == FORMAT_XML);
3100 struct variable_count *vc = NULL;
3101 struct ao2_container *vco = NULL;
3103 for (v = vars; v; v = v->next) {
3104 if (!dest && !strcasecmp(v->name, "ajaxdest"))
3106 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
3112 objtype = "generic";
3114 /* we want to stop when we find an empty line */
3116 val = strsep(&in, "\r\n"); /* mark start and end of line */
3117 if (in && *in == '\n') /* remove trailing \n if any */
3119 ast_trim_blanks(val);
3120 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
3121 if (ast_strlen_zero(val)) {
3122 if (in_data) { /* close data */
3123 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3126 ast_str_append(out, 0, xml ? " /></response>\n" :
3127 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3134 /* we expect Name: value lines */
3138 var = strsep(&val, ":");
3139 if (val) { /* found the field name */
3140 val = ast_skip_blanks(val);
3141 ast_trim_blanks(var);
3142 } else { /* field name not found, move to opaque mode */
3144 var = "Opaque-data";
3150 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
3152 ast_str_append(out, 0, "<body>\n");
3153 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
3157 if (!in_data) { /* build appropriate line start */
3158 ast_str_append(out, 0, xml ? " " : "<tr><td>");
3159 if ((vc = ao2_find(vco, var, 0)))
3162 /* Create a new entry for this one */
3163 vc = ao2_alloc(sizeof(*vc), NULL);
3168 xml_copy_escape(out, var, xml ? 1 | 2 : 0);
3170 ast_str_append(out, 0, "-%d", vc->count);
3172 ast_str_append(out, 0, xml ? "='" : "</td><td>");
3173 if (!strcmp(var, "Opaque-data"))
3176 xml_copy_escape(out, val, 0); /* data field */
3178 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3180 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
3183 ast_str_append(out, 0, xml ? " /></response>\n" :
3184 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3189 static struct ast_str *generic_http_callback(enum output_format format,
3190 struct sockaddr_in *requestor, const char *uri,
3191 struct ast_variable *params, int *status,
3192 char **title, int *contentlength)
3194 struct mansession *s = NULL;
3195 unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
3197 struct ast_variable *v;
3198 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
3199 struct ast_str *out = NULL;
3200 struct message m = { 0 };
3204 for (v = params; v; v = v->next) {
3205 if (!strcasecmp(v->name, "mansession_id")) {
3206 sscanf(v->value, "%lx", &ident);
3211 if (!(s = find_session(ident))) {
3212 /* Create new session.
3213 * While it is not in the list we don't need any locking
3215 if (!(s = ast_calloc(1, sizeof(*s)))) {
3217 goto generic_callback_out;
3219 s->sin = *requestor;
3221 s->waiting_thread = AST_PTHREADT_NULL;
3223 ast_mutex_init(&s->__lock);
3224 ast_mutex_lock(&s->__lock);
3226 s->managerid = rand() | 1; /* make sure it is non-zero */
3227 s->last_ev = grab_last();
3228 AST_LIST_LOCK(&sessions);
3229 AST_LIST_INSERT_HEAD(&sessions, s, list);
3230 ast_atomic_fetchadd_int(&num_sessions, 1);
3231 AST_LIST_UNLOCK(&sessions);
3234 ast_mutex_unlock(&s->__lock);
3236 if (!(out = ast_str_create(1024))) {
3238 goto generic_callback_out;
3241 s->fd = mkstemp(template); /* create a temporary file for command output */
3243 s->f = fdopen(s->fd, "w+");
3245 for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
3246 hdrlen = strlen(v->name) + strlen(v->value) + 3;