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/term.h"
72 #include "asterisk/astobj2.h"
75 * Linked list of events.
76 * Global events are appended to the list by append_event().
77 * The usecount is the number of stored pointers to the element,
78 * excluding the list pointers. So an element that is only in
79 * the list has a usecount of 0, not 1.
81 * Clients have a pointer to the last event processed, and for each
82 * of these clients we track the usecount of the elements.
83 * If we have a pointer to an entry in the list, it is safe to navigate
84 * it forward because elements will not be deleted, but only appended.
85 * The worst that can happen is seeing the pointer still NULL.
87 * When the usecount of an element drops to 0, and the element is the
88 * first in the list, we can remove it. Removal is done within the
89 * main thread, which is woken up for the purpose.
91 * For simplicity of implementation, we make sure the list is never empty.
94 int usecount; /*!< # of clients who still need the event */
96 unsigned int seq; /*!< sequence number */
97 AST_LIST_ENTRY(eventqent) eq_next;
98 char eventdata[1]; /*!< really variable size, allocated by append_event() */
101 static AST_LIST_HEAD_STATIC(all_events, eventqent);
103 static int displayconnects = 1;
104 static int allowmultiplelogin = 1;
105 static int timestampevents;
106 static int httptimeout = 60;
107 static int manager_enabled = 0;
108 static int webmanager_enabled = 0;
110 static int block_sockets;
111 static int num_sessions;
113 static int manager_debug; /*!< enable some debugging code in the manager */
116 * Descriptor for a manager session, either on the AMI socket or over HTTP.
119 * AMI session have managerid == 0; the entry is created upon a connect,
120 * and destroyed with the socket.
121 * HTTP sessions have managerid != 0, the value is used as a search key
122 * to lookup sessions (using the mansession_id cookie).
124 static const char *command_blacklist[] = {
130 pthread_t ms_t; /*!< Execution thread, basically useless */
131 ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
132 /* XXX need to document which fields it is protecting */
133 struct sockaddr_in sin; /*!< address we are connecting from */
134 FILE *f; /*!< fdopen() on the underlying fd */
135 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
136 int inuse; /*!< number of HTTP sessions using this entry */
137 int needdestroy; /*!< Whether an HTTP session should be destroyed */
138 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
139 unsigned long managerid; /*!< Unique manager identifier, 0 for AMI sessions */
140 time_t sessionstart; /*!< Session start time */
141 time_t sessiontimeout; /*!< Session timeout if HTTP */
142 char username[80]; /*!< Logged in username */
143 char challenge[10]; /*!< Authentication challenge */
144 int authenticated; /*!< Authentication status */
145 int readperm; /*!< Authorization for reading */
146 int writeperm; /*!< Authorization for writing */
147 char inbuf[1025]; /*!< Buffer */
148 /* we use the extra byte to add a '\0' and simplify parsing */
149 int inlen; /*!< number of buffered bytes */
150 int send_events; /*!< XXX what ? */
151 struct eventqent *last_ev; /*!< last event processed. */
152 int writetimeout; /*!< Timeout for ast_carefulwrite() */
153 AST_LIST_ENTRY(mansession) list;
156 #define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
158 static AST_LIST_HEAD_STATIC(sessions, mansession);
160 /*! \brief user descriptor, as read from the config file.
162 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
163 * lines which are not supported here, and readperm/writeperm/writetimeout
166 struct ast_manager_user {
173 int displayconnects; /*!< XXX unused */
174 int keep; /*!< mark entries created on a reload */
175 AST_RWLIST_ENTRY(ast_manager_user) list;
178 /*! \brief list of users found in the config file */
179 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
181 /*! \brief list of actions registered */
182 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
184 /*! \brief list of hooks registered */
185 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
187 /*! \brief Add a custom hook to be called when an event is fired */
188 void ast_manager_register_hook(struct manager_custom_hook *hook)
190 AST_RWLIST_WRLOCK(&manager_hooks);
191 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
192 AST_RWLIST_UNLOCK(&manager_hooks);
196 /*! \brief Delete a custom hook to be called when an event is fired */
197 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
199 AST_RWLIST_WRLOCK(&manager_hooks);
200 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
201 AST_RWLIST_UNLOCK(&manager_hooks);
206 * Event list management functions.
207 * We assume that the event list always has at least one element,
208 * and the delete code will not remove the last entry even if the
212 static time_t __deb(time_t start, const char *msg)
214 time_t now = time(NULL);
215 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
216 if (start != 0 && now - start > 5)
217 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
221 static void LOCK_EVENTS(void)
223 time_t start = __deb(0, "about to lock events");
224 AST_LIST_LOCK(&all_events);
225 __deb(start, "done lock events");
228 static void UNLOCK_EVENTS(void)
230 __deb(0, "about to unlock events");
231 AST_LIST_UNLOCK(&all_events);
234 static void LOCK_SESS(void)
236 time_t start = __deb(0, "about to lock sessions");
237 AST_LIST_LOCK(&sessions);
238 __deb(start, "done lock sessions");
241 static void UNLOCK_SESS(void)
243 __deb(0, "about to unlock sessions");
244 AST_LIST_UNLOCK(&sessions);
248 int check_manager_enabled()
250 return manager_enabled;
253 int check_webmanager_enabled()
255 return (webmanager_enabled && manager_enabled);
259 * Grab a reference to the last event, update usecount as needed.
260 * Can handle a NULL pointer.
262 static struct eventqent *grab_last(void)
264 struct eventqent *ret;
266 AST_LIST_LOCK(&all_events);
267 ret = AST_LIST_LAST(&all_events);
268 /* the list is never empty now, but may become so when
269 * we optimize it in the future, so be prepared.
272 ast_atomic_fetchadd_int(&ret->usecount, 1);
273 AST_LIST_UNLOCK(&all_events);
278 * Purge unused events. Remove elements from the head
279 * as long as their usecount is 0 and there is a next element.
281 static void purge_events(void)
283 struct eventqent *ev;
285 AST_LIST_LOCK(&all_events);
286 while ( (ev = AST_LIST_FIRST(&all_events)) &&
287 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
288 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
291 AST_LIST_UNLOCK(&all_events);
295 * helper functions to convert back and forth between
296 * string and numeric representation of set of flags
298 static struct permalias {
302 { EVENT_FLAG_SYSTEM, "system" },
303 { EVENT_FLAG_CALL, "call" },
304 { EVENT_FLAG_LOG, "log" },
305 { EVENT_FLAG_VERBOSE, "verbose" },
306 { EVENT_FLAG_COMMAND, "command" },
307 { EVENT_FLAG_AGENT, "agent" },
308 { EVENT_FLAG_USER, "user" },
309 { EVENT_FLAG_CONFIG, "config" },
310 { EVENT_FLAG_DTMF, "dtmf" },
311 { EVENT_FLAG_REPORTING, "reporting" },
312 { EVENT_FLAG_CDR, "cdr" },
317 /*! \brief Convert authority code to a list of options */
318 static char *authority_to_str(int authority, struct ast_str **res)
324 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
325 if (authority & perms[i].num) {
326 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
331 if ((*res)->used == 0) /* replace empty string with something sensible */
332 ast_str_append(res, 0, "<none>");
337 /*! Tells you if smallstr exists inside bigstr
338 which is delim by delim and uses no buf or stringsep
339 ast_instring("this|that|more","this",'|') == 1;
341 feel free to move this to app.c -anthm */
342 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
344 const char *val = bigstr, *next;
347 if ((next = strchr(val, delim))) {
348 if (!strncmp(val, smallstr, (next - val)))
353 return !strcmp(smallstr, val);
354 } while (*(val = (next + 1)));
359 static int get_perm(const char *instr)
366 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
367 if (ast_instring(instr, perms[x].label, ','))
375 * A number returns itself, false returns 0, true returns all flags,
376 * other strings return the flags that are set.
378 static int strings_to_mask(const char *string)
382 if (ast_strlen_zero(string))
385 for (p = string; *p; p++)
386 if (*p < '0' || *p > '9')
388 if (!p) /* all digits */
390 if (ast_false(string))
392 if (ast_true(string)) { /* all permissions */
394 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
398 return get_perm(string);
401 static int check_manager_session_inuse(const char *name)
403 struct mansession *session = NULL;
405 AST_LIST_LOCK(&sessions);
406 AST_LIST_TRAVERSE(&sessions, session, list) {
407 if (!strcasecmp(session->username, name))
410 AST_LIST_UNLOCK(&sessions);
412 return session ? 1 : 0;
417 * lookup an entry in the list of registered users.
418 * must be called with the list lock held.
420 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
422 struct ast_manager_user *user = NULL;
424 AST_RWLIST_TRAVERSE(&users, user, list)
425 if (!strcasecmp(user->username, name))
430 /*! \brief Get displayconnects config option.
431 * \param s manager session to get parameter from.
432 * \return displayconnects config option value.
434 static int manager_displayconnects (struct mansession *s)
436 struct ast_manager_user *user = NULL;
439 AST_RWLIST_RDLOCK(&users);
440 if ((user = get_manager_by_name_locked (s->username)))
441 ret = user->displayconnects;
442 AST_RWLIST_UNLOCK(&users);
447 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
449 struct manager_action *cur;
450 struct ast_str *authority;
455 e->command = "manager show command";
457 "Usage: manager show command <actionname>\n"
458 " Shows the detailed description for a specific Asterisk manager interface command.\n";
463 AST_RWLIST_RDLOCK(&actions);
464 AST_RWLIST_TRAVERSE(&actions, cur, list) {
465 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
466 ret = ast_strdup(cur->action);
467 break; /* make sure we exit even if ast_strdup() returns NULL */
470 AST_RWLIST_UNLOCK(&actions);
473 authority = ast_str_alloca(80);
475 return CLI_SHOWUSAGE;
477 AST_RWLIST_RDLOCK(&actions);
478 AST_RWLIST_TRAVERSE(&actions, cur, list) {
479 for (num = 3; num < a->argc; num++) {
480 if (!strcasecmp(cur->action, a->argv[num])) {
481 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
482 cur->action, cur->synopsis,
483 authority_to_str(cur->authority, &authority),
484 S_OR(cur->description, ""));
488 AST_RWLIST_UNLOCK(&actions);
493 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
497 e->command = "manager debug [on|off]";
498 e->usage = "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
504 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
505 else if (a->argc == 3) {
506 if (!strcasecmp(a->argv[2], "on"))
508 else if (!strcasecmp(a->argv[2], "off"))
511 return CLI_SHOWUSAGE;
516 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
518 struct ast_manager_user *user = NULL;
523 e->command = "manager show user";
525 " Usage: manager show user <user>\n"
526 " Display all information related to the manager user specified.\n";
533 AST_RWLIST_RDLOCK(&users);
534 AST_RWLIST_TRAVERSE(&users, user, list) {
535 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
536 ret = ast_strdup(user->username);
540 AST_RWLIST_UNLOCK(&users);
545 return CLI_SHOWUSAGE;
547 AST_RWLIST_RDLOCK(&users);
549 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
550 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
551 AST_RWLIST_UNLOCK(&users);
563 "displayconnects: %s\n",
564 (user->username ? user->username : "(N/A)"),
565 (user->secret ? "<Set>" : "(N/A)"),
566 (user->deny ? user->deny : "(N/A)"),
567 (user->permit ? user->permit : "(N/A)"),
568 (user->read ? user->read : "(N/A)"),
569 (user->write ? user->write : "(N/A)"),
570 (user->displayconnects ? "yes" : "no"));
572 AST_RWLIST_UNLOCK(&users);
578 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
580 struct ast_manager_user *user = NULL;
584 e->command = "manager show users";
586 "Usage: manager show users\n"
587 " Prints a listing of all managers that are currently configured on that\n"
594 return CLI_SHOWUSAGE;
596 AST_RWLIST_RDLOCK(&users);
598 /* If there are no users, print out something along those lines */
599 if (AST_RWLIST_EMPTY(&users)) {
600 ast_cli(a->fd, "There are no manager users.\n");
601 AST_RWLIST_UNLOCK(&users);
605 ast_cli(a->fd, "\nusername\n--------\n");
607 AST_RWLIST_TRAVERSE(&users, user, list) {
608 ast_cli(a->fd, "%s\n", user->username);
612 AST_RWLIST_UNLOCK(&users);
614 ast_cli(a->fd,"-------------------\n");
615 ast_cli(a->fd,"%d manager users configured.\n", count_amu);
621 /*! \brief CLI command manager list commands */
622 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
624 struct manager_action *cur;
625 struct ast_str *authority;
626 static const char *format = " %-15.15s %-15.15s %-55.55s\n";
629 e->command = "manager show commands";
631 "Usage: manager show commands\n"
632 " Prints a listing of all the available Asterisk manager interface commands.\n";
637 authority = ast_str_alloca(80);
638 ast_cli(a->fd, format, "Action", "Privilege", "Synopsis");
639 ast_cli(a->fd, format, "------", "---------", "--------");
641 AST_RWLIST_RDLOCK(&actions);
642 AST_RWLIST_TRAVERSE(&actions, cur, list)
643 ast_cli(a->fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
644 AST_RWLIST_UNLOCK(&actions);
649 /*! \brief CLI command manager list connected */
650 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
652 struct mansession *s;
653 time_t now = time(NULL);
654 static const char *format = " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n";
655 static const char *format2 = " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n";
659 e->command = "manager show connected";
661 "Usage: manager show connected\n"
662 " Prints a listing of the users that are currently connected to the\n"
663 "Asterisk manager interface.\n";
669 ast_cli(a->fd, format, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
671 AST_LIST_LOCK(&sessions);
672 AST_LIST_TRAVERSE(&sessions, s, list) {
673 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);
676 AST_LIST_UNLOCK(&sessions);
678 ast_cli(a->fd, "%d users connected.\n", count);
683 /*! \brief CLI command manager list eventq */
684 /* Should change to "manager show connected" */
685 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
690 e->command = "manager show eventq";
692 "Usage: manager show eventq\n"
693 " Prints a listing of all events pending in the Asterisk manger\n"
699 AST_LIST_LOCK(&all_events);
700 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
701 ast_cli(a->fd, "Usecount: %d\n",s->usecount);
702 ast_cli(a->fd, "Category: %d\n", s->category);
703 ast_cli(a->fd, "Event:\n%s", s->eventdata);
705 AST_LIST_UNLOCK(&all_events);
710 static struct ast_cli_entry cli_manager[] = {
711 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
712 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
713 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
714 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
715 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
716 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
717 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
721 * Decrement the usecount for the event; if it goes to zero,
722 * (why check for e->next ?) wakeup the
723 * main thread, which is in charge of freeing the record.
724 * Returns the next record.
726 static struct eventqent *unref_event(struct eventqent *e)
728 ast_atomic_fetchadd_int(&e->usecount, -1);
729 return AST_LIST_NEXT(e, eq_next);
732 static void ref_event(struct eventqent *e)
734 ast_atomic_fetchadd_int(&e->usecount, 1);
738 * destroy a session, leaving the usecount
740 static void free_session(struct mansession *s)
742 struct eventqent *eqe = s->last_ev;
745 ast_mutex_destroy(&s->__lock);
750 static void destroy_session(struct mansession *s)
752 AST_LIST_LOCK(&sessions);
753 AST_LIST_REMOVE(&sessions, s, list);
754 ast_atomic_fetchadd_int(&num_sessions, -1);
756 AST_LIST_UNLOCK(&sessions);
759 const char *astman_get_header(const struct message *m, char *var)
761 int x, l = strlen(var);
763 for (x = 0; x < m->hdrcount; x++) {
764 const char *h = m->headers[x];
765 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
772 struct ast_variable *astman_get_variables(const struct message *m)
775 struct ast_variable *head = NULL, *cur;
777 AST_DECLARE_APP_ARGS(args,
778 AST_APP_ARG(vars)[32];
781 varlen = strlen("Variable: ");
783 for (x = 0; x < m->hdrcount; x++) {
784 char *parse, *var, *val;
786 if (strncasecmp("Variable: ", m->headers[x], varlen))
788 parse = ast_strdupa(m->headers[x] + varlen);
790 AST_STANDARD_APP_ARGS(args, parse);
793 for (y = 0; y < args.argc; y++) {
796 var = val = ast_strdupa(args.vars[y]);
798 if (!val || ast_strlen_zero(var))
800 cur = ast_variable_new(var, val, "");
810 * helper function to send a string to the socket.
811 * Return -1 on error (e.g. buffer full).
813 static int send_string(struct mansession *s, char *string)
815 int len = strlen(string); /* residual length */
817 struct timeval start = ast_tvnow();
823 n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
824 if (n == len /* ok */ || n < 0 /* error */)
826 len -= n; /* skip already written data */
830 n = -1; /* error marker */
831 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
832 if (elapsed > s->writetimeout)
834 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
838 return n < 0 ? -1 : 0;
842 * \brief thread local buffer for astman_append
844 * \note This can not be defined within the astman_append() function
845 * because it declares a couple of functions that get used to
846 * initialize the thread local storage key.
848 AST_THREADSTORAGE(astman_append_buf);
849 /*! \brief initial allocated size for the astman_append_buf */
850 #define ASTMAN_APPEND_BUF_INITSIZE 256
853 * utility functions for creating AMI replies
855 void astman_append(struct mansession *s, const char *fmt, ...)
860 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
864 ast_str_set_va(&buf, 0, fmt, ap);
868 send_string(s, buf->str);
870 ast_verbose("fd == -1 in astman_append, should not happen\n");
873 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
874 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
875 hold the session lock _or_ be running in an action callback (in which case s->busy will
876 be non-zero). In either of these cases, there is no need to lock-protect the session's
877 fd, since no other output will be sent (events will be queued), and no input will
878 be read until either the current action finishes or get_input() obtains the session
882 /*! \brief send a response with an optional message,
883 * and terminate it with an empty line.
884 * m is used only to grab the 'ActionID' field.
886 * Use the explicit constant MSG_MOREDATA to remove the empty line.
887 * XXX MSG_MOREDATA should go to a header file.
889 #define MSG_MOREDATA ((char *)astman_send_response)
890 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
892 const char *id = astman_get_header(m,"ActionID");
894 astman_append(s, "Response: %s\r\n", resp);
895 if (!ast_strlen_zero(id))
896 astman_append(s, "ActionID: %s\r\n", id);
898 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
899 if (msg == MSG_MOREDATA)
902 astman_append(s, "Message: %s\r\n\r\n", msg);
904 astman_append(s, "\r\n");
907 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
909 astman_send_response_full(s, m, resp, msg, NULL);
912 void astman_send_error(struct mansession *s, const struct message *m, char *error)
914 astman_send_response_full(s, m, "Error", error, NULL);
917 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
919 astman_send_response_full(s, m, "Success", msg, NULL);
922 static void astman_start_ack(struct mansession *s, const struct message *m)
924 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
927 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
929 astman_send_response_full(s, m, "Success", msg, listflag);
934 Rather than braindead on,off this now can also accept a specific int mask value
935 or a ',' delim list of mask strings (the same as manager.conf) -anthm
937 static int set_eventmask(struct mansession *s, const char *eventmask)
939 int maskint = strings_to_mask(eventmask);
941 ast_mutex_lock(&s->__lock);
943 s->send_events = maskint;
944 ast_mutex_unlock(&s->__lock);
950 * Here we start with action_ handlers for AMI actions,
951 * and the internal functions used by them.
952 * Generally, the handlers are called action_foo()
955 /* helper function for action_login() */
956 static int authenticate(struct mansession *s, const struct message *m)
958 const char *user = astman_get_header(m, "Username");
960 struct ast_ha *ha = NULL;
961 char *password = NULL;
962 int readperm = 0, writeperm = 0;
963 struct ast_flags config_flags = { 0 };
965 if (ast_strlen_zero(user)) /* missing username */
970 * XXX there should be no need to scan the config file again here,
971 * suffices to call get_manager_by_name_locked() to fetch
974 struct ast_config *cfg = ast_config_load("manager.conf", config_flags);
976 struct ast_variable *v;
980 while ( (cat = ast_category_browse(cfg, cat)) ) {
981 /* "general" is not a valid user */
982 if (strcasecmp(cat, user) || !strcasecmp(cat, "general"))
984 /* collect parameters for the user's entry */
985 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
986 if (!strcasecmp(v->name, "secret"))
987 password = ast_strdupa(v->value);
988 else if (!strcasecmp(v->name, "read"))
989 readperm = get_perm(v->value);
990 else if (!strcasecmp(v->name, "write"))
991 writeperm = get_perm(v->value);
992 else if (!strcasecmp(v->name, "permit") ||
993 !strcasecmp(v->name, "deny")) {
994 ha = ast_append_ha(v->name, v->value, ha, NULL);
995 } else if (!strcasecmp(v->name, "writetimeout")) {
996 int val = atoi(v->value);
999 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
1001 s->writetimeout = val;
1007 ast_config_destroy(cfg);
1009 /* Didn't find the user in manager.conf, check users.conf */
1011 cfg = ast_config_load("users.conf", config_flags);
1014 while ( (cat = ast_category_browse(cfg, cat)) ) {
1015 if (!strcasecmp(cat, user) && strcasecmp(cat, "general"))
1019 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1020 ast_config_destroy(cfg);
1023 /* collect parameters for the user's entry from users.conf */
1024 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
1025 if (!strcasecmp(v->name, "secret"))
1026 password = ast_strdupa(v->value);
1027 else if (!strcasecmp(v->name, "read"))
1028 readperm = get_perm(v->value);
1029 else if (!strcasecmp(v->name, "write"))
1030 writeperm = get_perm(v->value);
1031 else if (!strcasecmp(v->name, "permit") ||
1032 !strcasecmp(v->name, "deny")) {
1033 ha = ast_append_ha(v->name, v->value, ha, NULL);
1034 } else if (!strcasecmp(v->name, "writetimeout")) {
1035 int val = atoi(v->value);
1038 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
1040 s->writetimeout = val;
1041 } else if (!strcasecmp(v->name, "hasmanager"))
1042 hasmanager = ast_true(v->value);
1043 else if (!strcasecmp(v->name, "managerread"))
1044 readperm = get_perm(v->value);
1045 else if (!strcasecmp(v->name, "managerwrite"))
1046 writeperm = get_perm(v->value);
1048 ast_config_destroy(cfg);
1050 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1062 int good = ast_apply_ha(ha, &(s->sin));
1065 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1069 if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
1070 const char *key = astman_get_header(m, "Key");
1071 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) &&
1072 !ast_strlen_zero(password)) {
1075 char md5key[256] = "";
1076 struct MD5Context md5;
1077 unsigned char digest[16];
1080 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1081 MD5Update(&md5, (unsigned char *) password, strlen(password));
1082 MD5Final(digest, &md5);
1083 for (x=0; x<16; x++)
1084 len += sprintf(md5key + len, "%2.2x", digest[x]);
1085 if (!strcmp(md5key, key))
1088 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
1089 S_OR(s->challenge, ""));
1092 } else if (password) {
1093 const char *pass = astman_get_header(m, "Secret");
1094 if (!strcmp(password, pass))
1098 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1101 ast_copy_string(s->username, user, sizeof(s->username));
1102 s->readperm = readperm;
1103 s->writeperm = writeperm;
1104 s->sessionstart = time(NULL);
1105 set_eventmask(s, astman_get_header(m, "Events"));
1109 /*! \brief Manager PING */
1110 static char mandescr_ping[] =
1111 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1112 " manager connection open.\n"
1113 "Variables: NONE\n";
1115 static int action_ping(struct mansession *s, const struct message *m)
1117 astman_send_response(s, m, "Pong", NULL);
1121 static char mandescr_getconfig[] =
1122 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1123 "file by category and contents.\n"
1125 " Filename: Configuration filename (e.g. foo.conf)\n";
1127 static int action_getconfig(struct mansession *s, const struct message *m)
1129 struct ast_config *cfg;
1130 const char *fn = astman_get_header(m, "Filename");
1133 char *category=NULL;
1134 struct ast_variable *v;
1135 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1137 if (ast_strlen_zero(fn)) {
1138 astman_send_error(s, m, "Filename not specified");
1141 if (!(cfg = ast_config_load(fn, config_flags))) {
1142 astman_send_error(s, m, "Config file not found");
1145 astman_start_ack(s, m);
1146 while ((category = ast_category_browse(cfg, category))) {
1148 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1149 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1150 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1153 ast_config_destroy(cfg);
1154 astman_append(s, "\r\n");
1159 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1160 static void json_escape(char *out, const char *in)
1163 if (*in == '\\' || *in == '\"')
1170 static char mandescr_getconfigjson[] =
1171 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1172 "file by category and contents in JSON format. This only makes sense to be used\n"
1173 "using rawman over the HTTP interface.\n"
1175 " Filename: Configuration filename (e.g. foo.conf)\n";
1177 static int action_getconfigjson(struct mansession *s, const struct message *m)
1179 struct ast_config *cfg;
1180 const char *fn = astman_get_header(m, "Filename");
1181 char *category = NULL;
1182 struct ast_variable *v;
1185 unsigned int buf_len = 0;
1186 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1188 if (ast_strlen_zero(fn)) {
1189 astman_send_error(s, m, "Filename not specified");
1193 if (!(cfg = ast_config_load(fn, config_flags))) {
1194 astman_send_error(s, m, "Config file not found");
1199 buf = alloca(buf_len);
1201 astman_start_ack(s, m);
1202 astman_append(s, "JSON: {");
1203 while ((category = ast_category_browse(cfg, category))) {
1205 if (buf_len < 2 * strlen(category) + 1) {
1207 buf = alloca(buf_len);
1209 json_escape(buf, category);
1210 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1213 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1215 astman_append(s, ",");
1216 if (buf_len < 2 * strlen(v->name) + 1) {
1218 buf = alloca(buf_len);
1220 json_escape(buf, v->name);
1221 astman_append(s, "\"%s", buf);
1222 if (buf_len < 2 * strlen(v->value) + 1) {
1224 buf = alloca(buf_len);
1226 json_escape(buf, v->value);
1227 astman_append(s, "%s\"", buf);
1231 astman_append(s, "]");
1233 astman_append(s, "}\r\n\r\n");
1235 ast_config_destroy(cfg);
1240 /* helper function for action_updateconfig */
1241 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1245 const char *action, *cat, *var, *value, *match;
1246 struct ast_category *category;
1247 struct ast_variable *v;
1249 for (x = 0; x < 100000; x++) {
1250 unsigned int object = 0;
1252 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1253 action = astman_get_header(m, hdr);
1254 if (ast_strlen_zero(action))
1256 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1257 cat = astman_get_header(m, hdr);
1258 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1259 var = astman_get_header(m, hdr);
1260 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1261 value = astman_get_header(m, hdr);
1262 if (!ast_strlen_zero(value) && *value == '>') {
1266 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1267 match = astman_get_header(m, hdr);
1268 if (!strcasecmp(action, "newcat")) {
1269 if (!ast_strlen_zero(cat)) {
1270 category = ast_category_new(cat, dfn, 99999);
1272 ast_category_append(cfg, category);
1275 } else if (!strcasecmp(action, "renamecat")) {
1276 if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1277 category = ast_category_get(cfg, cat);
1279 ast_category_rename(category, value);
1281 } else if (!strcasecmp(action, "delcat")) {
1282 if (!ast_strlen_zero(cat))
1283 ast_category_delete(cfg, cat);
1284 } else if (!strcasecmp(action, "update")) {
1285 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1286 ast_variable_update(category, var, value, match, object);
1287 } else if (!strcasecmp(action, "delete")) {
1288 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1289 ast_variable_delete(category, var, match);
1290 } else if (!strcasecmp(action, "append")) {
1291 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1292 (category = ast_category_get(cfg, cat)) &&
1293 (v = ast_variable_new(var, value, dfn))){
1294 if (object || (match && !strcasecmp(match, "object")))
1296 ast_variable_append(category, v);
1302 static char mandescr_updateconfig[] =
1303 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1304 "configuration elements in Asterisk configuration files.\n"
1305 "Variables (X's represent 6 digit number beginning with 000000):\n"
1306 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1307 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1308 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1309 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1310 " Cat-XXXXXX: Category to operate on\n"
1311 " Var-XXXXXX: Variable to work on\n"
1312 " Value-XXXXXX: Value to work on\n"
1313 " Match-XXXXXX: Extra match required to match line\n";
1315 static int action_updateconfig(struct mansession *s, const struct message *m)
1317 struct ast_config *cfg;
1318 const char *sfn = astman_get_header(m, "SrcFilename");
1319 const char *dfn = astman_get_header(m, "DstFilename");
1321 const char *rld = astman_get_header(m, "Reload");
1322 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1324 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1325 astman_send_error(s, m, "Filename not specified");
1328 if (!(cfg = ast_config_load(sfn, config_flags))) {
1329 astman_send_error(s, m, "Config file not found");
1332 handle_updates(s, m, cfg, dfn);
1333 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1334 res = config_text_file_save(dfn, cfg, "Manager");
1335 ast_config_destroy(cfg);
1337 astman_send_error(s, m, "Save of config failed");
1340 astman_send_ack(s, m, NULL);
1341 if (!ast_strlen_zero(rld)) {
1344 ast_module_reload(rld);
1349 /*! \brief Manager WAITEVENT */
1350 static char mandescr_waitevent[] =
1351 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1352 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1353 "session, events will be generated and queued.\n"
1355 " Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1357 static int action_waitevent(struct mansession *s, const struct message *m)
1359 const char *timeouts = astman_get_header(m, "Timeout");
1363 const char *id = astman_get_header(m,"ActionID");
1364 char idText[256] = "";
1366 if (!ast_strlen_zero(id))
1367 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1369 if (!ast_strlen_zero(timeouts)) {
1370 sscanf(timeouts, "%i", &timeout);
1373 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1376 ast_mutex_lock(&s->__lock);
1377 if (s->waiting_thread != AST_PTHREADT_NULL)
1378 pthread_kill(s->waiting_thread, SIGURG);
1380 if (s->managerid) { /* AMI-over-HTTP session */
1382 * Make sure the timeout is within the expire time of the session,
1383 * as the client will likely abort the request if it does not see
1384 * data coming after some amount of time.
1386 time_t now = time(NULL);
1387 int max = s->sessiontimeout - now - 10;
1389 if (max < 0) /* We are already late. Strange but possible. */
1391 if (timeout < 0 || timeout > max)
1393 if (!s->send_events) /* make sure we record events */
1394 s->send_events = -1;
1396 ast_mutex_unlock(&s->__lock);
1398 /* XXX should this go inside the lock ? */
1399 s->waiting_thread = pthread_self(); /* let new events wake up this thread */
1400 ast_debug(1, "Starting waiting for an event!\n");
1402 for (x=0; x < timeout || timeout < 0; x++) {
1403 ast_mutex_lock(&s->__lock);
1406 /* We can have multiple HTTP session point to the same mansession entry.
1407 * The way we deal with it is not very nice: newcomers kick out the previous
1408 * HTTP session. XXX this needs to be improved.
1410 if (s->waiting_thread != pthread_self())
1414 ast_mutex_unlock(&s->__lock);
1417 if (s->managerid == 0) { /* AMI session */
1418 if (ast_wait_for_input(s->fd, 1000))
1420 } else { /* HTTP session */
1424 ast_debug(1, "Finished waiting for an event!\n");
1425 ast_mutex_lock(&s->__lock);
1426 if (s->waiting_thread == pthread_self()) {
1427 struct eventqent *eqe;
1428 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1429 while ( (eqe = NEW_EVENT(s)) ) {
1431 if (((s->readperm & eqe->category) == eqe->category) &&
1432 ((s->send_events & eqe->category) == eqe->category)) {
1433 astman_append(s, "%s", eqe->eventdata);
1435 s->last_ev = unref_event(s->last_ev);
1438 "Event: WaitEventComplete\r\n"
1441 s->waiting_thread = AST_PTHREADT_NULL;
1443 ast_debug(1, "Abandoning event request!\n");
1445 ast_mutex_unlock(&s->__lock);
1449 static char mandescr_listcommands[] =
1450 "Description: Returns the action name and synopsis for every\n"
1451 " action that is available to the user\n"
1452 "Variables: NONE\n";
1454 /*! \note The actionlock is read-locked by the caller of this function */
1455 static int action_listcommands(struct mansession *s, const struct message *m)
1457 struct manager_action *cur;
1458 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1460 astman_start_ack(s, m);
1461 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1462 if ((s->writeperm & cur->authority) == cur->authority)
1463 astman_append(s, "%s: %s (Priv: %s)\r\n",
1464 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1466 astman_append(s, "\r\n");
1471 static char mandescr_events[] =
1472 "Description: Enable/Disable sending of events to this manager\n"
1475 " EventMask: 'on' if all events should be sent,\n"
1476 " 'off' if no events should be sent,\n"
1477 " 'system,call,log' to select which flags events should have to be sent.\n";
1479 static int action_events(struct mansession *s, const struct message *m)
1481 const char *mask = astman_get_header(m, "EventMask");
1484 res = set_eventmask(s, mask);
1486 astman_send_response(s, m, "Events On", NULL);
1488 astman_send_response(s, m, "Events Off", NULL);
1493 static char mandescr_logoff[] =
1494 "Description: Logoff this manager session\n"
1495 "Variables: NONE\n";
1497 static int action_logoff(struct mansession *s, const struct message *m)
1499 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1503 static int action_login(struct mansession *s, const struct message *m)
1505 if (authenticate(s, m)) {
1507 astman_send_error(s, m, "Authentication failed");
1510 s->authenticated = 1;
1511 if (manager_displayconnects(s))
1512 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1513 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1514 astman_send_ack(s, m, "Authentication accepted");
1518 static int action_challenge(struct mansession *s, const struct message *m)
1520 const char *authtype = astman_get_header(m, "AuthType");
1522 if (!strcasecmp(authtype, "MD5")) {
1523 if (ast_strlen_zero(s->challenge))
1524 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1525 ast_mutex_lock(&s->__lock);
1526 astman_start_ack(s, m);
1527 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1528 ast_mutex_unlock(&s->__lock);
1530 astman_send_error(s, m, "Must specify AuthType");
1535 static char mandescr_hangup[] =
1536 "Description: Hangup a channel\n"
1538 " Channel: The channel name to be hungup\n";
1540 static int action_hangup(struct mansession *s, const struct message *m)
1542 struct ast_channel *c = NULL;
1543 const char *name = astman_get_header(m, "Channel");
1544 if (ast_strlen_zero(name)) {
1545 astman_send_error(s, m, "No channel specified");
1548 c = ast_get_channel_by_name_locked(name);
1550 astman_send_error(s, m, "No such channel");
1553 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1554 ast_channel_unlock(c);
1555 astman_send_ack(s, m, "Channel Hungup");
1559 static char mandescr_setvar[] =
1560 "Description: Set a global or local channel variable.\n"
1561 "Variables: (Names marked with * are required)\n"
1562 " Channel: Channel to set variable for\n"
1563 " *Variable: Variable name\n"
1566 static int action_setvar(struct mansession *s, const struct message *m)
1568 struct ast_channel *c = NULL;
1569 const char *name = astman_get_header(m, "Channel");
1570 const char *varname = astman_get_header(m, "Variable");
1571 const char *varval = astman_get_header(m, "Value");
1573 if (ast_strlen_zero(varname)) {
1574 astman_send_error(s, m, "No variable specified");
1578 if (!ast_strlen_zero(name)) {
1579 c = ast_get_channel_by_name_locked(name);
1581 astman_send_error(s, m, "No such channel");
1586 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1589 ast_channel_unlock(c);
1591 astman_send_ack(s, m, "Variable Set");
1596 static char mandescr_getvar[] =
1597 "Description: Get the value of a global or local channel variable.\n"
1598 "Variables: (Names marked with * are required)\n"
1599 " Channel: Channel to read variable from\n"
1600 " *Variable: Variable name\n"
1601 " ActionID: Optional Action id for message matching.\n";
1603 static int action_getvar(struct mansession *s, const struct message *m)
1605 struct ast_channel *c = NULL;
1606 const char *name = astman_get_header(m, "Channel");
1607 const char *varname = astman_get_header(m, "Variable");
1609 char workspace[1024] = "";
1611 if (ast_strlen_zero(varname)) {
1612 astman_send_error(s, m, "No variable specified");
1616 if (!ast_strlen_zero(name)) {
1617 c = ast_get_channel_by_name_locked(name);
1619 astman_send_error(s, m, "No such channel");
1624 if (varname[strlen(varname) - 1] == ')') {
1625 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1628 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1632 ast_channel_unlock(c);
1633 astman_start_ack(s, m);
1634 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1640 /*! \brief Manager "status" command to show channels */
1641 /* Needs documentation... */
1642 static int action_status(struct mansession *s, const struct message *m)
1644 const char *name = astman_get_header(m,"Channel");
1645 struct ast_channel *c;
1647 struct timeval now = ast_tvnow();
1648 long elapsed_seconds = 0;
1649 int all = ast_strlen_zero(name); /* set if we want all channels */
1650 const char *id = astman_get_header(m,"ActionID");
1651 char idText[256] = "";
1653 if (!ast_strlen_zero(id))
1654 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1657 c = ast_channel_walk_locked(NULL);
1659 c = ast_get_channel_by_name_locked(name);
1661 astman_send_error(s, m, "No such channel");
1665 astman_send_ack(s, m, "Channel status will follow");
1666 /* if we look by name, we break after the first iteration */
1669 snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1674 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1678 "Privilege: Call\r\n"
1680 "CallerIDNum: %s\r\n"
1681 "CallerIDName: %s\r\n"
1693 S_OR(c->cid.cid_num, "<unknown>"),
1694 S_OR(c->cid.cid_name, "<unknown>"),
1696 ast_state2str(c->_state), c->context,
1697 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1701 "Privilege: Call\r\n"
1703 "CallerIDNum: %s\r\n"
1704 "CallerIDName: %s\r\n"
1712 S_OR(c->cid.cid_num, "<unknown>"),
1713 S_OR(c->cid.cid_name, "<unknown>"),
1715 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1717 ast_channel_unlock(c);
1720 c = ast_channel_walk_locked(c);
1723 "Event: StatusComplete\r\n"
1729 static char mandescr_sendtext[] =
1730 "Description: Sends A Text Message while in a call.\n"
1731 "Variables: (Names marked with * are required)\n"
1732 " *Channel: Channel to send message to\n"
1733 " *Message: Message to send\n"
1734 " ActionID: Optional Action id for message matching.\n";
1736 static int action_sendtext(struct mansession *s, const struct message *m)
1738 struct ast_channel *c = NULL;
1739 const char *name = astman_get_header(m, "Channel");
1740 const char *textmsg = astman_get_header(m, "Message");
1743 if (ast_strlen_zero(name)) {
1744 astman_send_error(s, m, "No channel specified");
1748 if (ast_strlen_zero(textmsg)) {
1749 astman_send_error(s, m, "No Message specified");
1753 c = ast_get_channel_by_name_locked(name);
1755 astman_send_error(s, m, "No such channel");
1759 res = ast_sendtext(c, textmsg);
1760 ast_channel_unlock(c);
1763 astman_send_ack(s, m, "Success");
1765 astman_send_error(s, m, "Failure");
1770 static char mandescr_redirect[] =
1771 "Description: Redirect (transfer) a call.\n"
1772 "Variables: (Names marked with * are required)\n"
1773 " *Channel: Channel to redirect\n"
1774 " ExtraChannel: Second call leg to transfer (optional)\n"
1775 " *Exten: Extension to transfer to\n"
1776 " *Context: Context to transfer to\n"
1777 " *Priority: Priority to transfer to\n"
1778 " ActionID: Optional Action id for message matching.\n";
1780 /*! \brief action_redirect: The redirect manager command */
1781 static int action_redirect(struct mansession *s, const struct message *m)
1783 const char *name = astman_get_header(m, "Channel");
1784 const char *name2 = astman_get_header(m, "ExtraChannel");
1785 const char *exten = astman_get_header(m, "Exten");
1786 const char *context = astman_get_header(m, "Context");
1787 const char *priority = astman_get_header(m, "Priority");
1788 struct ast_channel *chan, *chan2 = NULL;
1792 if (ast_strlen_zero(name)) {
1793 astman_send_error(s, m, "Channel not specified");
1796 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1797 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1798 astman_send_error(s, m, "Invalid priority\n");
1802 /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1803 chan = ast_get_channel_by_name_locked(name);
1806 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1807 astman_send_error(s, m, buf);
1810 if (ast_check_hangup(chan)) {
1811 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1812 ast_channel_unlock(chan);
1815 if (!ast_strlen_zero(name2))
1816 chan2 = ast_get_channel_by_name_locked(name2);
1817 if (chan2 && ast_check_hangup(chan2)) {
1818 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1819 ast_channel_unlock(chan);
1820 ast_channel_unlock(chan2);
1823 res = ast_async_goto(chan, context, exten, pi);
1825 if (!ast_strlen_zero(name2)) {
1827 res = ast_async_goto(chan2, context, exten, pi);
1831 astman_send_ack(s, m, "Dual Redirect successful");
1833 astman_send_error(s, m, "Secondary redirect failed");
1835 astman_send_ack(s, m, "Redirect successful");
1837 astman_send_error(s, m, "Redirect failed");
1839 ast_channel_unlock(chan);
1841 ast_channel_unlock(chan2);
1845 static char mandescr_command[] =
1846 "Description: Run a CLI command.\n"
1847 "Variables: (Names marked with * are required)\n"
1848 " *Command: Asterisk CLI command to run\n"
1849 " ActionID: Optional Action id for message matching.\n";
1851 /*! \brief Manager command "command" - execute CLI command */
1852 static int action_command(struct mansession *s, const struct message *m)
1854 const char *cmd = astman_get_header(m, "Command");
1855 const char *id = astman_get_header(m, "ActionID");
1856 char *buf, *final_buf;
1857 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1858 int fd = mkstemp(template), i = 0;
1861 for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
1862 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
1863 astman_send_error(s, m, "Command blacklisted");
1868 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1869 if (!ast_strlen_zero(id))
1870 astman_append(s, "ActionID: %s\r\n", id);
1871 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1872 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
1873 l = lseek(fd, 0, SEEK_END); /* how many chars available */
1875 /* This has a potential to overflow the stack. Hence, use the heap. */
1876 buf = ast_calloc(1, l + 1);
1877 final_buf = ast_calloc(1, l + 1);
1879 lseek(fd, 0, SEEK_SET);
1883 term_strip(final_buf, buf, l);
1884 final_buf[l] = '\0';
1886 astman_append(s, S_OR(final_buf, buf));
1891 astman_append(s, "--END COMMAND--\r\n\r\n");
1893 ast_free(final_buf);
1897 /* helper function for originate */
1898 struct fast_originate_helper {
1899 char tech[AST_MAX_EXTENSION];
1900 char data[AST_MAX_EXTENSION];
1902 char app[AST_MAX_APP];
1903 char appdata[AST_MAX_EXTENSION];
1904 char cid_name[AST_MAX_EXTENSION];
1905 char cid_num[AST_MAX_EXTENSION];
1906 char context[AST_MAX_CONTEXT];
1907 char exten[AST_MAX_EXTENSION];
1908 char idtext[AST_MAX_EXTENSION];
1909 char account[AST_MAX_ACCOUNT_CODE];
1911 struct ast_variable *vars;
1914 static void *fast_originate(void *data)
1916 struct fast_originate_helper *in = data;
1919 struct ast_channel *chan = NULL;
1920 char requested_channel[AST_CHANNEL_NAME];
1922 if (!ast_strlen_zero(in->app)) {
1923 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1924 S_OR(in->cid_num, NULL),
1925 S_OR(in->cid_name, NULL),
1926 in->vars, in->account, &chan);
1928 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1929 S_OR(in->cid_num, NULL),
1930 S_OR(in->cid_name, NULL),
1931 in->vars, in->account, &chan);
1935 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
1936 /* Tell the manager what happened with the channel */
1937 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1945 "CallerIDNum: %s\r\n"
1946 "CallerIDName: %s\r\n",
1947 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
1948 chan ? chan->uniqueid : "<null>",
1949 S_OR(in->cid_num, "<unknown>"),
1950 S_OR(in->cid_name, "<unknown>")
1953 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1955 ast_channel_unlock(chan);
1960 static char mandescr_originate[] =
1961 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1962 " Application/Data\n"
1963 "Variables: (Names marked with * are required)\n"
1964 " *Channel: Channel name to call\n"
1965 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1966 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1967 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1968 " Application: Application to use\n"
1969 " Data: Data to use (requires 'Application')\n"
1970 " Timeout: How long to wait for call to be answered (in ms)\n"
1971 " CallerID: Caller ID to be set on the outgoing channel\n"
1972 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1973 " Account: Account code\n"
1974 " Async: Set to 'true' for fast origination\n";
1976 static int action_originate(struct mansession *s, const struct message *m)
1978 const char *name = astman_get_header(m, "Channel");
1979 const char *exten = astman_get_header(m, "Exten");
1980 const char *context = astman_get_header(m, "Context");
1981 const char *priority = astman_get_header(m, "Priority");
1982 const char *timeout = astman_get_header(m, "Timeout");
1983 const char *callerid = astman_get_header(m, "CallerID");
1984 const char *account = astman_get_header(m, "Account");
1985 const char *app = astman_get_header(m, "Application");
1986 const char *appdata = astman_get_header(m, "Data");
1987 const char *async = astman_get_header(m, "Async");
1988 const char *id = astman_get_header(m, "ActionID");
1989 struct ast_variable *vars = astman_get_variables(m);
1991 char *l = NULL, *n = NULL;
2001 astman_send_error(s, m, "Channel not specified");
2004 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2005 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2006 astman_send_error(s, m, "Invalid priority\n");
2010 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2011 astman_send_error(s, m, "Invalid timeout\n");
2014 ast_copy_string(tmp, name, sizeof(tmp));
2016 data = strchr(tmp, '/');
2018 astman_send_error(s, m, "Invalid channel\n");
2022 ast_copy_string(tmp2, callerid, sizeof(tmp2));
2023 ast_callerid_parse(tmp2, &n, &l);
2025 if (ast_strlen_zero(n))
2029 ast_shrink_phone_number(l);
2030 if (ast_strlen_zero(l))
2033 if (ast_true(async)) {
2034 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2038 if (!ast_strlen_zero(id))
2039 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2040 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2041 ast_copy_string(fast->data, data, sizeof(fast->data));
2042 ast_copy_string(fast->app, app, sizeof(fast->app));
2043 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2045 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2047 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2049 ast_copy_string(fast->context, context, sizeof(fast->context));
2050 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2051 ast_copy_string(fast->account, account, sizeof(fast->account));
2053 fast->priority = pi;
2054 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2060 } else if (!ast_strlen_zero(app)) {
2061 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2063 if (exten && context && pi)
2064 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2066 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2071 astman_send_ack(s, m, "Originate successfully queued");
2073 astman_send_error(s, m, "Originate failed");
2077 /*! \brief Help text for manager command mailboxstatus
2079 static char mandescr_mailboxstatus[] =
2080 "Description: Checks a voicemail account for status.\n"
2081 "Variables: (Names marked with * are required)\n"
2082 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2083 " ActionID: Optional ActionID for message matching.\n"
2084 "Returns number of messages.\n"
2085 " Message: Mailbox Status\n"
2086 " Mailbox: <mailboxid>\n"
2087 " Waiting: <count>\n"
2090 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2092 const char *mailbox = astman_get_header(m, "Mailbox");
2095 if (ast_strlen_zero(mailbox)) {
2096 astman_send_error(s, m, "Mailbox not specified");
2099 ret = ast_app_has_voicemail(mailbox, NULL);
2100 astman_start_ack(s, m);
2101 astman_append(s, "Message: Mailbox Status\r\n"
2103 "Waiting: %d\r\n\r\n", mailbox, ret);
2107 static char mandescr_mailboxcount[] =
2108 "Description: Checks a voicemail account for new messages.\n"
2109 "Variables: (Names marked with * are required)\n"
2110 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2111 " ActionID: Optional ActionID for message matching.\n"
2112 "Returns number of new and old messages.\n"
2113 " Message: Mailbox Message Count\n"
2114 " Mailbox: <mailboxid>\n"
2115 " NewMessages: <count>\n"
2116 " OldMessages: <count>\n"
2118 static int action_mailboxcount(struct mansession *s, const struct message *m)
2120 const char *mailbox = astman_get_header(m, "Mailbox");
2121 int newmsgs = 0, oldmsgs = 0;
2123 if (ast_strlen_zero(mailbox)) {
2124 astman_send_error(s, m, "Mailbox not specified");
2127 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2128 astman_start_ack(s, m);
2129 astman_append(s, "Message: Mailbox Message Count\r\n"
2131 "NewMessages: %d\r\n"
2132 "OldMessages: %d\r\n"
2134 mailbox, newmsgs, oldmsgs);
2138 static char mandescr_extensionstate[] =
2139 "Description: Report the extension state for given extension.\n"
2140 " If the extension has a hint, will use devicestate to check\n"
2141 " the status of the device connected to the extension.\n"
2142 "Variables: (Names marked with * are required)\n"
2143 " *Exten: Extension to check state on\n"
2144 " *Context: Context for extension\n"
2145 " ActionId: Optional ID for this transaction\n"
2146 "Will return an \"Extension Status\" message.\n"
2147 "The response will include the hint for the extension and the status.\n";
2149 static int action_extensionstate(struct mansession *s, const struct message *m)
2151 const char *exten = astman_get_header(m, "Exten");
2152 const char *context = astman_get_header(m, "Context");
2153 char hint[256] = "";
2155 if (ast_strlen_zero(exten)) {
2156 astman_send_error(s, m, "Extension not specified");
2159 if (ast_strlen_zero(context))
2160 context = "default";
2161 status = ast_extension_state(NULL, context, exten);
2162 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2163 astman_start_ack(s, m);
2164 astman_append(s, "Message: Extension Status\r\n"
2168 "Status: %d\r\n\r\n",
2169 exten, context, hint, status);
2173 static char mandescr_timeout[] =
2174 "Description: Hangup a channel after a certain time.\n"
2175 "Variables: (Names marked with * are required)\n"
2176 " *Channel: Channel name to hangup\n"
2177 " *Timeout: Maximum duration of the call (sec)\n"
2178 "Acknowledges set time with 'Timeout Set' message\n";
2180 static int action_timeout(struct mansession *s, const struct message *m)
2182 struct ast_channel *c;
2183 const char *name = astman_get_header(m, "Channel");
2184 int timeout = atoi(astman_get_header(m, "Timeout"));
2186 if (ast_strlen_zero(name)) {
2187 astman_send_error(s, m, "No channel specified");
2191 astman_send_error(s, m, "No timeout specified");
2194 c = ast_get_channel_by_name_locked(name);
2196 astman_send_error(s, m, "No such channel");
2199 ast_channel_setwhentohangup(c, timeout);
2200 ast_channel_unlock(c);
2201 astman_send_ack(s, m, "Timeout Set");
2206 * Send any applicable events to the client listening on this socket.
2207 * Wait only for a finite time on each event, and drop all events whether
2208 * they are successfully sent or not.
2210 static int process_events(struct mansession *s)
2214 ast_mutex_lock(&s->__lock);
2216 struct eventqent *eqe;
2218 while ( (eqe = NEW_EVENT(s)) ) {
2220 if (!ret && s->authenticated &&
2221 (s->readperm & eqe->category) == eqe->category &&
2222 (s->send_events & eqe->category) == eqe->category) {
2223 if (send_string(s, eqe->eventdata) < 0)
2224 ret = -1; /* don't send more */
2226 s->last_ev = unref_event(s->last_ev);
2229 ast_mutex_unlock(&s->__lock);
2233 static char mandescr_userevent[] =
2234 "Description: Send an event to manager sessions.\n"
2235 "Variables: (Names marked with * are required)\n"
2236 " *UserEvent: EventStringToSend\n"
2237 " Header1: Content1\n"
2238 " HeaderN: ContentN\n";
2240 static int action_userevent(struct mansession *s, const struct message *m)
2242 const char *event = astman_get_header(m, "UserEvent");
2243 char body[2048] = "";
2245 for (x = 0; x < m->hdrcount; x++) {
2246 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2247 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2248 bodylen += strlen(m->headers[x]);
2249 ast_copy_string(body + bodylen, "\r\n", 3);
2254 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2258 static char mandescr_coresettings[] =
2259 "Description: Query for Core PBX settings.\n"
2260 "Variables: (Names marked with * are optional)\n"
2261 " *ActionID: ActionID of this transaction\n";
2263 /*! \brief Show PBX core settings information */
2264 static int action_coresettings(struct mansession *s, const struct message *m)
2266 const char *actionid = astman_get_header(m, "ActionID");
2267 char idText[150] = "";
2269 if (!ast_strlen_zero(actionid))
2270 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2272 astman_append(s, "Response: Success\r\n"
2274 "AMIversion: %s\r\n"
2275 "AsteriskVersion: %s\r\n"
2276 "SystemName: %s\r\n"
2277 "CoreMaxCalls: %d\r\n"
2278 "CoreMaxLoadAvg: %f\r\n"
2279 "CoreRunUser: %s\r\n"
2280 "CoreRunGroup: %s\r\n"
2281 "CoreMaxFilehandles: %d\r\n"
2282 "CoreRealTimeEnabled: %s\r\n"
2283 "CoreCDRenabled: %s\r\n"
2284 "CoreHTTPenabled: %s\r\n"
2289 ast_config_AST_SYSTEM_NAME,
2292 ast_config_AST_RUN_USER,
2293 ast_config_AST_RUN_GROUP,
2295 ast_realtime_enabled() ? "Yes" : "No",
2296 check_cdr_enabled() ? "Yes" : "No",
2297 check_webmanager_enabled() ? "Yes" : "No"
2302 static char mandescr_corestatus[] =
2303 "Description: Query for Core PBX status.\n"
2304 "Variables: (Names marked with * are optional)\n"
2305 " *ActionID: ActionID of this transaction\n";
2307 /*! \brief Show PBX core status information */
2308 static int action_corestatus(struct mansession *s, const struct message *m)
2310 const char *actionid = astman_get_header(m, "ActionID");
2312 char startuptime[150];
2313 char reloadtime[150];
2316 if (!ast_strlen_zero(actionid))
2317 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2319 ast_localtime(&ast_startuptime, &tm, NULL);
2320 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2321 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2322 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2324 astman_append(s, "Response: Success\r\n"
2326 "CoreStartupTime: %s\r\n"
2327 "CoreReloadTime: %s\r\n"
2328 "CoreCurrentCalls: %d\r\n"
2333 ast_active_channels()
2338 static char mandescr_reload[] =
2339 "Description: Send a reload event.\n"
2340 "Variables: (Names marked with * are optional)\n"
2341 " *ActionID: ActionID of this transaction\n"
2342 " *Module: Name of the module to reload\n";
2344 /*! \brief Send a reload event */
2345 static int action_reload(struct mansession *s, const struct message *m)
2347 const char *actionid = astman_get_header(m, "ActionID");
2348 const char *module = astman_get_header(m, "Module");
2349 int res = ast_module_reload(S_OR(module, NULL));
2350 char idText[80] = "";
2352 if (!ast_strlen_zero(actionid))
2353 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2355 astman_append(s, "Response: Success\r\n%s", idText);
2357 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2361 static char mandescr_coreshowchannels[] =
2362 "Description: List currently defined channels and some information\n"
2365 " ActionID: Optional Action id for message matching.\n";
2367 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2368 * and some information about them. */
2369 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2371 const char *actionid = astman_get_header(m, "ActionID");
2372 char actionidtext[256] = "";
2373 struct ast_channel *c = NULL;
2375 int duration, durh, durm, durs;
2377 if (!ast_strlen_zero(actionid))
2378 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2380 astman_send_listack(s, m, "Channels will follow", "start");
2382 while ((c = ast_channel_walk_locked(c)) != NULL) {
2383 struct ast_channel *bc = ast_bridged_channel(c);
2384 char durbuf[10] = "";
2386 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2387 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2388 durh = duration / 3600;
2389 durm = (duration % 3600) / 60;
2390 durs = duration % 60;
2391 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2400 "ChannelState: %d\r\n"
2401 "ChannelStateDesc: %s\r\n"
2402 "Application: %s\r\n"
2403 "ApplicationData: %s\r\n"
2404 "CallerIDnum: %s\r\n"
2406 "AccountCode: %s\r\n"
2407 "BridgedChannel: %s\r\n"
2408 "BridgedUniqueID: %s\r\n"
2409 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2410 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2411 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2412 ast_channel_unlock(c);
2417 "Event: CoreShowChannelsComplete\r\n"
2418 "EventList: Complete\r\n"
2421 "\r\n", numchans, actionidtext);
2428 * Done with the action handlers here, we start with the code in charge
2429 * of accepting connections and serving them.
2430 * accept_thread() forks a new thread for each connection, session_do(),
2431 * which in turn calls get_input() repeatedly until a full message has
2432 * been accumulated, and then invokes process_message() to pass it to
2433 * the appropriate handler.
2437 * Process an AMI message, performing desired action.
2438 * Return 0 on success, -1 on error that require the session to be destroyed.
2440 static int process_message(struct mansession *s, const struct message *m)
2442 char action[80] = "";
2444 struct manager_action *tmp;
2445 const char *user = astman_get_header(m, "Username");
2447 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2448 ast_debug(1, "Manager received command '%s'\n", action);
2450 if (ast_strlen_zero(action)) {
2451 ast_mutex_lock(&s->__lock);
2452 astman_send_error(s, m, "Missing action in request");
2453 ast_mutex_unlock(&s->__lock);
2457 if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2458 ast_mutex_lock(&s->__lock);
2459 astman_send_error(s, m, "Permission denied");
2460 ast_mutex_unlock(&s->__lock);
2464 if (!allowmultiplelogin && !s->authenticated && user &&
2465 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2466 if (check_manager_session_inuse(user)) {
2468 ast_mutex_lock(&s->__lock);
2469 astman_send_error(s, m, "Login Already In Use");
2470 ast_mutex_unlock(&s->__lock);
2475 AST_RWLIST_RDLOCK(&actions);
2476 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2477 if (strcasecmp(action, tmp->action))
2479 if ((s->writeperm & tmp->authority) == tmp->authority)
2480 ret = tmp->func(s, m);
2482 astman_send_error(s, m, "Permission denied");
2485 AST_RWLIST_UNLOCK(&actions);
2488 ast_mutex_lock(&s->__lock);
2489 astman_send_error(s, m, "Invalid/unknown command. Use Action: ListCommands to show available commands.");
2490 ast_mutex_unlock(&s->__lock);
2494 /* Once done with our message, deliver any pending events */
2495 return process_events(s);
2499 * Read one full line (including crlf) from the manager socket.
2501 * \r\n is the only valid terminator for the line.
2502 * (Note that, later, '\0' will be considered as the end-of-line marker,
2503 * so everything between the '\0' and the '\r\n' will not be used).
2504 * Also note that we assume output to have at least "maxlen" space.
2507 static int get_input(struct mansession *s, char *output)
2510 int maxlen = sizeof(s->inbuf) - 1;
2511 char *src = s->inbuf;
2514 * Look for \r\n within the buffer. If found, copy to the output
2515 * buffer and return, trimming the \r\n (not used afterwards).
2517 for (x = 0; x < s->inlen; x++) {
2518 int cr; /* set if we have \r */
2519 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2520 cr = 2; /* Found. Update length to include \r\n */
2521 else if (src[x] == '\n')
2522 cr = 1; /* also accept \n only */
2525 memmove(output, src, x); /*... but trim \r\n */
2526 output[x] = '\0'; /* terminate the string */
2527 x += cr; /* number of bytes used */
2528 s->inlen -= x; /* remaining size */
2529 memmove(src, src + x, s->inlen); /* remove used bytes */
2532 if (s->inlen >= maxlen) {
2533 /* no crlf found, and buffer full - sorry, too long for us */
2534 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2539 /* XXX do we really need this locking ? */
2540 ast_mutex_lock(&s->__lock);
2541 s->waiting_thread = pthread_self();
2542 ast_mutex_unlock(&s->__lock);
2544 res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
2546 ast_mutex_lock(&s->__lock);
2547 s->waiting_thread = AST_PTHREADT_NULL;
2548 ast_mutex_unlock(&s->__lock);
2551 /* If we get a signal from some other thread (typically because
2552 * there are new events queued), return 0 to notify the caller.
2556 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2559 ast_mutex_lock(&s->__lock);
2560 res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2562 res = -1; /* error return */
2565 src[s->inlen] = '\0';
2568 ast_mutex_unlock(&s->__lock);
2572 static int do_message(struct mansession *s)
2574 struct message m = { 0 };
2575 char header_buf[sizeof(s->inbuf)] = { '\0' };
2579 /* Check if any events are pending and do them if needed */
2580 if (process_events(s))
2582 res = get_input(s, header_buf);
2585 } else if (res > 0) {
2586 if (ast_strlen_zero(header_buf))
2587 return process_message(s, &m) ? -1 : 0;
2588 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2589 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2596 /*! \brief The body of the individual manager session.
2597 * Call get_input() to read one line at a time
2598 * (or be woken up on new events), collect the lines in a
2599 * message until found an empty line, and execute the request.
2600 * In any case, deliver events asynchronously through process_events()
2601 * (called from here if no line is available, or at the end of
2602 * process_message(). )
2604 static void *session_do(void *data)
2606 struct server_instance *ser = data;
2607 struct mansession *s = ast_calloc(1, sizeof(*s));
2614 s->writetimeout = 100;
2615 s->waiting_thread = AST_PTHREADT_NULL;
2617 flags = fcntl(ser->fd, F_GETFL);
2618 if (!block_sockets) /* make sure socket is non-blocking */
2619 flags |= O_NONBLOCK;
2621 flags &= ~O_NONBLOCK;
2622 fcntl(ser->fd, F_SETFL, flags);
2624 ast_mutex_init(&s->__lock);
2625 s->send_events = -1;
2626 /* these fields duplicate those in the 'ser' structure */
2629 s->sin = ser->requestor;
2631 AST_LIST_LOCK(&sessions);
2632 AST_LIST_INSERT_HEAD(&sessions, s, list);
2633 ast_atomic_fetchadd_int(&num_sessions, 1);
2634 AST_LIST_UNLOCK(&sessions);
2635 /* Hook to the tail of the event queue */
2636 s->last_ev = grab_last();
2638 astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
2640 if ((res = do_message(s)) < 0)
2643 /* session is over, explain why and terminate */
2644 if (s->authenticated) {
2645 if (manager_displayconnects(s))
2646 ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2647 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2649 if (displayconnects)
2650 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2651 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2660 /*! \brief remove at most n_max stale session from the list. */
2661 static void purge_sessions(int n_max)
2663 struct mansession *s;
2664 time_t now = time(NULL);
2666 AST_LIST_LOCK(&sessions);
2667 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2668 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2669 AST_LIST_REMOVE_CURRENT(list);
2670 ast_atomic_fetchadd_int(&num_sessions, -1);
2671 if (s->authenticated && (option_verbose > 1) && manager_displayconnects(s)) {
2672 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
2673 s->username, ast_inet_ntoa(s->sin.sin_addr));
2675 free_session(s); /* XXX outside ? */
2680 AST_LIST_TRAVERSE_SAFE_END;
2681 AST_LIST_UNLOCK(&sessions);
2685 * events are appended to a queue from where they
2686 * can be dispatched to clients.
2688 static int append_event(const char *str, int category)
2690 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2691 static int seq; /* sequence number */
2696 /* need to init all fields, because ast_malloc() does not */
2698 tmp->category = category;
2699 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2700 AST_LIST_NEXT(tmp, eq_next) = NULL;
2701 strcpy(tmp->eventdata, str);
2703 AST_LIST_LOCK(&all_events);
2704 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
2705 AST_LIST_UNLOCK(&all_events);
2710 /* XXX see if can be moved inside the function */
2711 AST_THREADSTORAGE(manager_event_buf);
2712 #define MANAGER_EVENT_BUF_INITSIZE 256
2714 /*! \brief manager_event: Send AMI event to client */
2715 int __manager_event(int category, const char *event,
2716 const char *file, int line, const char *func, const char *fmt, ...)
2718 struct mansession *s;
2719 struct manager_custom_hook *hook;
2720 struct ast_str *auth = ast_str_alloca(80);
2721 const char *cat_str;
2724 struct ast_str *buf;
2726 /* Abort if there aren't any manager sessions */
2730 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2733 cat_str = authority_to_str(category, &auth);
2734 ast_str_set(&buf, 0,
2735 "Event: %s\r\nPrivilege: %s\r\n",
2738 if (timestampevents) {
2740 ast_str_append(&buf, 0,
2741 "Timestamp: %ld.%06lu\r\n",
2742 now.tv_sec, (unsigned long) now.tv_usec);
2744 if (manager_debug) {
2746 ast_str_append(&buf, 0,
2747 "SequenceNumber: %d\r\n",
2748 ast_atomic_fetchadd_int(&seq, 1));
2749 ast_str_append(&buf, 0,
2750 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
2754 ast_str_append_va(&buf, 0, fmt, ap);
2757 ast_str_append(&buf, 0, "\r\n");
2759 append_event(buf->str, category);
2761 /* Wake up any sleeping sessions */
2762 AST_LIST_LOCK(&sessions);
2763 AST_LIST_TRAVERSE(&sessions, s, list) {
2764 ast_mutex_lock(&s->__lock);
2765 if (s->waiting_thread != AST_PTHREADT_NULL)
2766 pthread_kill(s->waiting_thread, SIGURG);
2767 ast_mutex_unlock(&s->__lock);
2769 AST_LIST_UNLOCK(&sessions);
2771 AST_RWLIST_RDLOCK(&manager_hooks);
2772 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
2773 hook->helper(category, event, buf->str);
2775 AST_RWLIST_UNLOCK(&manager_hooks);
2781 * support functions to register/unregister AMI action handlers,
2783 int ast_manager_unregister(char *action)
2785 struct manager_action *cur;
2787 AST_RWLIST_WRLOCK(&actions);
2788 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
2789 if (!strcasecmp(action, cur->action)) {
2790 AST_RWLIST_REMOVE_CURRENT(list);
2792 ast_verb(2, "Manager unregistered action %s\n", action);
2796 AST_RWLIST_TRAVERSE_SAFE_END;
2797 AST_RWLIST_UNLOCK(&actions);
2802 static int manager_state_cb(char *context, char *exten, int state, void *data)
2804 /* Notify managers of change */
2806 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
2808 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
2812 static int ast_manager_register_struct(struct manager_action *act)
2814 struct manager_action *cur, *prev = NULL;
2816 AST_RWLIST_WRLOCK(&actions);
2817 AST_RWLIST_TRAVERSE(&actions, cur, list) {
2818 int ret = strcasecmp(cur->action, act->action);
2820 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2821 AST_RWLIST_UNLOCK(&actions);
2824 if (ret > 0) { /* Insert these alphabetically */
2831 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
2833 AST_RWLIST_INSERT_HEAD(&actions, act, list);
2835 ast_verb(2, "Manager registered action %s\n", act->action);
2837 AST_RWLIST_UNLOCK(&actions);
2842 /*! \brief register a new command with manager, including online help. This is
2843 the preferred way to register a manager command */
2844 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2846 struct manager_action *cur = NULL;
2848 if (!(cur = ast_calloc(1, sizeof(*cur))))
2851 cur->action = action;
2852 cur->authority = auth;
2854 cur->synopsis = synopsis;
2855 cur->description = description;
2857 ast_manager_register_struct(cur);
2862 END Doxygen group */
2865 * The following are support functions for AMI-over-http.
2866 * The common entry point is generic_http_callback(),
2867 * which extracts HTTP header and URI fields and reformats
2868 * them into AMI messages, locates a proper session
2869 * (using the mansession_id Cookie or GET variable),
2870 * and calls process_message() as for regular AMI clients.
2871 * When done, the output (which goes to a temporary file)
2872 * is read back into a buffer and reformatted as desired,
2873 * then fed back to the client over the original socket.
2876 enum output_format {
2882 static char *contenttype[] = {
2883 [FORMAT_RAW] = "plain",
2884 [FORMAT_HTML] = "html",
2885 [FORMAT_XML] = "xml",
2889 * locate an http session in the list. The search key (ident) is
2890 * the value of the mansession_id cookie (0 is not valid and means
2891 * a session on the AMI socket).
2893 static struct mansession *find_session(unsigned long ident)
2895 struct mansession *s;
2900 AST_LIST_LOCK(&sessions);
2901 AST_LIST_TRAVERSE(&sessions, s, list) {
2902 ast_mutex_lock(&s->__lock);
2903 if (s->managerid == ident && !s->needdestroy) {
2904 ast_atomic_fetchadd_int(&s->inuse, 1);
2907 ast_mutex_unlock(&s->__lock);
2909 AST_LIST_UNLOCK(&sessions);
2914 int astman_verify_session_readpermissions(unsigned long ident, int perm)
2917 struct mansession *s;
2919 AST_LIST_LOCK(&sessions);
2920 AST_LIST_TRAVERSE(&sessions, s, list) {
2921 ast_mutex_lock(&s->__lock);
2922 if ((s->managerid == ident) && (s->readperm & perm)) {
2924 ast_mutex_unlock(&s->__lock);
2927 ast_mutex_unlock(&s->__lock);
2929 AST_LIST_UNLOCK(&sessions);
2933 int astman_verify_session_writepermissions(unsigned long ident, int perm)
2936 struct mansession *s;
2938 AST_LIST_LOCK(&sessions);
2939 AST_LIST_TRAVERSE(&sessions, s, list) {
2940 ast_mutex_lock(&s->__lock);
2941 if ((s->managerid == ident) && (s->writeperm & perm)) {
2943 ast_mutex_unlock(&s->__lock);
2946 ast_mutex_unlock(&s->__lock);
2948 AST_LIST_UNLOCK(&sessions);
2953 * convert to xml with various conversion:
2954 * mode & 1 -> lowercase;
2955 * mode & 2 -> replace non-alphanumeric chars with underscore
2957 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
2959 /* store in a local buffer to avoid calling ast_str_append too often */
2962 int space = sizeof(buf);
2963 /* repeat until done and nothing to flush */
2964 for ( ; *src || dst != buf ; src++) {
2965 if (*src == '\0' || space < 10) { /* flush */
2967 ast_str_append(out, 0, "%s", buf);
2969 space = sizeof(buf);
2974 if ( (mode & 2) && !isalnum(*src)) {
2981 strcpy(dst, "<");
2986 strcpy(dst, ">");
2991 strcpy(dst, """);
2996 strcpy(dst, "'");
3001 strcpy(dst, "&");
3007 *dst++ = mode ? tolower(*src) : *src;
3013 struct variable_count {
3018 static int compress_char(char c)
3023 else if (c >= 'a' && c <= 'z')
3031 static int variable_count_hash_fn(const void *vvc, const int flags)
3033 const struct variable_count *vc = vvc;
3035 for (i = 0; i < 5; i++) {
3036 if (vc->varname[i] == '\0')
3038 res += compress_char(vc->varname[i]) << (i * 6);
3043 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
3045 /* Due to the simplicity of struct variable_count, it makes no difference
3046 * if you pass in objects or strings, the same operation applies. This is
3047 * due to the fact that the hash occurs on the first element, which means
3048 * the address of both the struct and the string are exactly the same. */
3049 struct variable_count *vc = obj;
3051 return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
3054 /*! \brief Convert the input into XML or HTML.
3055 * The input is supposed to be a sequence of lines of the form
3057 * optionally followed by a blob of unformatted text.
3058 * A blank line is a section separator. Basically, this is a
3059 * mixture of the format of Manager Interface and CLI commands.
3060 * The unformatted text is considered as a single value of a field
3061 * named 'Opaque-data'.
3063 * At the moment the output format is the following (but it may
3064 * change depending on future requirements so don't count too
3065 * much on it when writing applications):
3067 * General: the unformatted text is used as a value of
3068 * XML output: to be completed
3071 * Each section is within <response type="object" id="xxx">
3072 * where xxx is taken from ajaxdest variable or defaults to unknown
3073 * Each row is reported as an attribute Name="value" of an XML
3074 * entity named from the variable ajaxobjtype, default to "generic"
3078 * each Name-value pair is output as a single row of a two-column table.
3079 * Sections (blank lines in the input) are separated by a <HR>
3082 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
3084 struct ast_variable *v;
3085 const char *dest = NULL;
3087 const char *objtype = NULL;
3088 int in_data = 0; /* parsing data */
3090 int xml = (format == FORMAT_XML);
3091 struct variable_count *vc = NULL;
3092 struct ao2_container *vco = NULL;
3094 for (v = vars; v; v = v->next) {
3095 if (!dest && !strcasecmp(v->name, "ajaxdest"))
3097 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
3103 objtype = "generic";
3105 /* we want to stop when we find an empty line */
3107 val = strsep(&in, "\r\n"); /* mark start and end of line */
3108 if (in && *in == '\n') /* remove trailing \n if any */
3110 ast_trim_blanks(val);
3111 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
3112 if (ast_strlen_zero(val)) {
3113 if (in_data) { /* close data */
3114 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3117 ast_str_append(out, 0, xml ? " /></response>\n" :
3118 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3125 /* we expect Name: value lines */
3129 var = strsep(&val, ":");
3130 if (val) { /* found the field name */
3131 val = ast_skip_blanks(val);
3132 ast_trim_blanks(var);
3133 } else { /* field name not found, move to opaque mode */
3135 var = "Opaque-data";
3141 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
3143 ast_str_append(out, 0, "<body>\n");
3144 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
3148 if (!in_data) { /* build appropriate line start */
3149 ast_str_append(out, 0, xml ? " " : "<tr><td>");
3150 if ((vc = ao2_find(vco, var, 0)))
3153 /* Create a new entry for this one */
3154 vc = ao2_alloc(sizeof(*vc), NULL);
3159 xml_copy_escape(out, var, xml ? 1 | 2 : 0);
3161 ast_str_append(out, 0, "-%d", vc->count);
3163 ast_str_append(out, 0, xml ? "='" : "</td><td>");
3164 if (!strcmp(var, "Opaque-data"))
3167 xml_copy_escape(out, val, 0); /* data field */
3169 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3171 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
3174 ast_str_append(out, 0, xml ? " /></response>\n" :
3175 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3180 static struct ast_str *generic_http_callback(enum output_format format,
3181 struct sockaddr_in *requestor, const char *uri,
3182 struct ast_variable *params, int *status,
3183 char **title, int *contentlength)
3185 struct mansession *s = NULL;
3186 unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
3188 struct ast_variable *v;
3189 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
3190 struct ast_str *out = NULL;
3191 struct message m = { 0 };
3195 for (v = params; v; v = v->next) {
3196 if (!strcasecmp(v->name, "mansession_id")) {
3197 sscanf(v->value, "%lx", &ident);
3202 if (!(s = find_session(ident))) {
3203 /* Create new session.
3204 * While it is not in the list we don't need any locking
3206 if (!(s = ast_calloc(1, sizeof(*s)))) {
3208 goto generic_callback_out;
3210 s->sin = *requestor;
3212 s->waiting_thread = AST_PTHREADT_NULL;
3214 ast_mutex_init(&s->__lock);
3215 ast_mutex_lock(&s->__lock);
3217 s->managerid = rand() | 1; /* make sure it is non-zero */
3218 s->last_ev = grab_last();
3219 AST_LIST_LOCK(&sessions);
3220 AST_LIST_INSERT_HEAD(&sessions, s, list);
3221 ast_atomic_fetchadd_int(&num_sessions, 1);
3222 AST_LIST_UNLOCK(&sessions);
3225 ast_mutex_unlock(&s->__lock);
3227 if (!(out = ast_str_create(1024))) {
3229 goto generic_callback_out;
3232 s->fd = mkstemp(template); /* create a temporary file for command output */
3234 s->f = fdopen(s->fd, "w+");
3236 for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
3237 hdrlen = strlen(v->name) + strlen(v->value) + 3;
3238 m.headers[m.hdrcount] = alloca(hdrlen);
3239 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
3243 if (process_message(s, &m)) {
3244 if (s->authenticated) {
3245 if (manager_displayconnects(s))
3246 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3247 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3249 if (displayconnects)
3250 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
3251 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
3256 ast_str_append(&out, 0,
3257 "Content-type: text/%s\r\n"
3258 "Cache-Control: no-cache;\r\n"
3259 "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n"
3261 contenttype[format],
3262 s->managerid, httptimeout);
3264 if (format == FORMAT_XML) {
3265 ast_str_append(&out, 0, "<ajax-response>\n");
3266 } else if (format == FORMAT_HTML) {
3268 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
3269 #define TEST_STRING \
3270 "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
3271 user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
3272 <input type=\"submit\"></form>"
3274 ast_str_append(&out, 0, "<title>Asterisk™ Manager Interface</title>");
3275 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
3276 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
3277 ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
3280 if (s->f != NULL) { /* have temporary output */
3282 size_t l = ftell(s->f);
3285 if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
3286 if (format == FORMAT_XML || format == FORMAT_HTML)
3287 xml_translate(&out, buf, params, format);
3289 ast_str_append(&out, 0, buf);
3292 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
3293 xml_translate(&out, "", params, format);
3300 if (format == FORMAT_XML) {
3301 ast_str_append(&out, 0, "</ajax-response>\n");
3302 } else if (format == FORMAT_HTML)
3303 ast_str_append(&out, 0, "</table></body>\r\n");
3305 ast_mutex_lock(&s->__lock);
3306 /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */
3307 s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
3309 if (s->needdestroy) {
3310 if (s->inuse == 1) {
3311 ast_debug(1, "Need destroy, doing it now!\n");
3314 ast_debug(1, "Need destroy, but can't do it yet!\n");
3315 if (s->waiting_thread != AST_PTHREADT_NULL)
3316 pthread_kill(s->waiting_thread, SIGURG);
3321 ast_mutex_unlock(&s->__lock);
3325 generic_callback_out:
3327 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
3331 static struct ast_str *manager_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
3333 return generic_http_callback(FORMAT_HTML, &ser->requestor, uri, params, status, title, contentlength);
3336 static struct ast_str *mxml_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
3338 return generic_http_callback(FORMAT_XML, &ser->requestor, uri, params, status, title, contentlength);
3341 static struct ast_str *rawman_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
3343 return generic_http_callback(FORMAT_RAW, &ser->requestor, uri, params, status, title, contentlength);
3346 struct ast_http_uri rawmanuri = {
3347 .description = "Raw HTTP Manager Event Interface",
3350 .callback = rawman_http_callback,
3353 struct ast_http_uri manageruri = {
3354 .description = "HTML Manager Event Interface",
3357 .callback = manager_http_callback,
3360 struct ast_http_uri managerxmluri = {
3361 .description = "XML Manager Event Interface",
3364 .callback = mxml_http_callback,
3367 static int registered = 0;
3368 static int webregged = 0;
3370 /*! \brief cleanup code called at each iteration of server_root,
3371 * guaranteed to happen every 5 seconds at most
3373 static void purge_old_stuff(void *data)
3379 struct tls_config ami_tls_cfg;
3380 static struct server_args ami_desc = {
3382 .master = AST_PTHREADT_NULL,
3384 .poll_timeout = 5000, /* wake up every 5 seconds */
3385 .periodic_fn = purge_old_stuff,
3386 .name = "AMI server",
3387 .accept_fn = server_root, /* thread doing the accept() */
3388 .worker_fn = session_do, /* thread handling the session */
3391 static struct server_args amis_desc = {
3393 .master = AST_PTHREADT_NULL,
3394 .tls_cfg = &ami_tls_cfg,
3395 .poll_timeout = -1, /* the other does the periodic cleanup */
3396 .name = "AMI TLS server",
3397 .accept_fn = server_root, /* thread doing the accept() */
3398 .worker_fn = session_do, /* thread handling the session */
3401 static int __init_manager(int reload)
3403 struct ast_config *cfg = NULL;
3406 int newhttptimeout = 60;
3407 int have_sslbindaddr = 0;
3409 struct ast_hostent ahp;
3410 struct ast_manager_user *user = NULL;
3411 struct ast_variable *var;
3412 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
3415 /* Register default actions */
3416 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
3417 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
3418 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
3419 ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
3420 ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
3421 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
3422 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
3423 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
3424 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
3425 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
3426 ast_manager_register2("GetConfigJSON", EVENT_FLAG_CONFIG, action_getconfigjson, "Retrieve configuration (JSON format)", mandescr_getconfigjson);
3427 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
3428 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
3429 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
3430 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
3431 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
3432 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
3433 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
3434 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
3435 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
3436 ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
3437 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
3438 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
3439 ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
3440 ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
3441 ast_manager_register2("Reload", EVENT_FLAG_CONFIG, action_reload, "Send a reload event", mandescr_reload);
3442 ast_manager_register2("CoreShowChannels", EVENT_FLAG_SYSTEM, action_coreshowchannels, "List currently active channels", mandescr_coreshowchannels);
3444 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
3445 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
3447 /* Append placeholder event so master_eventq never runs dry */
3448 append_event("Event: Placeholder\r\n\r\n", 0);
3450 if ((cfg = ast_config_load("manager.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
3453 displayconnects = 1;
3455 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
3459 /* default values */
3460 memset(&ami_desc.sin, 0, sizeof(struct sockaddr_in));
3461 memset(&amis_desc.sin, 0, sizeof(amis_desc.sin));
3462 amis_desc.sin.sin_port = htons(5039);
3463 ami_desc.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
3465 ami_tls_cfg.enabled = 0;
3466 if (ami_tls_cfg.certfile)
3467 ast_free(ami_tls_cfg.certfile);
3468 ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
3469 if (ami_tls_cfg.cipher)
3470 ast_free(ami_tls_cfg.cipher);
3471 ami_tls_cfg.cipher = ast_strdup("");
3473 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
3475 if (!strcasecmp(var->name, "sslenable"))
3476 ami_tls_cfg.enabled = ast_true(val);
3477 else if (!strcasecmp(var->name, "sslbindport"))
3478 amis_desc.sin.sin_port = htons(atoi(val));
3479 else if (!strcasecmp(var->name, "sslbindaddr")) {
3480 if ((hp = ast_gethostbyname(val, &ahp))) {
3481 memcpy(&amis_desc.sin.sin_addr, hp->h_addr, sizeof(amis_desc.sin.sin_addr));
3482 have_sslbindaddr = 1;
3484 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
3486 } else if (!strcasecmp(var->name, "sslcert")) {
3487 ast_free(ami_tls_cfg.certfile);
3488 ami_tls_cfg.certfile = ast_strdup(val);
3489 } else if (!strcasecmp(var->name, "sslcipher")) {
3490 ast_free(ami_tls_cfg.cipher);
3491 ami_tls_cfg.cipher = ast_strdup(val);
3492 } else if (!strcasecmp(var->name, "enabled")) {
3493 manager_enabled = ast_true(val);
3494 } else if (!strcasecmp(var->name, "block-sockets")) {
3495 block_sockets = ast_true(val);
3496 } else if (!strcasecmp(var->name, "webenabled")) {
3497 webmanager_enabled = ast_true(val);
3498 } else if (!strcasecmp(var->name, "port")) {
3499 ami_desc.sin.sin_port = htons(atoi(val));
3500 } else if (!strcasecmp(var->name, "bindaddr")) {
3501 if (!inet_aton(val, &ami_desc.sin.sin_addr)) {
3502 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
3503 memset(&ami_desc.sin.sin_addr, 0, sizeof(ami_desc.sin.sin_addr));
3505 } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
3506 allowmultiplelogin = ast_true(val);
3507 } else if (!strcasecmp(var->name, "displayconnects")) {
3508 displayconnects = ast_true(val);
3509 } else if (!strcasecmp(var->name, "timestampevents")) {
3510 timestampevents = ast_true(val);
3511 } else if (!strcasecmp(var->name, "debug")) {
3512 manager_debug = ast_true(val);
3513 } else if (!strcasecmp(var->name, "httptimeout")) {
3514 newhttptimeout = atoi(val);
3516 ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
3521 if (manager_enabled)
3522 ami_desc.sin.sin_family = AF_INET;
3523 if (!have_sslbindaddr)
3524 amis_desc.sin.sin_addr = ami_desc.sin.sin_addr;
3525 if (ami_tls_cfg.enabled)
3526 amis_desc.sin.sin_family = AF_INET;
3529 AST_RWLIST_WRLOCK(&users);
3531 while ((cat = ast_category_browse(cfg, cat))) {
3533 if (!strcasecmp(cat, "general"))
3536 /* Look for an existing entry, if none found - create one and add it to the list */
3537 if (!(user = get_manager_by_name_locked(cat))) {
3538 if (!(user = ast_calloc(1, sizeof(*user))))
3540 /* Copy name over */
3541 ast_copy_string(user->username, cat, sizeof(user->username));
3542 /* Insert into list */
3543 AST_RWLIST_INSERT_TAIL(&users, user, list);
3546 /* Make sure we keep this user and don't destroy it during cleanup */
3548 /* Default displayconnect to [general] */
3549 user->displayconnects = displayconnects;
3551 var = ast_variable_browse(cfg, cat);
3553 if (!strcasecmp(var->name, "secret")) {
3555 ast_free(user->secret);
3556 user->secret = ast_strdup(var->value);
3557 } else if (!strcasecmp(var->name, "deny") ) {
3559 ast_free(user->deny);
3560 user->deny = ast_strdup(var->value);
3561 } else if (!strcasecmp(var->name, "permit") ) {
3563 ast_free(user->permit);
3564 user->permit = ast_strdup(var->value);
3565 } else if (!strcasecmp(var->name, "read") ) {
3567 ast_free(user->read);
3568 user->read = ast_strdup(var->value);
3569 } else if (!strcasecmp(var->name, "write") ) {
3571 ast_free(user->write);
3572 user->write = ast_strdup(var->value);
3573 } else if (!strcasecmp(var->name, "displayconnects") )
3574 user->displayconnects = ast_true(var->value);
3576 ast_debug(1, "%s is an unknown option.\n", var->name);
3582 /* Perform cleanup - essentially prune out old users that no longer exist */
3583 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
3584 if (user->keep) { /* valid record. clear flag for the next round */
3588 /* We do not need to keep this user so take them out of the list */
3589 AST_RWLIST_REMOVE_CURRENT(list);
3590 /* Free their memory now */
3592 ast_free(user->secret);
3594 ast_free(user->deny);
3596 ast_free(user->permit);
3598 ast_free(user->read);
3600 ast_free(user->write);
3603 AST_RWLIST_TRAVERSE_SAFE_END;
3605 AST_RWLIST_UNLOCK(&users);
3607 ast_config_destroy(cfg);
3609 if (webmanager_enabled && manager_enabled) {
3611 ast_http_uri_link(&rawmanuri);
3612 ast_http_uri_link(&manageruri);
3613 ast_http_uri_link(&managerxmluri);
3618 ast_http_uri_unlink(&rawmanuri);
3619 ast_http_uri_unlink(&manageruri);
3620 ast_http_uri_unlink(&managerxmluri);
3625 if (newhttptimeout > 0)
3626 httptimeout = newhttptimeout;
3628 server_start(&ami_desc);
3629 if (ssl_setup(amis_desc.tls_cfg))
3630 server_start(&amis_desc);
3634 int init_manager(void)
3636 return __init_manager(0);
3639 int reload_manager(void)
3641 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
3642 return __init_manager(1);