2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief The Asterisk Management Interface - AMI
23 * \author Mark Spencer <markster@digium.com>
25 * \extref OpenSSL http://www.openssl.org - for AMI/SSL
27 * At the moment this file contains a number of functions, namely:
29 * - data structures storing AMI state
30 * - AMI-related API functions, used by internal asterisk components
31 * - handlers for AMI-related CLI functions
32 * - handlers for AMI functions (available through the AMI socket)
33 * - the code for the main AMI listener thread and individual session threads
34 * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
39 /*! \addtogroup Group_AMI AMI functions
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48 #include "asterisk/_private.h"
49 #include "asterisk/paths.h" /* use various ast_config_AST_* */
55 #include "asterisk/channel.h"
56 #include "asterisk/file.h"
57 #include "asterisk/manager.h"
58 #include "asterisk/module.h"
59 #include "asterisk/config.h"
60 #include "asterisk/callerid.h"
61 #include "asterisk/lock.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/app.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/md5.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/http.h"
69 #include "asterisk/version.h"
70 #include "asterisk/threadstorage.h"
71 #include "asterisk/linkedlists.h"
72 #include "asterisk/version.h"
73 #include "asterisk/term.h"
74 #include "asterisk/astobj2.h"
77 * Linked list of events.
78 * Global events are appended to the list by append_event().
79 * The usecount is the number of stored pointers to the element,
80 * excluding the list pointers. So an element that is only in
81 * the list has a usecount of 0, not 1.
83 * Clients have a pointer to the last event processed, and for each
84 * of these clients we track the usecount of the elements.
85 * If we have a pointer to an entry in the list, it is safe to navigate
86 * it forward because elements will not be deleted, but only appended.
87 * The worst that can happen is seeing the pointer still NULL.
89 * When the usecount of an element drops to 0, and the element is the
90 * first in the list, we can remove it. Removal is done within the
91 * main thread, which is woken up for the purpose.
93 * For simplicity of implementation, we make sure the list is never empty.
96 int usecount; /*!< # of clients who still need the event */
98 unsigned int seq; /*!< sequence number */
99 AST_LIST_ENTRY(eventqent) eq_next;
100 char eventdata[1]; /*!< really variable size, allocated by append_event() */
103 static AST_LIST_HEAD_STATIC(all_events, eventqent);
105 static int displayconnects = 1;
106 static int allowmultiplelogin = 1;
107 static int timestampevents;
108 static int httptimeout = 60;
109 static int manager_enabled = 0;
110 static int webmanager_enabled = 0;
112 static int block_sockets;
113 static int num_sessions;
115 static int manager_debug; /*!< enable some debugging code in the manager */
118 * Descriptor for a manager session, either on the AMI socket or over HTTP.
121 * AMI session have managerid == 0; the entry is created upon a connect,
122 * and destroyed with the socket.
123 * HTTP sessions have managerid != 0, the value is used as a search key
124 * to lookup sessions (using the mansession_id cookie).
126 static const char *command_blacklist[] = {
132 pthread_t ms_t; /*!< Execution thread, basically useless */
133 ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
134 /* XXX need to document which fields it is protecting */
135 struct sockaddr_in sin; /*!< address we are connecting from */
136 FILE *f; /*!< fdopen() on the underlying fd */
137 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
138 int inuse; /*!< number of HTTP sessions using this entry */
139 int needdestroy; /*!< Whether an HTTP session should be destroyed */
140 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
141 unsigned long managerid; /*!< Unique manager identifier, 0 for AMI sessions */
142 time_t sessionstart; /*!< Session start time */
143 time_t sessiontimeout; /*!< Session timeout if HTTP */
144 char username[80]; /*!< Logged in username */
145 char challenge[10]; /*!< Authentication challenge */
146 int authenticated; /*!< Authentication status */
147 int readperm; /*!< Authorization for reading */
148 int writeperm; /*!< Authorization for writing */
149 char inbuf[1025]; /*!< Buffer */
150 /* we use the extra byte to add a '\0' and simplify parsing */
151 int inlen; /*!< number of buffered bytes */
152 int send_events; /*!< XXX what ? */
153 struct eventqent *last_ev; /*!< last event processed. */
154 int writetimeout; /*!< Timeout for ast_carefulwrite() */
155 AST_LIST_ENTRY(mansession) list;
158 #define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
160 static AST_LIST_HEAD_STATIC(sessions, mansession);
162 /*! \brief user descriptor, as read from the config file.
164 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
165 * lines which are not supported here, and readperm/writeperm/writetimeout
168 struct ast_manager_user {
171 struct ast_ha *ha; /*!< ACL setting */
172 int readperm; /*! Authorization for reading */
173 int writeperm; /*! Authorization for writing */
174 int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
175 int displayconnects; /*!< XXX unused */
176 int keep; /*!< mark entries created on a reload */
177 AST_RWLIST_ENTRY(ast_manager_user) list;
180 /*! \brief list of users found in the config file */
181 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
183 /*! \brief list of actions registered */
184 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
186 /*! \brief list of hooks registered */
187 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
189 /*! \brief Add a custom hook to be called when an event is fired */
190 void ast_manager_register_hook(struct manager_custom_hook *hook)
192 AST_RWLIST_WRLOCK(&manager_hooks);
193 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
194 AST_RWLIST_UNLOCK(&manager_hooks);
198 /*! \brief Delete a custom hook to be called when an event is fired */
199 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
201 AST_RWLIST_WRLOCK(&manager_hooks);
202 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
203 AST_RWLIST_UNLOCK(&manager_hooks);
208 * Event list management functions.
209 * We assume that the event list always has at least one element,
210 * and the delete code will not remove the last entry even if the
214 static time_t __deb(time_t start, const char *msg)
216 time_t now = time(NULL);
217 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
218 if (start != 0 && now - start > 5)
219 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
223 static void LOCK_EVENTS(void)
225 time_t start = __deb(0, "about to lock events");
226 AST_LIST_LOCK(&all_events);
227 __deb(start, "done lock events");
230 static void UNLOCK_EVENTS(void)
232 __deb(0, "about to unlock events");
233 AST_LIST_UNLOCK(&all_events);
236 static void LOCK_SESS(void)
238 time_t start = __deb(0, "about to lock sessions");
239 AST_LIST_LOCK(&sessions);
240 __deb(start, "done lock sessions");
243 static void UNLOCK_SESS(void)
245 __deb(0, "about to unlock sessions");
246 AST_LIST_UNLOCK(&sessions);
250 int check_manager_enabled()
252 return manager_enabled;
255 int check_webmanager_enabled()
257 return (webmanager_enabled && manager_enabled);
261 * Grab a reference to the last event, update usecount as needed.
262 * Can handle a NULL pointer.
264 static struct eventqent *grab_last(void)
266 struct eventqent *ret;
268 AST_LIST_LOCK(&all_events);
269 ret = AST_LIST_LAST(&all_events);
270 /* the list is never empty now, but may become so when
271 * we optimize it in the future, so be prepared.
274 ast_atomic_fetchadd_int(&ret->usecount, 1);
275 AST_LIST_UNLOCK(&all_events);
280 * Purge unused events. Remove elements from the head
281 * as long as their usecount is 0 and there is a next element.
283 static void purge_events(void)
285 struct eventqent *ev;
287 AST_LIST_LOCK(&all_events);
288 while ( (ev = AST_LIST_FIRST(&all_events)) &&
289 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
290 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
293 AST_LIST_UNLOCK(&all_events);
297 * helper functions to convert back and forth between
298 * string and numeric representation of set of flags
300 static struct permalias {
304 { EVENT_FLAG_SYSTEM, "system" },
305 { EVENT_FLAG_CALL, "call" },
306 { EVENT_FLAG_LOG, "log" },
307 { EVENT_FLAG_VERBOSE, "verbose" },
308 { EVENT_FLAG_COMMAND, "command" },
309 { EVENT_FLAG_AGENT, "agent" },
310 { EVENT_FLAG_USER, "user" },
311 { EVENT_FLAG_CONFIG, "config" },
312 { EVENT_FLAG_DTMF, "dtmf" },
313 { EVENT_FLAG_REPORTING, "reporting" },
314 { EVENT_FLAG_CDR, "cdr" },
319 /*! \brief Convert authority code to a list of options */
320 static char *authority_to_str(int authority, struct ast_str **res)
326 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
327 if (authority & perms[i].num) {
328 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
333 if ((*res)->used == 0) /* replace empty string with something sensible */
334 ast_str_append(res, 0, "<none>");
339 /*! Tells you if smallstr exists inside bigstr
340 which is delim by delim and uses no buf or stringsep
341 ast_instring("this|that|more","this",'|') == 1;
343 feel free to move this to app.c -anthm */
344 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
346 const char *val = bigstr, *next;
349 if ((next = strchr(val, delim))) {
350 if (!strncmp(val, smallstr, (next - val)))
355 return !strcmp(smallstr, val);
356 } while (*(val = (next + 1)));
361 static int get_perm(const char *instr)
368 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
369 if (ast_instring(instr, perms[x].label, ','))
377 * A number returns itself, false returns 0, true returns all flags,
378 * other strings return the flags that are set.
380 static int strings_to_mask(const char *string)
384 if (ast_strlen_zero(string))
387 for (p = string; *p; p++)
388 if (*p < '0' || *p > '9')
390 if (!p) /* all digits */
392 if (ast_false(string))
394 if (ast_true(string)) { /* all permissions */
396 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
400 return get_perm(string);
403 static int check_manager_session_inuse(const char *name)
405 struct mansession *session = NULL;
407 AST_LIST_LOCK(&sessions);
408 AST_LIST_TRAVERSE(&sessions, session, list) {
409 if (!strcasecmp(session->username, name))
412 AST_LIST_UNLOCK(&sessions);
414 return session ? 1 : 0;
419 * lookup an entry in the list of registered users.
420 * must be called with the list lock held.
422 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
424 struct ast_manager_user *user = NULL;
426 AST_RWLIST_TRAVERSE(&users, user, list)
427 if (!strcasecmp(user->username, name))
432 /*! \brief Get displayconnects config option.
433 * \param s manager session to get parameter from.
434 * \return displayconnects config option value.
436 static int manager_displayconnects (struct mansession *s)
438 struct ast_manager_user *user = NULL;
441 AST_RWLIST_RDLOCK(&users);
442 if ((user = get_manager_by_name_locked (s->username)))
443 ret = user->displayconnects;
444 AST_RWLIST_UNLOCK(&users);
449 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
451 struct manager_action *cur;
452 struct ast_str *authority;
457 e->command = "manager show command";
459 "Usage: manager show command <actionname>\n"
460 " Shows the detailed description for a specific Asterisk manager interface command.\n";
465 AST_RWLIST_RDLOCK(&actions);
466 AST_RWLIST_TRAVERSE(&actions, cur, list) {
467 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
468 ret = ast_strdup(cur->action);
469 break; /* make sure we exit even if ast_strdup() returns NULL */
472 AST_RWLIST_UNLOCK(&actions);
475 authority = ast_str_alloca(80);
477 return CLI_SHOWUSAGE;
479 AST_RWLIST_RDLOCK(&actions);
480 AST_RWLIST_TRAVERSE(&actions, cur, list) {
481 for (num = 3; num < a->argc; num++) {
482 if (!strcasecmp(cur->action, a->argv[num])) {
483 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
484 cur->action, cur->synopsis,
485 authority_to_str(cur->authority, &authority),
486 S_OR(cur->description, ""));
490 AST_RWLIST_UNLOCK(&actions);
495 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
499 e->command = "manager debug [on|off]";
500 e->usage = "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
506 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
507 else if (a->argc == 3) {
508 if (!strcasecmp(a->argv[2], "on"))
510 else if (!strcasecmp(a->argv[2], "off"))
513 return CLI_SHOWUSAGE;
518 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
520 struct ast_manager_user *user = NULL;
523 struct ast_str *rauthority = ast_str_alloca(80);
524 struct ast_str *wauthority = ast_str_alloca(80);
528 e->command = "manager show user";
530 " Usage: manager show user <user>\n"
531 " Display all information related to the manager user specified.\n";
538 AST_RWLIST_RDLOCK(&users);
539 AST_RWLIST_TRAVERSE(&users, user, list) {
540 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
541 ret = ast_strdup(user->username);
545 AST_RWLIST_UNLOCK(&users);
550 return CLI_SHOWUSAGE;
552 AST_RWLIST_RDLOCK(&users);
554 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
555 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
556 AST_RWLIST_UNLOCK(&users);
567 "displayconnects: %s\n",
568 (user->username ? user->username : "(N/A)"),
569 (user->secret ? "<Set>" : "(N/A)"),
570 (user->ha ? "yes" : "no"),
571 authority_to_str(user->readperm, &rauthority),
572 authority_to_str(user->writeperm, &wauthority),
573 (user->displayconnects ? "yes" : "no"));
575 AST_RWLIST_UNLOCK(&users);
581 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
583 struct ast_manager_user *user = NULL;
587 e->command = "manager show users";
589 "Usage: manager show users\n"
590 " Prints a listing of all managers that are currently configured on that\n"
597 return CLI_SHOWUSAGE;
599 AST_RWLIST_RDLOCK(&users);
601 /* If there are no users, print out something along those lines */
602 if (AST_RWLIST_EMPTY(&users)) {
603 ast_cli(a->fd, "There are no manager users.\n");
604 AST_RWLIST_UNLOCK(&users);
608 ast_cli(a->fd, "\nusername\n--------\n");
610 AST_RWLIST_TRAVERSE(&users, user, list) {
611 ast_cli(a->fd, "%s\n", user->username);
615 AST_RWLIST_UNLOCK(&users);
617 ast_cli(a->fd,"-------------------\n");
618 ast_cli(a->fd,"%d manager users configured.\n", count_amu);
624 /*! \brief CLI command manager list commands */
625 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
627 struct manager_action *cur;
628 struct ast_str *authority;
629 static const char *format = " %-15.15s %-15.15s %-55.55s\n";
632 e->command = "manager show commands";
634 "Usage: manager show commands\n"
635 " Prints a listing of all the available Asterisk manager interface commands.\n";
640 authority = ast_str_alloca(80);
641 ast_cli(a->fd, format, "Action", "Privilege", "Synopsis");
642 ast_cli(a->fd, format, "------", "---------", "--------");
644 AST_RWLIST_RDLOCK(&actions);
645 AST_RWLIST_TRAVERSE(&actions, cur, list)
646 ast_cli(a->fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
647 AST_RWLIST_UNLOCK(&actions);
652 /*! \brief CLI command manager list connected */
653 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
655 struct mansession *s;
656 time_t now = time(NULL);
657 static const char *format = " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n";
658 static const char *format2 = " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n";
662 e->command = "manager show connected";
664 "Usage: manager show connected\n"
665 " Prints a listing of the users that are currently connected to the\n"
666 "Asterisk manager interface.\n";
672 ast_cli(a->fd, format, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
674 AST_LIST_LOCK(&sessions);
675 AST_LIST_TRAVERSE(&sessions, s, list) {
676 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);
679 AST_LIST_UNLOCK(&sessions);
681 ast_cli(a->fd, "%d users connected.\n", count);
686 /*! \brief CLI command manager list eventq */
687 /* Should change to "manager show connected" */
688 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
693 e->command = "manager show eventq";
695 "Usage: manager show eventq\n"
696 " Prints a listing of all events pending in the Asterisk manger\n"
702 AST_LIST_LOCK(&all_events);
703 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
704 ast_cli(a->fd, "Usecount: %d\n",s->usecount);
705 ast_cli(a->fd, "Category: %d\n", s->category);
706 ast_cli(a->fd, "Event:\n%s", s->eventdata);
708 AST_LIST_UNLOCK(&all_events);
713 /*! \brief CLI command manager reload */
714 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
718 e->command = "manager reload";
720 "Usage: manager reload\n"
721 " Reloads the manager configuration.\n";
727 return CLI_SHOWUSAGE;
733 static struct ast_cli_entry cli_manager[] = {
734 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
735 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
736 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
737 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
738 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
739 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
740 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
741 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
745 * Decrement the usecount for the event; if it goes to zero,
746 * (why check for e->next ?) wakeup the
747 * main thread, which is in charge of freeing the record.
748 * Returns the next record.
750 static struct eventqent *unref_event(struct eventqent *e)
752 ast_atomic_fetchadd_int(&e->usecount, -1);
753 return AST_LIST_NEXT(e, eq_next);
756 static void ref_event(struct eventqent *e)
758 ast_atomic_fetchadd_int(&e->usecount, 1);
762 * destroy a session, leaving the usecount
764 static void free_session(struct mansession *s)
766 struct eventqent *eqe = s->last_ev;
769 ast_mutex_destroy(&s->__lock);
774 static void destroy_session(struct mansession *s)
776 AST_LIST_LOCK(&sessions);
777 AST_LIST_REMOVE(&sessions, s, list);
778 ast_atomic_fetchadd_int(&num_sessions, -1);
780 AST_LIST_UNLOCK(&sessions);
783 const char *astman_get_header(const struct message *m, char *var)
785 int x, l = strlen(var);
787 for (x = 0; x < m->hdrcount; x++) {
788 const char *h = m->headers[x];
789 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
796 struct ast_variable *astman_get_variables(const struct message *m)
799 struct ast_variable *head = NULL, *cur;
801 AST_DECLARE_APP_ARGS(args,
802 AST_APP_ARG(vars)[32];
805 varlen = strlen("Variable: ");
807 for (x = 0; x < m->hdrcount; x++) {
808 char *parse, *var, *val;
810 if (strncasecmp("Variable: ", m->headers[x], varlen))
812 parse = ast_strdupa(m->headers[x] + varlen);
814 AST_STANDARD_APP_ARGS(args, parse);
817 for (y = 0; y < args.argc; y++) {
820 var = val = ast_strdupa(args.vars[y]);
822 if (!val || ast_strlen_zero(var))
824 cur = ast_variable_new(var, val, "");
834 * helper function to send a string to the socket.
835 * Return -1 on error (e.g. buffer full).
837 static int send_string(struct mansession *s, char *string)
839 int len = strlen(string); /* residual length */
841 struct timeval start = ast_tvnow();
847 n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
848 if (n == len /* ok */ || n < 0 /* error */)
850 len -= n; /* skip already written data */
854 n = -1; /* error marker */
855 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
856 if (elapsed > s->writetimeout)
858 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
862 return n < 0 ? -1 : 0;
866 * \brief thread local buffer for astman_append
868 * \note This can not be defined within the astman_append() function
869 * because it declares a couple of functions that get used to
870 * initialize the thread local storage key.
872 AST_THREADSTORAGE(astman_append_buf);
873 /*! \brief initial allocated size for the astman_append_buf */
874 #define ASTMAN_APPEND_BUF_INITSIZE 256
877 * utility functions for creating AMI replies
879 void astman_append(struct mansession *s, const char *fmt, ...)
884 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
888 ast_str_set_va(&buf, 0, fmt, ap);
892 send_string(s, buf->str);
894 ast_verbose("fd == -1 in astman_append, should not happen\n");
897 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
898 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
899 hold the session lock _or_ be running in an action callback (in which case s->busy will
900 be non-zero). In either of these cases, there is no need to lock-protect the session's
901 fd, since no other output will be sent (events will be queued), and no input will
902 be read until either the current action finishes or get_input() obtains the session
906 /*! \brief send a response with an optional message,
907 * and terminate it with an empty line.
908 * m is used only to grab the 'ActionID' field.
910 * Use the explicit constant MSG_MOREDATA to remove the empty line.
911 * XXX MSG_MOREDATA should go to a header file.
913 #define MSG_MOREDATA ((char *)astman_send_response)
914 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
916 const char *id = astman_get_header(m,"ActionID");
918 astman_append(s, "Response: %s\r\n", resp);
919 if (!ast_strlen_zero(id))
920 astman_append(s, "ActionID: %s\r\n", id);
922 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
923 if (msg == MSG_MOREDATA)
926 astman_append(s, "Message: %s\r\n\r\n", msg);
928 astman_append(s, "\r\n");
931 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
933 astman_send_response_full(s, m, resp, msg, NULL);
936 void astman_send_error(struct mansession *s, const struct message *m, char *error)
938 astman_send_response_full(s, m, "Error", error, NULL);
941 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
943 astman_send_response_full(s, m, "Success", msg, NULL);
946 static void astman_start_ack(struct mansession *s, const struct message *m)
948 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
951 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
953 astman_send_response_full(s, m, "Success", msg, listflag);
958 Rather than braindead on,off this now can also accept a specific int mask value
959 or a ',' delim list of mask strings (the same as manager.conf) -anthm
961 static int set_eventmask(struct mansession *s, const char *eventmask)
963 int maskint = strings_to_mask(eventmask);
965 ast_mutex_lock(&s->__lock);
967 s->send_events = maskint;
968 ast_mutex_unlock(&s->__lock);
974 * Here we start with action_ handlers for AMI actions,
975 * and the internal functions used by them.
976 * Generally, the handlers are called action_foo()
979 /* helper function for action_login() */
980 static int authenticate(struct mansession *s, const struct message *m)
982 const char *username = astman_get_header(m, "Username");
983 const char *password = astman_get_header(m, "Secret");
985 struct ast_manager_user *user = NULL;
987 if (ast_strlen_zero(username)) /* missing username */
990 /* locate user in locked state */
991 AST_RWLIST_WRLOCK(&users);
993 if (!(user = get_manager_by_name_locked(username))) {
994 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
995 } else if (user->ha && !ast_apply_ha(user->ha, &(s->sin))) {
996 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
997 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
998 const char *key = astman_get_header(m, "Key");
999 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) &&
1000 !ast_strlen_zero(password)) {
1003 char md5key[256] = "";
1004 struct MD5Context md5;
1005 unsigned char digest[16];
1008 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1009 MD5Update(&md5, (unsigned char *) password, strlen(password));
1010 MD5Final(digest, &md5);
1011 for (x=0; x<16; x++)
1012 len += sprintf(md5key + len, "%2.2x", digest[x]);
1013 if (!strcmp(md5key, key))
1016 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
1017 S_OR(s->challenge, ""));
1019 } else if (password && user->secret && !strcmp(password, user->secret))
1023 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1024 AST_RWLIST_UNLOCK(&users);
1030 ast_copy_string(s->username, username, sizeof(s->username));
1031 s->readperm = user->readperm;
1032 s->writeperm = user->writeperm;
1033 s->writetimeout = user->writetimeout;
1034 s->sessionstart = time(NULL);
1035 set_eventmask(s, astman_get_header(m, "Events"));
1037 AST_RWLIST_UNLOCK(&users);
1041 /*! \brief Manager PING */
1042 static char mandescr_ping[] =
1043 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1044 " manager connection open.\n"
1045 "Variables: NONE\n";
1047 static int action_ping(struct mansession *s, const struct message *m)
1049 astman_send_response(s, m, "Success", "Ping: Pong\r\n");
1053 static char mandescr_getconfig[] =
1054 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1055 "file by category and contents.\n"
1057 " Filename: Configuration filename (e.g. foo.conf)\n";
1059 static int action_getconfig(struct mansession *s, const struct message *m)
1061 struct ast_config *cfg;
1062 const char *fn = astman_get_header(m, "Filename");
1065 char *category=NULL;
1066 struct ast_variable *v;
1067 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1069 if (ast_strlen_zero(fn)) {
1070 astman_send_error(s, m, "Filename not specified");
1073 if (!(cfg = ast_config_load(fn, config_flags))) {
1074 astman_send_error(s, m, "Config file not found");
1077 astman_start_ack(s, m);
1078 while ((category = ast_category_browse(cfg, category))) {
1080 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1081 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1082 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1085 ast_config_destroy(cfg);
1086 astman_append(s, "\r\n");
1091 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1092 static void json_escape(char *out, const char *in)
1095 if (*in == '\\' || *in == '\"')
1102 static char mandescr_getconfigjson[] =
1103 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1104 "file by category and contents in JSON format. This only makes sense to be used\n"
1105 "using rawman over the HTTP interface.\n"
1107 " Filename: Configuration filename (e.g. foo.conf)\n";
1109 static int action_getconfigjson(struct mansession *s, const struct message *m)
1111 struct ast_config *cfg;
1112 const char *fn = astman_get_header(m, "Filename");
1113 char *category = NULL;
1114 struct ast_variable *v;
1117 unsigned int buf_len = 0;
1118 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1120 if (ast_strlen_zero(fn)) {
1121 astman_send_error(s, m, "Filename not specified");
1125 if (!(cfg = ast_config_load(fn, config_flags))) {
1126 astman_send_error(s, m, "Config file not found");
1131 buf = alloca(buf_len);
1133 astman_start_ack(s, m);
1134 astman_append(s, "JSON: {");
1135 while ((category = ast_category_browse(cfg, category))) {
1137 if (buf_len < 2 * strlen(category) + 1) {
1139 buf = alloca(buf_len);
1141 json_escape(buf, category);
1142 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1145 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1147 astman_append(s, ",");
1148 if (buf_len < 2 * strlen(v->name) + 1) {
1150 buf = alloca(buf_len);
1152 json_escape(buf, v->name);
1153 astman_append(s, "\"%s", buf);
1154 if (buf_len < 2 * strlen(v->value) + 1) {
1156 buf = alloca(buf_len);
1158 json_escape(buf, v->value);
1159 astman_append(s, "%s\"", buf);
1163 astman_append(s, "]");
1165 astman_append(s, "}\r\n\r\n");
1167 ast_config_destroy(cfg);
1172 /* helper function for action_updateconfig */
1173 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1177 const char *action, *cat, *var, *value, *match;
1178 struct ast_category *category;
1179 struct ast_variable *v;
1181 for (x = 0; x < 100000; x++) {
1182 unsigned int object = 0;
1184 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1185 action = astman_get_header(m, hdr);
1186 if (ast_strlen_zero(action))
1188 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1189 cat = astman_get_header(m, hdr);
1190 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1191 var = astman_get_header(m, hdr);
1192 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1193 value = astman_get_header(m, hdr);
1194 if (!ast_strlen_zero(value) && *value == '>') {
1198 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1199 match = astman_get_header(m, hdr);
1200 if (!strcasecmp(action, "newcat")) {
1201 if (!ast_strlen_zero(cat)) {
1202 category = ast_category_new(cat, dfn, 99999);
1204 ast_category_append(cfg, category);
1207 } else if (!strcasecmp(action, "renamecat")) {
1208 if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1209 category = ast_category_get(cfg, cat);
1211 ast_category_rename(category, value);
1213 } else if (!strcasecmp(action, "delcat")) {
1214 if (!ast_strlen_zero(cat))
1215 ast_category_delete(cfg, cat);
1216 } else if (!strcasecmp(action, "update")) {
1217 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1218 ast_variable_update(category, var, value, match, object);
1219 } else if (!strcasecmp(action, "delete")) {
1220 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1221 ast_variable_delete(category, var, match);
1222 } else if (!strcasecmp(action, "append")) {
1223 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1224 (category = ast_category_get(cfg, cat)) &&
1225 (v = ast_variable_new(var, value, dfn))){
1226 if (object || (match && !strcasecmp(match, "object")))
1228 ast_variable_append(category, v);
1234 static char mandescr_updateconfig[] =
1235 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1236 "configuration elements in Asterisk configuration files.\n"
1237 "Variables (X's represent 6 digit number beginning with 000000):\n"
1238 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1239 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1240 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1241 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1242 " Cat-XXXXXX: Category to operate on\n"
1243 " Var-XXXXXX: Variable to work on\n"
1244 " Value-XXXXXX: Value to work on\n"
1245 " Match-XXXXXX: Extra match required to match line\n";
1247 static int action_updateconfig(struct mansession *s, const struct message *m)
1249 struct ast_config *cfg;
1250 const char *sfn = astman_get_header(m, "SrcFilename");
1251 const char *dfn = astman_get_header(m, "DstFilename");
1253 const char *rld = astman_get_header(m, "Reload");
1254 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1256 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1257 astman_send_error(s, m, "Filename not specified");
1260 if (!(cfg = ast_config_load(sfn, config_flags))) {
1261 astman_send_error(s, m, "Config file not found");
1264 handle_updates(s, m, cfg, dfn);
1265 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1266 res = config_text_file_save(dfn, cfg, "Manager");
1267 ast_config_destroy(cfg);
1269 astman_send_error(s, m, "Save of config failed");
1272 astman_send_ack(s, m, NULL);
1273 if (!ast_strlen_zero(rld)) {
1276 ast_module_reload(rld);
1281 /*! \brief Manager WAITEVENT */
1282 static char mandescr_waitevent[] =
1283 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1284 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1285 "session, events will be generated and queued.\n"
1287 " Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1289 static int action_waitevent(struct mansession *s, const struct message *m)
1291 const char *timeouts = astman_get_header(m, "Timeout");
1295 const char *id = astman_get_header(m,"ActionID");
1296 char idText[256] = "";
1298 if (!ast_strlen_zero(id))
1299 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1301 if (!ast_strlen_zero(timeouts)) {
1302 sscanf(timeouts, "%i", &timeout);
1305 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1308 ast_mutex_lock(&s->__lock);
1309 if (s->waiting_thread != AST_PTHREADT_NULL)
1310 pthread_kill(s->waiting_thread, SIGURG);
1312 if (s->managerid) { /* AMI-over-HTTP session */
1314 * Make sure the timeout is within the expire time of the session,
1315 * as the client will likely abort the request if it does not see
1316 * data coming after some amount of time.
1318 time_t now = time(NULL);
1319 int max = s->sessiontimeout - now - 10;
1321 if (max < 0) /* We are already late. Strange but possible. */
1323 if (timeout < 0 || timeout > max)
1325 if (!s->send_events) /* make sure we record events */
1326 s->send_events = -1;
1328 ast_mutex_unlock(&s->__lock);
1330 /* XXX should this go inside the lock ? */
1331 s->waiting_thread = pthread_self(); /* let new events wake up this thread */
1332 ast_debug(1, "Starting waiting for an event!\n");
1334 for (x=0; x < timeout || timeout < 0; x++) {
1335 ast_mutex_lock(&s->__lock);
1338 /* We can have multiple HTTP session point to the same mansession entry.
1339 * The way we deal with it is not very nice: newcomers kick out the previous
1340 * HTTP session. XXX this needs to be improved.
1342 if (s->waiting_thread != pthread_self())
1346 ast_mutex_unlock(&s->__lock);
1349 if (s->managerid == 0) { /* AMI session */
1350 if (ast_wait_for_input(s->fd, 1000))
1352 } else { /* HTTP session */
1356 ast_debug(1, "Finished waiting for an event!\n");
1357 ast_mutex_lock(&s->__lock);
1358 if (s->waiting_thread == pthread_self()) {
1359 struct eventqent *eqe;
1360 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1361 while ( (eqe = NEW_EVENT(s)) ) {
1363 if (((s->readperm & eqe->category) == eqe->category) &&
1364 ((s->send_events & eqe->category) == eqe->category)) {
1365 astman_append(s, "%s", eqe->eventdata);
1367 s->last_ev = unref_event(s->last_ev);
1370 "Event: WaitEventComplete\r\n"
1373 s->waiting_thread = AST_PTHREADT_NULL;
1375 ast_debug(1, "Abandoning event request!\n");
1377 ast_mutex_unlock(&s->__lock);
1381 static char mandescr_listcommands[] =
1382 "Description: Returns the action name and synopsis for every\n"
1383 " action that is available to the user\n"
1384 "Variables: NONE\n";
1386 /*! \note The actionlock is read-locked by the caller of this function */
1387 static int action_listcommands(struct mansession *s, const struct message *m)
1389 struct manager_action *cur;
1390 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1392 astman_start_ack(s, m);
1393 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1394 if ((s->writeperm & cur->authority) == cur->authority)
1395 astman_append(s, "%s: %s (Priv: %s)\r\n",
1396 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1398 astman_append(s, "\r\n");
1403 static char mandescr_events[] =
1404 "Description: Enable/Disable sending of events to this manager\n"
1407 " EventMask: 'on' if all events should be sent,\n"
1408 " 'off' if no events should be sent,\n"
1409 " 'system,call,log' to select which flags events should have to be sent.\n";
1411 static int action_events(struct mansession *s, const struct message *m)
1413 const char *mask = astman_get_header(m, "EventMask");
1416 res = set_eventmask(s, mask);
1418 astman_send_response(s, m, "Success", "Events: On\r\n");
1420 astman_send_response(s, m, "Success", "Events: Off\r\n");
1425 static char mandescr_logoff[] =
1426 "Description: Logoff this manager session\n"
1427 "Variables: NONE\n";
1429 static int action_logoff(struct mansession *s, const struct message *m)
1431 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1435 static int action_login(struct mansession *s, const struct message *m)
1437 if (authenticate(s, m)) {
1439 astman_send_error(s, m, "Authentication failed");
1442 s->authenticated = 1;
1443 if (manager_displayconnects(s))
1444 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1445 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1446 astman_send_ack(s, m, "Authentication accepted");
1450 static int action_challenge(struct mansession *s, const struct message *m)
1452 const char *authtype = astman_get_header(m, "AuthType");
1454 if (!strcasecmp(authtype, "MD5")) {
1455 if (ast_strlen_zero(s->challenge))
1456 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1457 ast_mutex_lock(&s->__lock);
1458 astman_start_ack(s, m);
1459 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1460 ast_mutex_unlock(&s->__lock);
1462 astman_send_error(s, m, "Must specify AuthType");
1467 static char mandescr_hangup[] =
1468 "Description: Hangup a channel\n"
1470 " Channel: The channel name to be hungup\n";
1472 static int action_hangup(struct mansession *s, const struct message *m)
1474 struct ast_channel *c = NULL;
1475 const char *name = astman_get_header(m, "Channel");
1476 if (ast_strlen_zero(name)) {
1477 astman_send_error(s, m, "No channel specified");
1480 c = ast_get_channel_by_name_locked(name);
1482 astman_send_error(s, m, "No such channel");
1485 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1486 ast_channel_unlock(c);
1487 astman_send_ack(s, m, "Channel Hungup");
1491 static char mandescr_setvar[] =
1492 "Description: Set a global or local channel variable.\n"
1493 "Variables: (Names marked with * are required)\n"
1494 " Channel: Channel to set variable for\n"
1495 " *Variable: Variable name\n"
1498 static int action_setvar(struct mansession *s, const struct message *m)
1500 struct ast_channel *c = NULL;
1501 const char *name = astman_get_header(m, "Channel");
1502 const char *varname = astman_get_header(m, "Variable");
1503 const char *varval = astman_get_header(m, "Value");
1505 if (ast_strlen_zero(varname)) {
1506 astman_send_error(s, m, "No variable specified");
1510 if (!ast_strlen_zero(name)) {
1511 c = ast_get_channel_by_name_locked(name);
1513 astman_send_error(s, m, "No such channel");
1518 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1521 ast_channel_unlock(c);
1523 astman_send_ack(s, m, "Variable Set");
1528 static char mandescr_getvar[] =
1529 "Description: Get the value of a global or local channel variable.\n"
1530 "Variables: (Names marked with * are required)\n"
1531 " Channel: Channel to read variable from\n"
1532 " *Variable: Variable name\n"
1533 " ActionID: Optional Action id for message matching.\n";
1535 static int action_getvar(struct mansession *s, const struct message *m)
1537 struct ast_channel *c = NULL;
1538 const char *name = astman_get_header(m, "Channel");
1539 const char *varname = astman_get_header(m, "Variable");
1541 char workspace[1024] = "";
1543 if (ast_strlen_zero(varname)) {
1544 astman_send_error(s, m, "No variable specified");
1548 if (!ast_strlen_zero(name)) {
1549 c = ast_get_channel_by_name_locked(name);
1551 astman_send_error(s, m, "No such channel");
1556 if (varname[strlen(varname) - 1] == ')') {
1557 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1560 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1564 ast_channel_unlock(c);
1565 astman_start_ack(s, m);
1566 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1572 /*! \brief Manager "status" command to show channels */
1573 /* Needs documentation... */
1574 static int action_status(struct mansession *s, const struct message *m)
1576 const char *name = astman_get_header(m,"Channel");
1577 struct ast_channel *c;
1579 struct timeval now = ast_tvnow();
1580 long elapsed_seconds = 0;
1582 int all = ast_strlen_zero(name); /* set if we want all channels */
1583 const char *id = astman_get_header(m,"ActionID");
1584 char idText[256] = "";
1586 if (!ast_strlen_zero(id))
1587 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1590 c = ast_channel_walk_locked(NULL);
1592 c = ast_get_channel_by_name_locked(name);
1594 astman_send_error(s, m, "No such channel");
1598 astman_send_ack(s, m, "Channel status will follow");
1600 /* if we look by name, we break after the first iteration */
1604 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1609 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1613 "Privilege: Call\r\n"
1615 "CallerIDNum: %s\r\n"
1616 "CallerIDName: %s\r\n"
1617 "Accountcode: %s\r\n"
1618 "ChannelState: %d\r\n"
1619 "ChannelStateDesc: %s\r\n"
1629 S_OR(c->cid.cid_num, ""),
1630 S_OR(c->cid.cid_name, ""),
1633 ast_state2str(c->_state), c->context,
1634 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1638 "Privilege: Call\r\n"
1640 "CallerIDNum: %s\r\n"
1641 "CallerIDName: %s\r\n"
1649 S_OR(c->cid.cid_num, "<unknown>"),
1650 S_OR(c->cid.cid_name, "<unknown>"),
1652 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1654 ast_channel_unlock(c);
1657 c = ast_channel_walk_locked(c);
1660 "Event: StatusComplete\r\n"
1663 "\r\n",idText, channels);
1667 static char mandescr_sendtext[] =
1668 "Description: Sends A Text Message while in a call.\n"
1669 "Variables: (Names marked with * are required)\n"
1670 " *Channel: Channel to send message to\n"
1671 " *Message: Message to send\n"
1672 " ActionID: Optional Action id for message matching.\n";
1674 static int action_sendtext(struct mansession *s, const struct message *m)
1676 struct ast_channel *c = NULL;
1677 const char *name = astman_get_header(m, "Channel");
1678 const char *textmsg = astman_get_header(m, "Message");
1681 if (ast_strlen_zero(name)) {
1682 astman_send_error(s, m, "No channel specified");
1686 if (ast_strlen_zero(textmsg)) {
1687 astman_send_error(s, m, "No Message specified");
1691 c = ast_get_channel_by_name_locked(name);
1693 astman_send_error(s, m, "No such channel");
1697 res = ast_sendtext(c, textmsg);
1698 ast_channel_unlock(c);
1701 astman_send_ack(s, m, "Success");
1703 astman_send_error(s, m, "Failure");
1708 static char mandescr_redirect[] =
1709 "Description: Redirect (transfer) a call.\n"
1710 "Variables: (Names marked with * are required)\n"
1711 " *Channel: Channel to redirect\n"
1712 " ExtraChannel: Second call leg to transfer (optional)\n"
1713 " *Exten: Extension to transfer to\n"
1714 " *Context: Context to transfer to\n"
1715 " *Priority: Priority to transfer to\n"
1716 " ActionID: Optional Action id for message matching.\n";
1718 /*! \brief action_redirect: The redirect manager command */
1719 static int action_redirect(struct mansession *s, const struct message *m)
1721 const char *name = astman_get_header(m, "Channel");
1722 const char *name2 = astman_get_header(m, "ExtraChannel");
1723 const char *exten = astman_get_header(m, "Exten");
1724 const char *context = astman_get_header(m, "Context");
1725 const char *priority = astman_get_header(m, "Priority");
1726 struct ast_channel *chan, *chan2 = NULL;
1730 if (ast_strlen_zero(name)) {
1731 astman_send_error(s, m, "Channel not specified");
1734 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1735 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1736 astman_send_error(s, m, "Invalid priority\n");
1740 /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1741 chan = ast_get_channel_by_name_locked(name);
1744 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1745 astman_send_error(s, m, buf);
1748 if (ast_check_hangup(chan)) {
1749 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1750 ast_channel_unlock(chan);
1753 if (!ast_strlen_zero(name2))
1754 chan2 = ast_get_channel_by_name_locked(name2);
1755 if (chan2 && ast_check_hangup(chan2)) {
1756 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1757 ast_channel_unlock(chan);
1758 ast_channel_unlock(chan2);
1761 res = ast_async_goto(chan, context, exten, pi);
1763 if (!ast_strlen_zero(name2)) {
1765 res = ast_async_goto(chan2, context, exten, pi);
1769 astman_send_ack(s, m, "Dual Redirect successful");
1771 astman_send_error(s, m, "Secondary redirect failed");
1773 astman_send_ack(s, m, "Redirect successful");
1775 astman_send_error(s, m, "Redirect failed");
1777 ast_channel_unlock(chan);
1779 ast_channel_unlock(chan2);
1783 static char mandescr_command[] =
1784 "Description: Run a CLI command.\n"
1785 "Variables: (Names marked with * are required)\n"
1786 " *Command: Asterisk CLI command to run\n"
1787 " ActionID: Optional Action id for message matching.\n";
1789 /*! \brief Manager command "command" - execute CLI command */
1790 static int action_command(struct mansession *s, const struct message *m)
1792 const char *cmd = astman_get_header(m, "Command");
1793 const char *id = astman_get_header(m, "ActionID");
1794 char *buf, *final_buf;
1795 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1796 int fd = mkstemp(template), i = 0;
1799 for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
1800 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
1801 astman_send_error(s, m, "Command blacklisted");
1806 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1807 if (!ast_strlen_zero(id))
1808 astman_append(s, "ActionID: %s\r\n", id);
1809 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1810 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
1811 l = lseek(fd, 0, SEEK_END); /* how many chars available */
1813 /* This has a potential to overflow the stack. Hence, use the heap. */
1814 buf = ast_calloc(1, l + 1);
1815 final_buf = ast_calloc(1, l + 1);
1817 lseek(fd, 0, SEEK_SET);
1821 term_strip(final_buf, buf, l);
1822 final_buf[l] = '\0';
1824 astman_append(s, S_OR(final_buf, buf));
1829 astman_append(s, "--END COMMAND--\r\n\r\n");
1831 ast_free(final_buf);
1835 /* helper function for originate */
1836 struct fast_originate_helper {
1837 char tech[AST_MAX_EXTENSION];
1838 char data[AST_MAX_EXTENSION];
1840 char app[AST_MAX_APP];
1841 char appdata[AST_MAX_EXTENSION];
1842 char cid_name[AST_MAX_EXTENSION];
1843 char cid_num[AST_MAX_EXTENSION];
1844 char context[AST_MAX_CONTEXT];
1845 char exten[AST_MAX_EXTENSION];
1846 char idtext[AST_MAX_EXTENSION];
1847 char account[AST_MAX_ACCOUNT_CODE];
1849 struct ast_variable *vars;
1852 static void *fast_originate(void *data)
1854 struct fast_originate_helper *in = data;
1857 struct ast_channel *chan = NULL;
1858 char requested_channel[AST_CHANNEL_NAME];
1860 if (!ast_strlen_zero(in->app)) {
1861 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1862 S_OR(in->cid_num, NULL),
1863 S_OR(in->cid_name, NULL),
1864 in->vars, in->account, &chan);
1866 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1867 S_OR(in->cid_num, NULL),
1868 S_OR(in->cid_name, NULL),
1869 in->vars, in->account, &chan);
1873 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
1874 /* Tell the manager what happened with the channel */
1875 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1883 "CallerIDNum: %s\r\n"
1884 "CallerIDName: %s\r\n",
1885 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
1886 chan ? chan->uniqueid : "<null>",
1887 S_OR(in->cid_num, "<unknown>"),
1888 S_OR(in->cid_name, "<unknown>")
1891 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1893 ast_channel_unlock(chan);
1898 static char mandescr_originate[] =
1899 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1900 " Application/Data\n"
1901 "Variables: (Names marked with * are required)\n"
1902 " *Channel: Channel name to call\n"
1903 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1904 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1905 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1906 " Application: Application to use\n"
1907 " Data: Data to use (requires 'Application')\n"
1908 " Timeout: How long to wait for call to be answered (in ms)\n"
1909 " CallerID: Caller ID to be set on the outgoing channel\n"
1910 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1911 " Account: Account code\n"
1912 " Async: Set to 'true' for fast origination\n";
1914 static int action_originate(struct mansession *s, const struct message *m)
1916 const char *name = astman_get_header(m, "Channel");
1917 const char *exten = astman_get_header(m, "Exten");
1918 const char *context = astman_get_header(m, "Context");
1919 const char *priority = astman_get_header(m, "Priority");
1920 const char *timeout = astman_get_header(m, "Timeout");
1921 const char *callerid = astman_get_header(m, "CallerID");
1922 const char *account = astman_get_header(m, "Account");
1923 const char *app = astman_get_header(m, "Application");
1924 const char *appdata = astman_get_header(m, "Data");
1925 const char *async = astman_get_header(m, "Async");
1926 const char *id = astman_get_header(m, "ActionID");
1927 struct ast_variable *vars = astman_get_variables(m);
1929 char *l = NULL, *n = NULL;
1939 astman_send_error(s, m, "Channel not specified");
1942 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1943 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1944 astman_send_error(s, m, "Invalid priority\n");
1948 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1949 astman_send_error(s, m, "Invalid timeout\n");
1952 ast_copy_string(tmp, name, sizeof(tmp));
1954 data = strchr(tmp, '/');
1956 astman_send_error(s, m, "Invalid channel\n");
1960 ast_copy_string(tmp2, callerid, sizeof(tmp2));
1961 ast_callerid_parse(tmp2, &n, &l);
1963 if (ast_strlen_zero(n))
1967 ast_shrink_phone_number(l);
1968 if (ast_strlen_zero(l))
1971 if (ast_true(async)) {
1972 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1976 if (!ast_strlen_zero(id))
1977 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1978 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1979 ast_copy_string(fast->data, data, sizeof(fast->data));
1980 ast_copy_string(fast->app, app, sizeof(fast->app));
1981 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1983 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1985 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1987 ast_copy_string(fast->context, context, sizeof(fast->context));
1988 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1989 ast_copy_string(fast->account, account, sizeof(fast->account));
1991 fast->priority = pi;
1992 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
1998 } else if (!ast_strlen_zero(app)) {
1999 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2001 if (exten && context && pi)
2002 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2004 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2009 astman_send_ack(s, m, "Originate successfully queued");
2011 astman_send_error(s, m, "Originate failed");
2015 /*! \brief Help text for manager command mailboxstatus
2017 static char mandescr_mailboxstatus[] =
2018 "Description: Checks a voicemail account for status.\n"
2019 "Variables: (Names marked with * are required)\n"
2020 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2021 " ActionID: Optional ActionID for message matching.\n"
2022 "Returns number of messages.\n"
2023 " Message: Mailbox Status\n"
2024 " Mailbox: <mailboxid>\n"
2025 " Waiting: <count>\n"
2028 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2030 const char *mailbox = astman_get_header(m, "Mailbox");
2033 if (ast_strlen_zero(mailbox)) {
2034 astman_send_error(s, m, "Mailbox not specified");
2037 ret = ast_app_has_voicemail(mailbox, NULL);
2038 astman_start_ack(s, m);
2039 astman_append(s, "Message: Mailbox Status\r\n"
2041 "Waiting: %d\r\n\r\n", mailbox, ret);
2045 static char mandescr_mailboxcount[] =
2046 "Description: Checks a voicemail account for new messages.\n"
2047 "Variables: (Names marked with * are required)\n"
2048 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2049 " ActionID: Optional ActionID for message matching.\n"
2050 "Returns number of new and old messages.\n"
2051 " Message: Mailbox Message Count\n"
2052 " Mailbox: <mailboxid>\n"
2053 " NewMessages: <count>\n"
2054 " OldMessages: <count>\n"
2056 static int action_mailboxcount(struct mansession *s, const struct message *m)
2058 const char *mailbox = astman_get_header(m, "Mailbox");
2059 int newmsgs = 0, oldmsgs = 0;
2061 if (ast_strlen_zero(mailbox)) {
2062 astman_send_error(s, m, "Mailbox not specified");
2065 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2066 astman_start_ack(s, m);
2067 astman_append(s, "Message: Mailbox Message Count\r\n"
2069 "NewMessages: %d\r\n"
2070 "OldMessages: %d\r\n"
2072 mailbox, newmsgs, oldmsgs);
2076 static char mandescr_extensionstate[] =
2077 "Description: Report the extension state for given extension.\n"
2078 " If the extension has a hint, will use devicestate to check\n"
2079 " the status of the device connected to the extension.\n"
2080 "Variables: (Names marked with * are required)\n"
2081 " *Exten: Extension to check state on\n"
2082 " *Context: Context for extension\n"
2083 " ActionId: Optional ID for this transaction\n"
2084 "Will return an \"Extension Status\" message.\n"
2085 "The response will include the hint for the extension and the status.\n";
2087 static int action_extensionstate(struct mansession *s, const struct message *m)
2089 const char *exten = astman_get_header(m, "Exten");
2090 const char *context = astman_get_header(m, "Context");
2091 char hint[256] = "";
2093 if (ast_strlen_zero(exten)) {
2094 astman_send_error(s, m, "Extension not specified");
2097 if (ast_strlen_zero(context))
2098 context = "default";
2099 status = ast_extension_state(NULL, context, exten);
2100 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2101 astman_start_ack(s, m);
2102 astman_append(s, "Message: Extension Status\r\n"
2106 "Status: %d\r\n\r\n",
2107 exten, context, hint, status);
2111 static char mandescr_timeout[] =
2112 "Description: Hangup a channel after a certain time.\n"
2113 "Variables: (Names marked with * are required)\n"
2114 " *Channel: Channel name to hangup\n"
2115 " *Timeout: Maximum duration of the call (sec)\n"
2116 "Acknowledges set time with 'Timeout Set' message\n";
2118 static int action_timeout(struct mansession *s, const struct message *m)
2120 struct ast_channel *c;
2121 const char *name = astman_get_header(m, "Channel");
2122 int timeout = atoi(astman_get_header(m, "Timeout"));
2124 if (ast_strlen_zero(name)) {
2125 astman_send_error(s, m, "No channel specified");
2129 astman_send_error(s, m, "No timeout specified");
2132 c = ast_get_channel_by_name_locked(name);
2134 astman_send_error(s, m, "No such channel");
2137 ast_channel_setwhentohangup(c, timeout);
2138 ast_channel_unlock(c);
2139 astman_send_ack(s, m, "Timeout Set");
2144 * Send any applicable events to the client listening on this socket.
2145 * Wait only for a finite time on each event, and drop all events whether
2146 * they are successfully sent or not.
2148 static int process_events(struct mansession *s)
2152 ast_mutex_lock(&s->__lock);
2154 struct eventqent *eqe;
2156 while ( (eqe = NEW_EVENT(s)) ) {
2158 if (!ret && s->authenticated &&
2159 (s->readperm & eqe->category) == eqe->category &&
2160 (s->send_events & eqe->category) == eqe->category) {
2161 if (send_string(s, eqe->eventdata) < 0)
2162 ret = -1; /* don't send more */
2164 s->last_ev = unref_event(s->last_ev);
2167 ast_mutex_unlock(&s->__lock);
2171 static char mandescr_userevent[] =
2172 "Description: Send an event to manager sessions.\n"
2173 "Variables: (Names marked with * are required)\n"
2174 " *UserEvent: EventStringToSend\n"
2175 " Header1: Content1\n"
2176 " HeaderN: ContentN\n";
2178 static int action_userevent(struct mansession *s, const struct message *m)
2180 const char *event = astman_get_header(m, "UserEvent");
2181 char body[2048] = "";
2183 for (x = 0; x < m->hdrcount; x++) {
2184 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2185 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2186 bodylen += strlen(m->headers[x]);
2187 ast_copy_string(body + bodylen, "\r\n", 3);
2192 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2196 static char mandescr_coresettings[] =
2197 "Description: Query for Core PBX settings.\n"
2198 "Variables: (Names marked with * are optional)\n"
2199 " *ActionID: ActionID of this transaction\n";
2201 /*! \brief Show PBX core settings information */
2202 static int action_coresettings(struct mansession *s, const struct message *m)
2204 const char *actionid = astman_get_header(m, "ActionID");
2205 char idText[150] = "";
2207 if (!ast_strlen_zero(actionid))
2208 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2210 astman_append(s, "Response: Success\r\n"
2212 "AMIversion: %s\r\n"
2213 "AsteriskVersion: %s\r\n"
2214 "SystemName: %s\r\n"
2215 "CoreMaxCalls: %d\r\n"
2216 "CoreMaxLoadAvg: %f\r\n"
2217 "CoreRunUser: %s\r\n"
2218 "CoreRunGroup: %s\r\n"
2219 "CoreMaxFilehandles: %d\r\n"
2220 "CoreRealTimeEnabled: %s\r\n"
2221 "CoreCDRenabled: %s\r\n"
2222 "CoreHTTPenabled: %s\r\n"
2227 ast_config_AST_SYSTEM_NAME,
2230 ast_config_AST_RUN_USER,
2231 ast_config_AST_RUN_GROUP,
2233 ast_realtime_enabled() ? "Yes" : "No",
2234 check_cdr_enabled() ? "Yes" : "No",
2235 check_webmanager_enabled() ? "Yes" : "No"
2240 static char mandescr_corestatus[] =
2241 "Description: Query for Core PBX status.\n"
2242 "Variables: (Names marked with * are optional)\n"
2243 " *ActionID: ActionID of this transaction\n";
2245 /*! \brief Show PBX core status information */
2246 static int action_corestatus(struct mansession *s, const struct message *m)
2248 const char *actionid = astman_get_header(m, "ActionID");
2250 char startuptime[150];
2251 char reloadtime[150];
2254 if (!ast_strlen_zero(actionid))
2255 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2257 ast_localtime(&ast_startuptime, &tm, NULL);
2258 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2259 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2260 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2262 astman_append(s, "Response: Success\r\n"
2264 "CoreStartupTime: %s\r\n"
2265 "CoreReloadTime: %s\r\n"
2266 "CoreCurrentCalls: %d\r\n"
2271 ast_active_channels()
2276 static char mandescr_reload[] =
2277 "Description: Send a reload event.\n"
2278 "Variables: (Names marked with * are optional)\n"
2279 " *ActionID: ActionID of this transaction\n"
2280 " *Module: Name of the module to reload\n";
2282 /*! \brief Send a reload event */
2283 static int action_reload(struct mansession *s, const struct message *m)
2285 const char *actionid = astman_get_header(m, "ActionID");
2286 const char *module = astman_get_header(m, "Module");
2287 int res = ast_module_reload(S_OR(module, NULL));
2288 char idText[80] = "";
2290 if (!ast_strlen_zero(actionid))
2291 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2293 astman_append(s, "Response: Success\r\n%s", idText);
2295 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2299 static char mandescr_coreshowchannels[] =
2300 "Description: List currently defined channels and some information\n"
2303 " ActionID: Optional Action id for message matching.\n";
2305 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2306 * and some information about them. */
2307 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2309 const char *actionid = astman_get_header(m, "ActionID");
2310 char actionidtext[256] = "";
2311 struct ast_channel *c = NULL;
2313 int duration, durh, durm, durs;
2315 if (!ast_strlen_zero(actionid))
2316 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2318 astman_send_listack(s, m, "Channels will follow", "start");
2320 while ((c = ast_channel_walk_locked(c)) != NULL) {
2321 struct ast_channel *bc = ast_bridged_channel(c);
2322 char durbuf[10] = "";
2324 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2325 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2326 durh = duration / 3600;
2327 durm = (duration % 3600) / 60;
2328 durs = duration % 60;
2329 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2338 "ChannelState: %d\r\n"
2339 "ChannelStateDesc: %s\r\n"
2340 "Application: %s\r\n"
2341 "ApplicationData: %s\r\n"
2342 "CallerIDnum: %s\r\n"
2344 "AccountCode: %s\r\n"
2345 "BridgedChannel: %s\r\n"
2346 "BridgedUniqueID: %s\r\n"
2347 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2348 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2349 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2350 ast_channel_unlock(c);
2355 "Event: CoreShowChannelsComplete\r\n"
2356 "EventList: Complete\r\n"
2359 "\r\n", numchans, actionidtext);
2364 static char mandescr_modulecheck[] =
2365 "Description: Checks if Asterisk module is loaded\n"
2367 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2368 " Module: <name> Asterisk module name (not including extension)\n"
2370 "Will return Success/Failure\n"
2371 "For success returns, the module revision number is included.\n";
2373 /* Manager function to check if module is loaded */
2374 static int manager_modulecheck(struct mansession *s, const struct message *m)
2377 const char *module = astman_get_header(m, "Module");
2378 const char *id = astman_get_header(m,"ActionID");
2379 char idText[BUFSIZ];
2380 const char *version;
2381 char filename[BUFSIZ/2];
2384 snprintf(filename, sizeof(filename), module);
2385 if ((cut = strchr(filename, '.'))) {
2388 cut = filename + strlen(filename);
2390 sprintf(cut, ".so");
2391 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2392 res = ast_module_check(filename);
2394 astman_send_error(s, m, "Module not loaded");
2398 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2399 version = ast_file_version_find(filename);
2401 if (!ast_strlen_zero(id))
2402 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2403 astman_append(s, "Response: Success\r\n%s", idText);
2404 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2408 static char mandescr_moduleload[] =
2409 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2411 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2412 " Module: <name> Asterisk module name (including .so extension)\n"
2413 " or subsystem identifier:\n"
2414 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2415 " LoadType: load | unload | reload\n"
2416 " The operation to be done on module\n"
2417 " If no module is specified for a reload loadtype, all modules are reloaded";
2419 static int manager_moduleload(struct mansession *s, const struct message *m)
2422 const char *module = astman_get_header(m, "Module");
2423 const char *loadtype = astman_get_header(m, "LoadType");
2425 if (!loadtype || strlen(loadtype) == 0)
2426 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2427 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2428 astman_send_error(s, m, "Need module name");
2430 if (!strcasecmp(loadtype, "load")) {
2431 res = ast_load_resource(module);
2433 astman_send_error(s, m, "Could not load module.");
2435 astman_send_ack(s, m, "Module loaded.");
2436 } else if (!strcasecmp(loadtype, "unload")) {
2437 res = ast_unload_resource(module, AST_FORCE_SOFT);
2439 astman_send_error(s, m, "Could not unload module.");
2441 astman_send_ack(s, m, "Module unloaded.");
2442 } else if (!strcasecmp(loadtype, "reload")) {
2443 if (module != NULL) {
2444 res = ast_module_reload(module);
2446 astman_send_error(s, m, "No such module.");
2448 astman_send_error(s, m, "Module does not support reload action.");
2450 astman_send_ack(s, m, "Module reloaded.");
2452 ast_module_reload(NULL); /* Reload all modules */
2453 astman_send_ack(s, m, "All modules reloaded");
2456 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2461 * Done with the action handlers here, we start with the code in charge
2462 * of accepting connections and serving them.
2463 * accept_thread() forks a new thread for each connection, session_do(),
2464 * which in turn calls get_input() repeatedly until a full message has
2465 * been accumulated, and then invokes process_message() to pass it to
2466 * the appropriate handler.
2470 * Process an AMI message, performing desired action.
2471 * Return 0 on success, -1 on error that require the session to be destroyed.
2473 static int process_message(struct mansession *s, const struct message *m)
2475 char action[80] = "";
2477 struct manager_action *tmp;
2478 const char *user = astman_get_header(m, "Username");
2480 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2481 ast_debug(1, "Manager received command '%s'\n", action);
2483 if (ast_strlen_zero(action)) {
2484 ast_mutex_lock(&s->__lock);
2485 astman_send_error(s, m, "Missing action in request");
2486 ast_mutex_unlock(&s->__lock);
2490 if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2491 ast_mutex_lock(&s->__lock);
2492 astman_send_error(s, m, "Permission denied");
2493 ast_mutex_unlock(&s->__lock);
2497 if (!allowmultiplelogin && !s->authenticated && user &&
2498 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2499 if (check_manager_session_inuse(user)) {
2501 ast_mutex_lock(&s->__lock);
2502 astman_send_error(s, m, "Login Already In Use");
2503 ast_mutex_unlock(&s->__lock);
2508 AST_RWLIST_RDLOCK(&actions);
2509 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2510 if (strcasecmp(action, tmp->action))
2512 if ((s->writeperm & tmp->authority) == tmp->authority)
2513 ret = tmp->func(s, m);
2515 astman_send_error(s, m, "Permission denied");
2518 AST_RWLIST_UNLOCK(&actions);
2522 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2523 ast_mutex_lock(&s->__lock);
2524 astman_send_error(s, m, buf);
2525 ast_mutex_unlock(&s->__lock);
2529 /* Once done with our message, deliver any pending events */
2530 return process_events(s);
2534 * Read one full line (including crlf) from the manager socket.
2536 * \r\n is the only valid terminator for the line.
2537 * (Note that, later, '\0' will be considered as the end-of-line marker,
2538 * so everything between the '\0' and the '\r\n' will not be used).
2539 * Also note that we assume output to have at least "maxlen" space.
2542 static int get_input(struct mansession *s, char *output)
2545 int maxlen = sizeof(s->inbuf) - 1;
2546 char *src = s->inbuf;
2549 * Look for \r\n within the buffer. If found, copy to the output
2550 * buffer and return, trimming the \r\n (not used afterwards).
2552 for (x = 0; x < s->inlen; x++) {
2553 int cr; /* set if we have \r */
2554 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2555 cr = 2; /* Found. Update length to include \r\n */
2556 else if (src[x] == '\n')
2557 cr = 1; /* also accept \n only */
2560 memmove(output, src, x); /*... but trim \r\n */
2561 output[x] = '\0'; /* terminate the string */
2562 x += cr; /* number of bytes used */
2563 s->inlen -= x; /* remaining size */
2564 memmove(src, src + x, s->inlen); /* remove used bytes */
2567 if (s->inlen >= maxlen) {
2568 /* no crlf found, and buffer full - sorry, too long for us */
2569 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2574 /* XXX do we really need this locking ? */
2575 ast_mutex_lock(&s->__lock);
2576 s->waiting_thread = pthread_self();
2577 ast_mutex_unlock(&s->__lock);
2579 res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
2581 ast_mutex_lock(&s->__lock);
2582 s->waiting_thread = AST_PTHREADT_NULL;
2583 ast_mutex_unlock(&s->__lock);
2586 /* If we get a signal from some other thread (typically because
2587 * there are new events queued), return 0 to notify the caller.
2591 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2594 ast_mutex_lock(&s->__lock);
2595 res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2597 res = -1; /* error return */
2600 src[s->inlen] = '\0';
2603 ast_mutex_unlock(&s->__lock);
2607 static int do_message(struct mansession *s)
2609 struct message m = { 0 };
2610 char header_buf[sizeof(s->inbuf)] = { '\0' };
2614 /* Check if any events are pending and do them if needed */
2615 if (process_events(s))
2617 res = get_input(s, header_buf);
2620 } else if (res > 0) {
2621 if (ast_strlen_zero(header_buf))
2622 return process_message(s, &m) ? -1 : 0;
2623 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2624 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2631 /*! \brief The body of the individual manager session.
2632 * Call get_input() to read one line at a time
2633 * (or be woken up on new events), collect the lines in a
2634 * message until found an empty line, and execute the request.
2635 * In any case, deliver events asynchronously through process_events()
2636 * (called from here if no line is available, or at the end of
2637 * process_message(). )
2639 static void *session_do(void *data)
2641 struct server_instance *ser = data;
2642 struct mansession *s = ast_calloc(1, sizeof(*s));
2649 s->writetimeout = 100;
2650 s->waiting_thread = AST_PTHREADT_NULL;
2652 flags = fcntl(ser->fd, F_GETFL);
2653 if (!block_sockets) /* make sure socket is non-blocking */
2654 flags |= O_NONBLOCK;
2656 flags &= ~O_NONBLOCK;
2657 fcntl(ser->fd, F_SETFL, flags);
2659 ast_mutex_init(&s->__lock);
2660 s->send_events = -1;
2661 /* these fields duplicate those in the 'ser' structure */
2664 s->sin = ser->requestor;
2666 AST_LIST_LOCK(&sessions);
2667 AST_LIST_INSERT_HEAD(&sessions, s, list);
2668 ast_atomic_fetchadd_int(&num_sessions, 1);
2669 AST_LIST_UNLOCK(&sessions);
2670 /* Hook to the tail of the event queue */
2671 s->last_ev = grab_last();
2673 astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
2675 if ((res = do_message(s)) < 0)
2678 /* session is over, explain why and terminate */
2679 if (s->authenticated) {
2680 if (manager_displayconnects(s))
2681 ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2682 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2684 if (displayconnects)
2685 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2686 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2689 /* It is possible under certain circumstances for this session thread
2690 to complete its work and exit *before* the thread that created it
2691 has finished executing the ast_pthread_create_background() function.
2692 If this occurs, some versions of glibc appear to act in a buggy
2693 fashion and attempt to write data into memory that it thinks belongs
2694 to the thread but is in fact not owned by the thread (or may have
2695 been freed completely).
2697 Causing this thread to yield to other threads at least one time
2698 appears to work around this bug.
2709 /*! \brief remove at most n_max stale session from the list. */
2710 static void purge_sessions(int n_max)
2712 struct mansession *s;
2713 time_t now = time(NULL);
2715 AST_LIST_LOCK(&sessions);
2716 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2717 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2718 AST_LIST_REMOVE_CURRENT(list);
2719 ast_atomic_fetchadd_int(&num_sessions, -1);
2720 if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
2721 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
2722 s->username, ast_inet_ntoa(s->sin.sin_addr));
2724 free_session(s); /* XXX outside ? */
2729 AST_LIST_TRAVERSE_SAFE_END;
2730 AST_LIST_UNLOCK(&sessions);
2734 * events are appended to a queue from where they
2735 * can be dispatched to clients.
2737 static int append_event(const char *str, int category)
2739 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2740 static int seq; /* sequence number */
2745 /* need to init all fields, because ast_malloc() does not */
2747 tmp->category = category;
2748 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2749 AST_LIST_NEXT(tmp, eq_next) = NULL;
2750 strcpy(tmp->eventdata, str);
2752 AST_LIST_LOCK(&all_events);
2753 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
2754 AST_LIST_UNLOCK(&all_events);
2759 /* XXX see if can be moved inside the function */
2760 AST_THREADSTORAGE(manager_event_buf);
2761 #define MANAGER_EVENT_BUF_INITSIZE 256
2763 /*! \brief manager_event: Send AMI event to client */
2764 int __manager_event(int category, const char *event,
2765 const char *file, int line, const char *func, const char *fmt, ...)
2767 struct mansession *s;
2768 struct manager_custom_hook *hook;
2769 struct ast_str *auth = ast_str_alloca(80);
2770 const char *cat_str;
2773 struct ast_str *buf;
2775 /* Abort if there aren't any manager sessions */
2779 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2782 cat_str = authority_to_str(category, &auth);
2783 ast_str_set(&buf, 0,
2784 "Event: %s\r\nPrivilege: %s\r\n",
2787 if (timestampevents) {
2789 ast_str_append(&buf, 0,
2790 "Timestamp: %ld.%06lu\r\n",
2791 now.tv_sec, (unsigned long) now.tv_usec);
2793 if (manager_debug) {
2795 ast_str_append(&buf, 0,
2796 "SequenceNumber: %d\r\n",
2797 ast_atomic_fetchadd_int(&seq, 1));
2798 ast_str_append(&buf, 0,
2799 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
2803 ast_str_append_va(&buf, 0, fmt, ap);
2806 ast_str_append(&buf, 0, "\r\n");
2808 append_event(buf->str, category);
2810 /* Wake up any sleeping sessions */
2811 AST_LIST_LOCK(&sessions);
2812 AST_LIST_TRAVERSE(&sessions, s, list) {
2813 ast_mutex_lock(&s->__lock);
2814 if (s->waiting_thread != AST_PTHREADT_NULL)
2815 pthread_kill(s->waiting_thread, SIGURG);
2816 ast_mutex_unlock(&s->__lock);
2818 AST_LIST_UNLOCK(&sessions);
2820 AST_RWLIST_RDLOCK(&manager_hooks);
2821 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
2822 hook->helper(category, event, buf->str);
2824 AST_RWLIST_UNLOCK(&manager_hooks);
2830 * support functions to register/unregister AMI action handlers,
2832 int ast_manager_unregister(char *action)
2834 struct manager_action *cur;
2836 AST_RWLIST_WRLOCK(&actions);
2837 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
2838 if (!strcasecmp(action, cur->action)) {
2839 AST_RWLIST_REMOVE_CURRENT(list);
2841 ast_verb(2, "Manager unregistered action %s\n", action);
2845 AST_RWLIST_TRAVERSE_SAFE_END;
2846 AST_RWLIST_UNLOCK(&actions);
2851 static int manager_state_cb(char *context, char *exten, int state, void *data)
2853 /* Notify managers of change */
2855 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
2857 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
2861 static int ast_manager_register_struct(struct manager_action *act)
2863 struct manager_action *cur, *prev = NULL;
2865 AST_RWLIST_WRLOCK(&actions);
2866 AST_RWLIST_TRAVERSE(&actions, cur, list) {
2867 int ret = strcasecmp(cur->action, act->action);
2869 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2870 AST_RWLIST_UNLOCK(&actions);
2873 if (ret > 0) { /* Insert these alphabetically */
2880 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
2882 AST_RWLIST_INSERT_HEAD(&actions, act, list);
2884 ast_verb(2, "Manager registered action %s\n", act->action);
2886 AST_RWLIST_UNLOCK(&actions);
2891 /*! \brief register a new command with manager, including online help. This is
2892 the preferred way to register a manager command */
2893 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2895 struct manager_action *cur = NULL;
2897 if (!(cur = ast_calloc(1, sizeof(*cur))))
2900 cur->action = action;
2901 cur->authority = auth;
2903 cur->synopsis = synopsis;
2904 cur->description = description;
2906 ast_manager_register_struct(cur);
2911 END Doxygen group */
2914 * The following are support functions for AMI-over-http.
2915 * The common entry point is generic_http_callback(),
2916 * which extracts HTTP header and URI fields and reformats
2917 * them into AMI messages, locates a proper session
2918 * (using the mansession_id Cookie or GET variable),
2919 * and calls process_message() as for regular AMI clients.
2920 * When done, the output (which goes to a temporary file)
2921 * is read back into a buffer and reformatted as desired,
2922 * then fed back to the client over the original socket.
2925 enum output_format {
2931 static char *contenttype[] = {
2932 [FORMAT_RAW] = "plain",
2933 [FORMAT_HTML] = "html",
2934 [FORMAT_XML] = "xml",
2938 * locate an http session in the list. The search key (ident) is
2939 * the value of the mansession_id cookie (0 is not valid and means
2940 * a session on the AMI socket).
2942 static struct mansession *find_session(unsigned long ident)
2944 struct mansession *s;
2949 AST_LIST_LOCK(&sessions);
2950 AST_LIST_TRAVERSE(&sessions, s, list) {
2951 ast_mutex_lock(&s->__lock);
2952 if (s->managerid == ident && !s->needdestroy) {
2953 ast_atomic_fetchadd_int(&s->inuse, 1);
2956 ast_mutex_unlock(&s->__lock);
2958 AST_LIST_UNLOCK(&sessions);
2963 int astman_verify_session_readpermissions(unsigned long ident, int perm)
2966 struct mansession *s;
2968 AST_LIST_LOCK(&sessions);
2969 AST_LIST_TRAVERSE(&sessions, s, list) {
2970 ast_mutex_lock(&s->__lock);
2971 if ((s->managerid == ident) && (s->readperm & perm)) {
2973 ast_mutex_unlock(&s->__lock);
2976 ast_mutex_unlock(&s->__lock);
2978 AST_LIST_UNLOCK(&sessions);
2982 int astman_verify_session_writepermissions(unsigned long ident, int perm)
2985 struct mansession *s;
2987 AST_LIST_LOCK(&sessions);
2988 AST_LIST_TRAVERSE(&sessions, s, list) {
2989 ast_mutex_lock(&s->__lock);
2990 if ((s->managerid == ident) && (s->writeperm & perm)) {
2992 ast_mutex_unlock(&s->__lock);
2995 ast_mutex_unlock(&s->__lock);
2997 AST_LIST_UNLOCK(&sessions);
3002 * convert to xml with various conversion:
3003 * mode & 1 -> lowercase;
3004 * mode & 2 -> replace non-alphanumeric chars with underscore
3006 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
3008 /* store in a local buffer to avoid calling ast_str_append too often */
3011 int space = sizeof(buf);
3012 /* repeat until done and nothing to flush */
3013 for ( ; *src || dst != buf ; src++) {
3014 if (*src == '\0' || space < 10) { /* flush */
3016 ast_str_append(out, 0, "%s", buf);
3018 space = sizeof(buf);
3023 if ( (mode & 2) && !isalnum(*src)) {
3030 strcpy(dst, "<");
3035 strcpy(dst, ">");
3040 strcpy(dst, """);
3045 strcpy(dst, "'");
3050 strcpy(dst, "&");
3056 *dst++ = mode ? tolower(*src) : *src;
3062 struct variable_count {
3067 static int compress_char(char c)
3072 else if (c >= 'a' && c <= 'z')
3080 static int variable_count_hash_fn(const void *vvc, const int flags)
3082 const struct variable_count *vc = vvc;
3084 for (i = 0; i < 5; i++) {
3085 if (vc->varname[i] == '\0')
3087 res += compress_char(vc->varname[i]) << (i * 6);
3092 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
3094 /* Due to the simplicity of struct variable_count, it makes no difference
3095 * if you pass in objects or strings, the same operation applies. This is
3096 * due to the fact that the hash occurs on the first element, which means
3097 * the address of both the struct and the string are exactly the same. */
3098 struct variable_count *vc = obj;
3100 return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
3103 /*! \brief Convert the input into XML or HTML.
3104 * The input is supposed to be a sequence of lines of the form
3106 * optionally followed by a blob of unformatted text.
3107 * A blank line is a section separator. Basically, this is a
3108 * mixture of the format of Manager Interface and CLI commands.
3109 * The unformatted text is considered as a single value of a field
3110 * named 'Opaque-data'.
3112 * At the moment the output format is the following (but it may
3113 * change depending on future requirements so don't count too
3114 * much on it when writing applications):
3116 * General: the unformatted text is used as a value of
3117 * XML output: to be completed
3120 * Each section is within <response type="object" id="xxx">
3121 * where xxx is taken from ajaxdest variable or defaults to unknown
3122 * Each row is reported as an attribute Name="value" of an XML
3123 * entity named from the variable ajaxobjtype, default to "generic"
3127 * each Name-value pair is output as a single row of a two-column table.
3128 * Sections (blank lines in the input) are separated by a <HR>
3131 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
3133 struct ast_variable *v;
3134 const char *dest = NULL;
3136 const char *objtype = NULL;
3137 int in_data = 0; /* parsing data */
3139 int xml = (format == FORMAT_XML);
3140 struct variable_count *vc = NULL;
3141 struct ao2_container *vco = NULL;
3143 for (v = vars; v; v = v->next) {
3144 if (!dest && !strcasecmp(v->name, "ajaxdest"))
3146 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
3152 objtype = "generic";
3154 /* we want to stop when we find an empty line */
3156 val = strsep(&in, "\r\n"); /* mark start and end of line */
3157 if (in && *in == '\n') /* remove trailing \n if any */
3159 ast_trim_blanks(val);
3160 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
3161 if (ast_strlen_zero(val)) {
3162 if (in_data) { /* close data */
3163 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3166 ast_str_append(out, 0, xml ? " /></response>\n" :
3167 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3174 /* we expect Name: value lines */
3178 var = strsep(&val, ":");
3179 if (val) { /* found the field name */
3180 val = ast_skip_blanks(val);
3181 ast_trim_blanks(var);
3182 } else { /* field name not found, move to opaque mode */
3184 var = "Opaque-data";
3190 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
3192 ast_str_append(out, 0, "<body>\n");
3193 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
3197 if (!in_data) { /* build appropriate line start */
3198 ast_str_append(out, 0, xml ? " " : "<tr><td>");
3199 if ((vc = ao2_find(vco, var, 0)))
3202 /* Create a new entry for this one */
3203 vc = ao2_alloc(sizeof(*vc), NULL);
3208 xml_copy_escape(out, var, xml ? 1 | 2 : 0);
3210 ast_str_append(out, 0, "-%d", vc->count);
3212 ast_str_append(out, 0, xml ? "='" : "</td><td>");
3213 if (!strcmp(var, "Opaque-data"))
3216 xml_copy_escape(out, val, 0); /* data field */
3218 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3220 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
3223 ast_str_append(out, 0, xml ? " /></response>\n" :
3224 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3229 static struct ast_str *generic_http_callback(enum output_format format,
3230 struct sockaddr_in *requestor, const char *uri,
3231 struct ast_variable *params, int *status,
3232 char **title, int *contentlength)
3234 struct mansession *s = NULL;
3235 unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
3237 struct ast_variable *v;
3238 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
3239 struct ast_str *out = NULL;
3240 struct message m = { 0 };
3244 for (v = params; v; v = v->next) {
3245 if (!strcasecmp(v->name, "mansession_id")) {
3246 sscanf(v->value, "%lx", &ident);
3251 if (!(s = find_session(ident))) {
3252 /* Create new session.
3253 * While it is not in the list we don't need any locking
3255 if (!(s = ast_calloc(1, sizeof(*s)))) {
3257 goto generic_callback_out;
3259 s->sin = *requestor;
3261 s->waiting_thread = AST_PTHREADT_NULL;
3263 ast_mutex_init(&s->__lock);
3264 ast_mutex_lock(&s->__lock);
3266 s->managerid = rand() | 1; /* make sure it is non-zero */
3267 s->last_ev = grab_last();
3268 AST_LIST_LOCK(&sessions);
3269 AST_LIST_INSERT_HEAD(&sessions, s, list);
3270 ast_atomic_fetchadd_int(&num_sessions, 1);
3271 AST_LIST_UNLOCK(&sessions);
3274 ast_mutex_unlock(&s->__lock);
3276 if (!(out = ast_str_create(1024))) {
3278 goto generic_callback_out;
3281 s->fd = mkstemp(template); /* create a temporary file for command output */
3283 s->f = fdopen(s->fd, "w+");
3285 for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
3286 hdrlen = strlen(v->name) + strlen(v->value) + 3;
3287 m.headers[m.hdrcount] = alloca(hdrlen);
3288 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
3292 if (process_message(s, &m)) {
3293 if (s->authenticated) {
3294 if (manager_displayconnects(s))
3295 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3296 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3298 if (displayconnects)
3299 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
3300 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
3305 ast_str_append(&out, 0,
3306 "Content-type: text/%s\r\n"
3307 "Cache-Control: no-cache;\r\n"
3308 "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n"
3310 contenttype[format],
3311 s->managerid, httptimeout);
3313 if (format == FORMAT_XML) {
3314 ast_str_append(&out, 0, "<ajax-response>\n");
3315 } else if (format == FORMAT_HTML) {
3317 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
3318 #define TEST_STRING \
3319 "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
3320 user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
3321 <input type=\"submit\"></form>"
3323 ast_str_append(&out, 0, "<title>Asterisk™ Manager Interface</title>");
3324 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
3325 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
3326 ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
3329 if (s->f != NULL) { /* have temporary output */
3331 size_t l = ftell(s->f);
3334 if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
3335 if (format == FORMAT_XML || format == FORMAT_HTML)
3336 xml_translate(&out, buf, params, format);
3338 ast_str_append(&out, 0, buf);
3341 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
3342 xml_translate(&out, "", params, format);
3349 if (format == FORMAT_XML) {
3350 ast_str_append(&out, 0, "</ajax-response>\n");
3351 } else if (format == FORMAT_HTML)
3352 ast_str_append(&out, 0, "</table></body>\r\n");
3354 ast_mutex_lock(&s->__lock);
3355 /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */
3356 s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
3358 if (s->needdestroy) {
3359 if (s->inuse == 1) {
3360 ast_debug(1, "Need destroy, doing it now!\n");
3363 ast_debug(1, "Need destroy, but can't do it yet!\n");
3364 if (s->waiting_thread != AST_PTHREADT_NULL)
3365 pthread_kill(s->waiting_thread, SIGURG);
3370 ast_mutex_unlock(&s->__lock);
3374 generic_callback_out:
3376 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
3380 static struct ast_str *manager_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
3382 return generic_http_callback(FORMAT_HTML, &ser->requestor, uri, params, status, title, contentlength);
3385 static struct ast_str *mxml_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
3387 return generic_http_callback(FORMAT_XML, &ser->requestor, uri, params, status, title, contentlength);
3390 static struct ast_str *rawman_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
3392 return generic_http_callback(FORMAT_RAW, &ser->requestor, uri, params, status, title, contentlength);
3395 struct ast_http_uri rawmanuri = {
3396 .description = "Raw HTTP Manager Event Interface",
3399 .callback = rawman_http_callback,
3402 struct ast_http_uri manageruri = {
3403 .description = "HTML Manager Event Interface",
3406 .callback = manager_http_callback,
3409 struct ast_http_uri managerxmluri = {
3410 .description = "XML Manager Event Interface",
3413 .callback = mxml_http_callback,
3416 static int registered = 0;
3417 static int webregged = 0;
3419 /*! \brief cleanup code called at each iteration of server_root,
3420 * guaranteed to happen every 5 seconds at most
3422 static void purge_old_stuff(void *data)
3428 struct tls_config ami_tls_cfg;
3429 static struct server_args ami_desc = {
3431 .master = AST_PTHREADT_NULL,
3433 .poll_timeout = 5000, /* wake up every 5 seconds */
3434 .periodic_fn = purge_old_stuff,
3435 .name = "AMI server",
3436 .accept_fn = server_root, /* thread doing the accept() */
3437 .worker_fn = session_do, /* thread handling the session */
3440 static struct server_args amis_desc = {
3442 .master = AST_PTHREADT_NULL,
3443 .tls_cfg = &ami_tls_cfg,
3444 .poll_timeout = -1, /* the other does the periodic cleanup */
3445 .name = "AMI TLS server",
3446 .accept_fn = server_root, /* thread doing the accept() */
3447 .worker_fn = session_do, /* thread handling the session */
3450 static int __init_manager(int reload)
3452 struct ast_config *ucfg = NULL, *cfg = NULL;
3455 int newhttptimeout = 60;
3456 int have_sslbindaddr = 0;
3458 struct ast_hostent ahp;
3459 struct ast_manager_user *user = NULL;
3460 struct ast_variable *var;
3461 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
3464 /* Register default actions */
3465 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
3466 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
3467 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
3468 ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
3469 ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
3470 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
3471 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
3472 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
3473 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
3474 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
3475 ast_manager_register2("GetConfigJSON", EVENT_FLAG_CONFIG, action_getconfigjson, "Retrieve configuration (JSON format)", mandescr_getconfigjson);
3476 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
3477 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
3478 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
3479 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
3480 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
3481 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
3482 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
3483 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
3484 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
3485 ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
3486 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
3487 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
3488 ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
3489 ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
3490 ast_manager_register2("Reload", EVENT_FLAG_CONFIG, action_reload, "Send a reload event", mandescr_reload);
3491 ast_manager_register2("CoreShowChannels", EVENT_FLAG_SYSTEM, action_coreshowchannels, "List currently active channels", mandescr_coreshowchannels);
3492 ast_manager_register2("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload, "Module management", mandescr_moduleload);
3493 ast_manager_register2("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck, "Check if module is loaded", mandescr_modulecheck);
3495 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
3496 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
3498 /* Append placeholder event so master_eventq never runs dry */
3499 append_event("Event: Placeholder\r\n\r\n", 0);
3501 if ((cfg = ast_config_load("manager.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
3504 displayconnects = 1;
3506 ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf. Asterisk management interface (AMI) disabled.\n");
3510 /* default values */
3511 memset(&ami_desc.sin, 0, sizeof(struct sockaddr_in));
3512 memset(&amis_desc.sin, 0, sizeof(amis_desc.sin));
3513 amis_desc.sin.sin_port = htons(5039);
3514 ami_desc.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
3516 ami_tls_cfg.enabled = 0;
3517 if (ami_tls_cfg.certfile)
3518 ast_free(ami_tls_cfg.certfile);
3519 ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
3520 if (ami_tls_cfg.cipher)
3521 ast_free(ami_tls_cfg.cipher);
3522 ami_tls_cfg.cipher = ast_strdup("");
3524 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
3526 if (!strcasecmp(var->name, "sslenable"))
3527 ami_tls_cfg.enabled = ast_true(val);
3528 else if (!strcasecmp(var->name, "sslbindport"))
3529 amis_desc.sin.sin_port = htons(atoi(val));
3530 else if (!strcasecmp(var->name, "sslbindaddr")) {
3531 if ((hp = ast_gethostbyname(val, &ahp))) {
3532 memcpy(&amis_desc.sin.sin_addr, hp->h_addr, sizeof(amis_desc.sin.sin_addr));
3533 have_sslbindaddr = 1;
3535 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
3537 } else if (!strcasecmp(var->name, "sslcert")) {
3538 ast_free(ami_tls_cfg.certfile);
3539 ami_tls_cfg.certfile = ast_strdup(val);
3540 } else if (!strcasecmp(var->name, "sslcipher")) {
3541 ast_free(ami_tls_cfg.cipher);
3542 ami_tls_cfg.cipher = ast_strdup(val);
3543 } else if (!strcasecmp(var->name, "enabled")) {
3544 manager_enabled = ast_true(val);
3545 } else if (!strcasecmp(var->name, "block-sockets")) {
3546 block_sockets = ast_true(val);
3547 } else if (!strcasecmp(var->name, "webenabled")) {
3548 webmanager_enabled = ast_true(val);
3549 } else if (!strcasecmp(var->name, "port")) {
3550 ami_desc.sin.sin_port = htons(atoi(val));
3551 } else if (!strcasecmp(var->name, "bindaddr")) {
3552 if (!inet_aton(val, &ami_desc.sin.sin_addr)) {
3553 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
3554 memset(&ami_desc.sin.sin_addr, 0, sizeof(ami_desc.sin.sin_addr));
3556 } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
3557 allowmultiplelogin = ast_true(val);
3558 } else if (!strcasecmp(var->name, "displayconnects")) {
3559 displayconnects = ast_true(val);
3560 } else if (!strcasecmp(var->name, "timestampevents")) {
3561 timestampevents = ast_true(val);
3562 } else if (!strcasecmp(var->name, "debug")) {
3563 manager_debug = ast_true(val);
3564 } else if (!strcasecmp(var->name, "httptimeout")) {
3565 newhttptimeout = atoi(val);
3567 ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
3572 if (manager_enabled)
3573 ami_desc.sin.sin_family = AF_INET;
3574 if (!have_sslbindaddr)
3575 amis_desc.sin.sin_addr = ami_desc.sin.sin_addr;
3576 if (ami_tls_cfg.enabled)
3577 amis_desc.sin.sin_family = AF_INET;
3580 AST_RWLIST_WRLOCK(&users);
3582 /* First, get users from users.conf */
3583 ucfg = ast_config_load("users.conf", config_flags);
3584 if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED)) {
3585 const char *hasmanager;
3586 int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
3588 while ((cat = ast_category_browse(ucfg, cat))) {
3589 if (!strcasecmp(cat, "general"))
3592 hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
3593 if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
3594 const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
3595 const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
3596 const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
3597 const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
3598 const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
3600 /* Look for an existing entry,
3601 * if none found - create one and add it to the list
3603 if (!(user = get_manager_by_name_locked(cat))) {
3604 if (!(user = ast_calloc(1, sizeof(*user))))
3607 /* Copy name over */
3608 ast_copy_string(user->username, cat, sizeof(user->username));
3609 /* Insert into list */
3610 AST_LIST_INSERT_TAIL(&users, user, list);
3612 user->readperm = -1;
3613 user->writeperm = -1;
3614 /* Default displayconnect from [general] */
3615 user->displayconnects = displayconnects;
3616 user->writetimeout = 100;
3620 user_secret = ast_variable_retrieve(ucfg, "general", "secret");
3622 user_read = ast_variable_retrieve(ucfg, "general", "read");
3624 user_write = ast_variable_retrieve(ucfg, "general", "write");
3625 if (!user_displayconnects)
3626 user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
3627 if (!user_writetimeout)
3628 user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
3630 if (!ast_strlen_zero(user_secret)) {
3632 ast_free(user->secret);
3633 user->secret = ast_strdup(user_secret);
3637 user->readperm = get_perm(user_read);
3639 user->writeperm = get_perm(user_write);
3640 if (user_displayconnects)
3641 user->displayconnects = ast_true(user_displayconnects);
3643 if (user_writetimeout) {
3644 int val = atoi(user_writetimeout);
3646 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno);
3648 user->writetimeout = val;
3652 ast_config_destroy(ucfg);
3655 /* cat is NULL here in any case */
3657 while ((cat = ast_category_browse(cfg, cat))) {
3658 struct ast_ha *oldha;
3660 if (!strcasecmp(cat, "general"))
3663 /* Look for an existing entry, if none found - create one and add it to the list */
3664 if (!(user = get_manager_by_name_locked(cat))) {
3665 if (!(user = ast_calloc(1, sizeof(*user))))
3667 /* Copy name over */
3668 ast_copy_string(user->username, cat, sizeof(user->username));
3672 user->writeperm = 0;
3673 /* Default displayconnect from [general] */
3674 user->displayconnects = displayconnects;
3675 user->writetimeout = 100;
3677 /* Insert into list */
3678 AST_RWLIST_INSERT_TAIL(&users, user, list);
3681 /* Make sure we keep this user and don't destroy it during cleanup */
3686 var = ast_variable_browse(cfg, cat);
3687 for (; var; var = var->next) {
3688 if (!strcasecmp(var->name, "secret")) {
3690 ast_free(user->secret);
3691 user->secret = ast_strdup(var->value);
3692 } else if (!strcasecmp(var->name, "deny") ||
3693 !strcasecmp(var->name, "permit")) {
3694 user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
3695 } else if (!strcasecmp(var->name, "read") ) {
3696 user->readperm = get_perm(var->value);
3697 } else if (!strcasecmp(var->name, "write") ) {
3698 user->writeperm = get_perm(var->value);
3699 } else if (!strcasecmp(var->name, "displayconnects") ) {
3700 user->displayconnects = ast_true(var->value);
3701 } else if (!strcasecmp(var->name, "writetimeout")) {
3702 int val = atoi(var->value);
3704 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);