2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief The Asterisk Management Interface - AMI
23 * \author Mark Spencer <markster@digium.com>
25 * \extref OpenSSL http://www.openssl.org - for AMI/SSL
27 * At the moment this file contains a number of functions, namely:
29 * - data structures storing AMI state
30 * - AMI-related API functions, used by internal asterisk components
31 * - handlers for AMI-related CLI functions
32 * - handlers for AMI functions (available through the AMI socket)
33 * - the code for the main AMI listener thread and individual session threads
34 * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
39 /*! \addtogroup Group_AMI AMI functions
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48 #include "asterisk/_private.h"
49 #include "asterisk/paths.h" /* use various ast_config_AST_* */
55 #include "asterisk/channel.h"
56 #include "asterisk/file.h"
57 #include "asterisk/manager.h"
58 #include "asterisk/module.h"
59 #include "asterisk/config.h"
60 #include "asterisk/callerid.h"
61 #include "asterisk/lock.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/app.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/md5.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/tcptls.h"
69 #include "asterisk/http.h"
70 #include "asterisk/ast_version.h"
71 #include "asterisk/threadstorage.h"
72 #include "asterisk/linkedlists.h"
73 #include "asterisk/version.h"
74 #include "asterisk/term.h"
75 #include "asterisk/astobj2.h"
76 #include "asterisk/features.h"
93 * Linked list of events.
94 * Global events are appended to the list by append_event().
95 * The usecount is the number of stored pointers to the element,
96 * excluding the list pointers. So an element that is only in
97 * the list has a usecount of 0, not 1.
99 * Clients have a pointer to the last event processed, and for each
100 * of these clients we track the usecount of the elements.
101 * If we have a pointer to an entry in the list, it is safe to navigate
102 * it forward because elements will not be deleted, but only appended.
103 * The worst that can happen is seeing the pointer still NULL.
105 * When the usecount of an element drops to 0, and the element is the
106 * first in the list, we can remove it. Removal is done within the
107 * main thread, which is woken up for the purpose.
109 * For simplicity of implementation, we make sure the list is never empty.
112 int usecount; /*!< # of clients who still need the event */
114 unsigned int seq; /*!< sequence number */
115 AST_LIST_ENTRY(eventqent) eq_next;
116 char eventdata[1]; /*!< really variable size, allocated by append_event() */
119 static AST_LIST_HEAD_STATIC(all_events, eventqent);
121 static int displayconnects = 1;
122 static int allowmultiplelogin = 1;
123 static int timestampevents;
124 static int httptimeout = 60;
125 static int manager_enabled = 0;
126 static int webmanager_enabled = 0;
128 static int block_sockets;
129 static int num_sessions;
131 static int manager_debug; /*!< enable some debugging code in the manager */
134 * Descriptor for a manager session, either on the AMI socket or over HTTP.
137 * AMI session have managerid == 0; the entry is created upon a connect,
138 * and destroyed with the socket.
139 * HTTP sessions have managerid != 0, the value is used as a search key
140 * to lookup sessions (using the mansession_id cookie).
142 static const char *command_blacklist[] = {
148 pthread_t ms_t; /*!< Execution thread, basically useless */
149 ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
150 /* XXX need to document which fields it is protecting */
151 struct sockaddr_in sin; /*!< address we are connecting from */
152 FILE *f; /*!< fdopen() on the underlying fd */
153 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
154 int inuse; /*!< number of HTTP sessions using this entry */
155 int needdestroy; /*!< Whether an HTTP session should be destroyed */
156 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
157 uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
158 time_t sessionstart; /*!< Session start time */
159 time_t sessiontimeout; /*!< Session timeout if HTTP */
160 char username[80]; /*!< Logged in username */
161 char challenge[10]; /*!< Authentication challenge */
162 int authenticated; /*!< Authentication status */
163 int readperm; /*!< Authorization for reading */
164 int writeperm; /*!< Authorization for writing */
165 char inbuf[1025]; /*!< Buffer */
166 /* we use the extra byte to add a '\0' and simplify parsing */
167 int inlen; /*!< number of buffered bytes */
168 int send_events; /*!< XXX what ? */
169 struct eventqent *last_ev; /*!< last event processed. */
170 int writetimeout; /*!< Timeout for ast_carefulwrite() */
171 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
172 AST_LIST_ENTRY(mansession) list;
175 #define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
177 static AST_LIST_HEAD_STATIC(sessions, mansession);
179 /*! \brief user descriptor, as read from the config file.
181 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
182 * lines which are not supported here, and readperm/writeperm/writetimeout
185 struct ast_manager_user {
188 struct ast_ha *ha; /*!< ACL setting */
189 int readperm; /*! Authorization for reading */
190 int writeperm; /*! Authorization for writing */
191 int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
192 int displayconnects; /*!< XXX unused */
193 int keep; /*!< mark entries created on a reload */
194 AST_RWLIST_ENTRY(ast_manager_user) list;
197 /*! \brief list of users found in the config file */
198 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
200 /*! \brief list of actions registered */
201 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
203 /*! \brief list of hooks registered */
204 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
206 /*! \brief Add a custom hook to be called when an event is fired */
207 void ast_manager_register_hook(struct manager_custom_hook *hook)
209 AST_RWLIST_WRLOCK(&manager_hooks);
210 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
211 AST_RWLIST_UNLOCK(&manager_hooks);
215 /*! \brief Delete a custom hook to be called when an event is fired */
216 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
218 AST_RWLIST_WRLOCK(&manager_hooks);
219 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
220 AST_RWLIST_UNLOCK(&manager_hooks);
225 * Event list management functions.
226 * We assume that the event list always has at least one element,
227 * and the delete code will not remove the last entry even if the
231 static time_t __deb(time_t start, const char *msg)
233 time_t now = time(NULL);
234 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
235 if (start != 0 && now - start > 5)
236 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
240 static void LOCK_EVENTS(void)
242 time_t start = __deb(0, "about to lock events");
243 AST_LIST_LOCK(&all_events);
244 __deb(start, "done lock events");
247 static void UNLOCK_EVENTS(void)
249 __deb(0, "about to unlock events");
250 AST_LIST_UNLOCK(&all_events);
253 static void LOCK_SESS(void)
255 time_t start = __deb(0, "about to lock sessions");
256 AST_LIST_LOCK(&sessions);
257 __deb(start, "done lock sessions");
260 static void UNLOCK_SESS(void)
262 __deb(0, "about to unlock sessions");
263 AST_LIST_UNLOCK(&sessions);
267 int check_manager_enabled()
269 return manager_enabled;
272 int check_webmanager_enabled()
274 return (webmanager_enabled && manager_enabled);
278 * Grab a reference to the last event, update usecount as needed.
279 * Can handle a NULL pointer.
281 static struct eventqent *grab_last(void)
283 struct eventqent *ret;
285 AST_LIST_LOCK(&all_events);
286 ret = AST_LIST_LAST(&all_events);
287 /* the list is never empty now, but may become so when
288 * we optimize it in the future, so be prepared.
291 ast_atomic_fetchadd_int(&ret->usecount, 1);
292 AST_LIST_UNLOCK(&all_events);
297 * Purge unused events. Remove elements from the head
298 * as long as their usecount is 0 and there is a next element.
300 static void purge_events(void)
302 struct eventqent *ev;
304 AST_LIST_LOCK(&all_events);
305 while ( (ev = AST_LIST_FIRST(&all_events)) &&
306 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
307 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
310 AST_LIST_UNLOCK(&all_events);
314 * helper functions to convert back and forth between
315 * string and numeric representation of set of flags
317 static struct permalias {
321 { EVENT_FLAG_SYSTEM, "system" },
322 { EVENT_FLAG_CALL, "call" },
323 { EVENT_FLAG_LOG, "log" },
324 { EVENT_FLAG_VERBOSE, "verbose" },
325 { EVENT_FLAG_COMMAND, "command" },
326 { EVENT_FLAG_AGENT, "agent" },
327 { EVENT_FLAG_USER, "user" },
328 { EVENT_FLAG_CONFIG, "config" },
329 { EVENT_FLAG_DTMF, "dtmf" },
330 { EVENT_FLAG_REPORTING, "reporting" },
331 { EVENT_FLAG_CDR, "cdr" },
332 { EVENT_FLAG_DIALPLAN, "dialplan" },
333 { EVENT_FLAG_ORIGINATE, "originate" },
338 /*! \brief Convert authority code to a list of options */
339 static char *authority_to_str(int authority, struct ast_str **res)
345 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
346 if (authority & perms[i].num) {
347 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
352 if ((*res)->used == 0) /* replace empty string with something sensible */
353 ast_str_append(res, 0, "<none>");
358 /*! Tells you if smallstr exists inside bigstr
359 which is delim by delim and uses no buf or stringsep
360 ast_instring("this|that|more","this",'|') == 1;
362 feel free to move this to app.c -anthm */
363 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
365 const char *val = bigstr, *next;
368 if ((next = strchr(val, delim))) {
369 if (!strncmp(val, smallstr, (next - val)))
374 return !strcmp(smallstr, val);
375 } while (*(val = (next + 1)));
380 static int get_perm(const char *instr)
387 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
388 if (ast_instring(instr, perms[x].label, ','))
396 * A number returns itself, false returns 0, true returns all flags,
397 * other strings return the flags that are set.
399 static int strings_to_mask(const char *string)
403 if (ast_strlen_zero(string))
406 for (p = string; *p; p++)
407 if (*p < '0' || *p > '9')
409 if (!p) /* all digits */
411 if (ast_false(string))
413 if (ast_true(string)) { /* all permissions */
415 for (x = 0; x<sizeof(perms) / sizeof(perms[0]); x++)
419 return get_perm(string);
422 static int check_manager_session_inuse(const char *name)
424 struct mansession *session = NULL;
426 AST_LIST_LOCK(&sessions);
427 AST_LIST_TRAVERSE(&sessions, session, list) {
428 if (!strcasecmp(session->username, name))
431 AST_LIST_UNLOCK(&sessions);
433 return session ? 1 : 0;
438 * lookup an entry in the list of registered users.
439 * must be called with the list lock held.
441 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
443 struct ast_manager_user *user = NULL;
445 AST_RWLIST_TRAVERSE(&users, user, list)
446 if (!strcasecmp(user->username, name))
451 /*! \brief Get displayconnects config option.
452 * \param s manager session to get parameter from.
453 * \return displayconnects config option value.
455 static int manager_displayconnects (struct mansession *s)
457 struct ast_manager_user *user = NULL;
460 AST_RWLIST_RDLOCK(&users);
461 if ((user = get_manager_by_name_locked (s->username)))
462 ret = user->displayconnects;
463 AST_RWLIST_UNLOCK(&users);
468 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
470 struct manager_action *cur;
471 struct ast_str *authority;
476 e->command = "manager show command";
478 "Usage: manager show command <actionname>\n"
479 " Shows the detailed description for a specific Asterisk manager interface command.\n";
484 AST_RWLIST_RDLOCK(&actions);
485 AST_RWLIST_TRAVERSE(&actions, cur, list) {
486 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
487 ret = ast_strdup(cur->action);
488 break; /* make sure we exit even if ast_strdup() returns NULL */
491 AST_RWLIST_UNLOCK(&actions);
494 authority = ast_str_alloca(80);
496 return CLI_SHOWUSAGE;
498 AST_RWLIST_RDLOCK(&actions);
499 AST_RWLIST_TRAVERSE(&actions, cur, list) {
500 for (num = 3; num < a->argc; num++) {
501 if (!strcasecmp(cur->action, a->argv[num])) {
502 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
503 cur->action, cur->synopsis,
504 authority_to_str(cur->authority, &authority),
505 S_OR(cur->description, ""));
509 AST_RWLIST_UNLOCK(&actions);
514 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
518 e->command = "manager debug [on|off]";
519 e->usage = "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
525 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
526 else if (a->argc == 3) {
527 if (!strcasecmp(a->argv[2], "on"))
529 else if (!strcasecmp(a->argv[2], "off"))
532 return CLI_SHOWUSAGE;
537 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
539 struct ast_manager_user *user = NULL;
542 struct ast_str *rauthority = ast_str_alloca(80);
543 struct ast_str *wauthority = ast_str_alloca(80);
547 e->command = "manager show user";
549 " Usage: manager show user <user>\n"
550 " Display all information related to the manager user specified.\n";
557 AST_RWLIST_RDLOCK(&users);
558 AST_RWLIST_TRAVERSE(&users, user, list) {
559 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
560 ret = ast_strdup(user->username);
564 AST_RWLIST_UNLOCK(&users);
569 return CLI_SHOWUSAGE;
571 AST_RWLIST_RDLOCK(&users);
573 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
574 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
575 AST_RWLIST_UNLOCK(&users);
579 ast_cli(a->fd, "\n");
586 "displayconnects: %s\n",
587 (user->username ? user->username : "(N/A)"),
588 (user->secret ? "<Set>" : "(N/A)"),
589 (user->ha ? "yes" : "no"),
590 authority_to_str(user->readperm, &rauthority),
591 authority_to_str(user->writeperm, &wauthority),
592 (user->displayconnects ? "yes" : "no"));
594 AST_RWLIST_UNLOCK(&users);
600 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
602 struct ast_manager_user *user = NULL;
606 e->command = "manager show users";
608 "Usage: manager show users\n"
609 " Prints a listing of all managers that are currently configured on that\n"
616 return CLI_SHOWUSAGE;
618 AST_RWLIST_RDLOCK(&users);
620 /* If there are no users, print out something along those lines */
621 if (AST_RWLIST_EMPTY(&users)) {
622 ast_cli(a->fd, "There are no manager users.\n");
623 AST_RWLIST_UNLOCK(&users);
627 ast_cli(a->fd, "\nusername\n--------\n");
629 AST_RWLIST_TRAVERSE(&users, user, list) {
630 ast_cli(a->fd, "%s\n", user->username);
634 AST_RWLIST_UNLOCK(&users);
636 ast_cli(a->fd, "-------------------\n");
637 ast_cli(a->fd, "%d manager users configured.\n", count_amu);
643 /*! \brief CLI command manager list commands */
644 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
646 struct manager_action *cur;
647 struct ast_str *authority;
648 #define HSMC_FORMAT " %-15.15s %-15.15s %-55.55s\n"
651 e->command = "manager show commands";
653 "Usage: manager show commands\n"
654 " Prints a listing of all the available Asterisk manager interface commands.\n";
659 authority = ast_str_alloca(80);
660 ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
661 ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
663 AST_RWLIST_RDLOCK(&actions);
664 AST_RWLIST_TRAVERSE(&actions, cur, list)
665 ast_cli(a->fd, HSMC_FORMAT, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
666 AST_RWLIST_UNLOCK(&actions);
671 /*! \brief CLI command manager list connected */
672 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
674 struct mansession *s;
675 time_t now = time(NULL);
676 #define HSMCONN_FORMAT1 " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
677 #define HSMCONN_FORMAT2 " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
681 e->command = "manager show connected";
683 "Usage: manager show connected\n"
684 " Prints a listing of the users that are currently connected to the\n"
685 "Asterisk manager interface.\n";
691 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
693 AST_LIST_LOCK(&sessions);
694 AST_LIST_TRAVERSE(&sessions, s, list) {
695 ast_cli(a->fd, HSMCONN_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);
698 AST_LIST_UNLOCK(&sessions);
700 ast_cli(a->fd, "%d users connected.\n", count);
705 /*! \brief CLI command manager list eventq */
706 /* Should change to "manager show connected" */
707 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
712 e->command = "manager show eventq";
714 "Usage: manager show eventq\n"
715 " Prints a listing of all events pending in the Asterisk manger\n"
721 AST_LIST_LOCK(&all_events);
722 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
723 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
724 ast_cli(a->fd, "Category: %d\n", s->category);
725 ast_cli(a->fd, "Event:\n%s", s->eventdata);
727 AST_LIST_UNLOCK(&all_events);
732 /*! \brief CLI command manager reload */
733 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
737 e->command = "manager reload";
739 "Usage: manager reload\n"
740 " Reloads the manager configuration.\n";
746 return CLI_SHOWUSAGE;
752 static struct ast_cli_entry cli_manager[] = {
753 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
754 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
755 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
756 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
757 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
758 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
759 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
760 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
764 * Decrement the usecount for the event; if it goes to zero,
765 * (why check for e->next ?) wakeup the
766 * main thread, which is in charge of freeing the record.
767 * Returns the next record.
769 static struct eventqent *unref_event(struct eventqent *e)
771 ast_atomic_fetchadd_int(&e->usecount, -1);
772 return AST_LIST_NEXT(e, eq_next);
775 static void ref_event(struct eventqent *e)
777 ast_atomic_fetchadd_int(&e->usecount, 1);
781 * destroy a session, leaving the usecount
783 static void free_session(struct mansession *s)
785 struct eventqent *eqe = s->last_ev;
788 ast_mutex_destroy(&s->__lock);
793 static void destroy_session(struct mansession *s)
795 AST_LIST_LOCK(&sessions);
796 AST_LIST_REMOVE(&sessions, s, list);
797 ast_atomic_fetchadd_int(&num_sessions, -1);
799 AST_LIST_UNLOCK(&sessions);
802 const char *astman_get_header(const struct message *m, char *var)
804 int x, l = strlen(var);
806 for (x = 0; x < m->hdrcount; x++) {
807 const char *h = m->headers[x];
808 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
815 struct ast_variable *astman_get_variables(const struct message *m)
818 struct ast_variable *head = NULL, *cur;
820 AST_DECLARE_APP_ARGS(args,
821 AST_APP_ARG(vars)[32];
824 varlen = strlen("Variable: ");
826 for (x = 0; x < m->hdrcount; x++) {
827 char *parse, *var, *val;
829 if (strncasecmp("Variable: ", m->headers[x], varlen))
831 parse = ast_strdupa(m->headers[x] + varlen);
833 AST_STANDARD_APP_ARGS(args, parse);
836 for (y = 0; y < args.argc; y++) {
839 var = val = ast_strdupa(args.vars[y]);
841 if (!val || ast_strlen_zero(var))
843 cur = ast_variable_new(var, val, "");
853 * helper function to send a string to the socket.
854 * Return -1 on error (e.g. buffer full).
856 static int send_string(struct mansession *s, char *string)
858 int len = strlen(string); /* residual length */
860 struct timeval start = ast_tvnow();
866 n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
867 if (n == len /* ok */ || n < 0 /* error */)
869 len -= n; /* skip already written data */
873 n = -1; /* error marker */
874 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
875 if (elapsed > s->writetimeout)
877 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
881 return n < 0 ? -1 : 0;
885 * \brief thread local buffer for astman_append
887 * \note This can not be defined within the astman_append() function
888 * because it declares a couple of functions that get used to
889 * initialize the thread local storage key.
891 AST_THREADSTORAGE(astman_append_buf);
892 /*! \brief initial allocated size for the astman_append_buf */
893 #define ASTMAN_APPEND_BUF_INITSIZE 256
896 * utility functions for creating AMI replies
898 void astman_append(struct mansession *s, const char *fmt, ...)
903 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
907 ast_str_set_va(&buf, 0, fmt, ap);
911 send_string(s, buf->str);
913 ast_verbose("fd == -1 in astman_append, should not happen\n");
916 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
917 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
918 hold the session lock _or_ be running in an action callback (in which case s->busy will
919 be non-zero). In either of these cases, there is no need to lock-protect the session's
920 fd, since no other output will be sent (events will be queued), and no input will
921 be read until either the current action finishes or get_input() obtains the session
925 /*! \brief send a response with an optional message,
926 * and terminate it with an empty line.
927 * m is used only to grab the 'ActionID' field.
929 * Use the explicit constant MSG_MOREDATA to remove the empty line.
930 * XXX MSG_MOREDATA should go to a header file.
932 #define MSG_MOREDATA ((char *)astman_send_response)
933 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
935 const char *id = astman_get_header(m, "ActionID");
937 astman_append(s, "Response: %s\r\n", resp);
938 if (!ast_strlen_zero(id))
939 astman_append(s, "ActionID: %s\r\n", id);
941 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
942 if (msg == MSG_MOREDATA)
945 astman_append(s, "Message: %s\r\n\r\n", msg);
947 astman_append(s, "\r\n");
950 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
952 astman_send_response_full(s, m, resp, msg, NULL);
955 void astman_send_error(struct mansession *s, const struct message *m, char *error)
957 astman_send_response_full(s, m, "Error", error, NULL);
960 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
962 astman_send_response_full(s, m, "Success", msg, NULL);
965 static void astman_start_ack(struct mansession *s, const struct message *m)
967 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
970 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
972 astman_send_response_full(s, m, "Success", msg, listflag);
977 Rather than braindead on,off this now can also accept a specific int mask value
978 or a ',' delim list of mask strings (the same as manager.conf) -anthm
980 static int set_eventmask(struct mansession *s, const char *eventmask)
982 int maskint = strings_to_mask(eventmask);
984 ast_mutex_lock(&s->__lock);
986 s->send_events = maskint;
987 ast_mutex_unlock(&s->__lock);
993 * Here we start with action_ handlers for AMI actions,
994 * and the internal functions used by them.
995 * Generally, the handlers are called action_foo()
998 /* helper function for action_login() */
999 static int authenticate(struct mansession *s, const struct message *m)
1001 const char *username = astman_get_header(m, "Username");
1002 const char *password = astman_get_header(m, "Secret");
1004 struct ast_manager_user *user = NULL;
1006 if (ast_strlen_zero(username)) /* missing username */
1009 /* locate user in locked state */
1010 AST_RWLIST_WRLOCK(&users);
1012 if (!(user = get_manager_by_name_locked(username))) {
1013 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1014 } else if (user->ha && !ast_apply_ha(user->ha, &(s->sin))) {
1015 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1016 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
1017 const char *key = astman_get_header(m, "Key");
1018 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) && user->secret) {
1021 char md5key[256] = "";
1022 struct MD5Context md5;
1023 unsigned char digest[16];
1026 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1027 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
1028 MD5Final(digest, &md5);
1029 for (x = 0; x < 16; x++)
1030 len += sprintf(md5key + len, "%2.2x", digest[x]);
1031 if (!strcmp(md5key, key))
1034 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
1035 S_OR(s->challenge, ""));
1037 } else if (password && user->secret && !strcmp(password, user->secret))
1041 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1042 AST_RWLIST_UNLOCK(&users);
1048 ast_copy_string(s->username, username, sizeof(s->username));
1049 s->readperm = user->readperm;
1050 s->writeperm = user->writeperm;
1051 s->writetimeout = user->writetimeout;
1052 s->sessionstart = time(NULL);
1053 set_eventmask(s, astman_get_header(m, "Events"));
1055 AST_RWLIST_UNLOCK(&users);
1059 /*! \brief Manager PING */
1060 static char mandescr_ping[] =
1061 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1062 " manager connection open.\n"
1063 "Variables: NONE\n";
1065 static int action_ping(struct mansession *s, const struct message *m)
1067 astman_append(s, "Response: Success\r\n"
1072 static char mandescr_getconfig[] =
1073 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1074 "file by category and contents or optionally by specified category only.\n"
1075 "Variables: (Names marked with * are required)\n"
1076 " *Filename: Configuration filename (e.g. foo.conf)\n"
1077 " Category: Category in configuration file\n";
1079 static int action_getconfig(struct mansession *s, const struct message *m)
1081 struct ast_config *cfg;
1082 const char *fn = astman_get_header(m, "Filename");
1083 const char *category = astman_get_header(m, "Category");
1086 char *cur_category = NULL;
1087 struct ast_variable *v;
1088 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1090 if (ast_strlen_zero(fn)) {
1091 astman_send_error(s, m, "Filename not specified");
1094 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1095 astman_send_error(s, m, "Config file not found");
1099 astman_start_ack(s, m);
1100 while ((cur_category = ast_category_browse(cfg, cur_category))) {
1101 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
1103 astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
1104 for (v = ast_variable_browse(cfg, cur_category); v; v = v->next)
1105 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1109 if (!ast_strlen_zero(category) && catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1110 astman_append(s, "No categories found");
1111 ast_config_destroy(cfg);
1112 astman_append(s, "\r\n");
1117 static char mandescr_listcategories[] =
1118 "Description: A 'ListCategories' action will dump the categories in\n"
1121 " Filename: Configuration filename (e.g. foo.conf)\n";
1123 static int action_listcategories(struct mansession *s, const struct message *m)
1125 struct ast_config *cfg;
1126 const char *fn = astman_get_header(m, "Filename");
1127 char *category = NULL;
1128 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1131 if (ast_strlen_zero(fn)) {
1132 astman_send_error(s, m, "Filename not specified");
1135 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1136 astman_send_error(s, m, "Config file not found or file has invalid syntax");
1139 astman_start_ack(s, m);
1140 while ((category = ast_category_browse(cfg, category))) {
1141 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1144 if (catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1145 astman_append(s, "Error: no categories found");
1146 ast_config_destroy(cfg);
1147 astman_append(s, "\r\n");
1155 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1156 static void json_escape(char *out, const char *in)
1159 if (*in == '\\' || *in == '\"')
1166 static char mandescr_getconfigjson[] =
1167 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1168 "file by category and contents in JSON format. This only makes sense to be used\n"
1169 "using rawman over the HTTP interface.\n"
1171 " Filename: Configuration filename (e.g. foo.conf)\n";
1173 static int action_getconfigjson(struct mansession *s, const struct message *m)
1175 struct ast_config *cfg;
1176 const char *fn = astman_get_header(m, "Filename");
1177 char *category = NULL;
1178 struct ast_variable *v;
1181 unsigned int buf_len = 0;
1182 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1184 if (ast_strlen_zero(fn)) {
1185 astman_send_error(s, m, "Filename not specified");
1189 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1190 astman_send_error(s, m, "Config file not found");
1195 buf = alloca(buf_len);
1197 astman_start_ack(s, m);
1198 astman_append(s, "JSON: {");
1199 while ((category = ast_category_browse(cfg, category))) {
1201 if (buf_len < 2 * strlen(category) + 1) {
1203 buf = alloca(buf_len);
1205 json_escape(buf, category);
1206 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1209 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1211 astman_append(s, ",");
1212 if (buf_len < 2 * strlen(v->name) + 1) {
1214 buf = alloca(buf_len);
1216 json_escape(buf, v->name);
1217 astman_append(s, "\"%s", buf);
1218 if (buf_len < 2 * strlen(v->value) + 1) {
1220 buf = alloca(buf_len);
1222 json_escape(buf, v->value);
1223 astman_append(s, "%s\"", buf);
1227 astman_append(s, "]");
1229 astman_append(s, "}\r\n\r\n");
1231 ast_config_destroy(cfg);
1236 /* helper function for action_updateconfig */
1237 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1241 const char *action, *cat, *var, *value, *match, *line;
1242 struct ast_category *category;
1243 struct ast_variable *v;
1245 for (x = 0; x < 100000; x++) {
1246 unsigned int object = 0;
1248 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1249 action = astman_get_header(m, hdr);
1250 if (ast_strlen_zero(action))
1252 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1253 cat = astman_get_header(m, hdr);
1254 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1255 var = astman_get_header(m, hdr);
1256 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1257 value = astman_get_header(m, hdr);
1258 if (!ast_strlen_zero(value) && *value == '>') {
1262 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1263 match = astman_get_header(m, hdr);
1264 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
1265 line = astman_get_header(m, hdr);
1266 if (!strcasecmp(action, "newcat")) {
1267 if (ast_strlen_zero(cat))
1268 return UNSPECIFIED_CATEGORY;
1269 if (!(category = ast_category_new(cat, dfn, -1)))
1270 return FAILURE_ALLOCATION;
1271 if (ast_strlen_zero(match)) {
1272 ast_category_append(cfg, category);
1274 ast_category_insert(cfg, category, match);
1275 } else if (!strcasecmp(action, "renamecat")) {
1276 if (ast_strlen_zero(cat) || ast_strlen_zero(value))
1277 return UNSPECIFIED_ARGUMENT;
1278 if (!(category = ast_category_get(cfg, cat)))
1279 return UNKNOWN_CATEGORY;
1280 ast_category_rename(category, value);
1281 } else if (!strcasecmp(action, "delcat")) {
1282 if (ast_strlen_zero(cat))
1283 return UNSPECIFIED_CATEGORY;
1284 if (ast_category_delete(cfg, cat))
1285 return FAILURE_DELCAT;
1286 } else if (!strcasecmp(action, "emptycat")) {
1287 if (ast_strlen_zero(cat))
1288 return UNSPECIFIED_CATEGORY;
1289 if (ast_category_empty(cfg, cat))
1290 return FAILURE_EMPTYCAT;
1291 } else if (!strcasecmp(action, "update")) {
1292 if (ast_strlen_zero(cat) || ast_strlen_zero(var))
1293 return UNSPECIFIED_ARGUMENT;
1294 if (!(category = ast_category_get(cfg,cat)))
1295 return UNKNOWN_CATEGORY;
1296 if (ast_variable_update(category, var, value, match, object))
1297 return FAILURE_UPDATE;
1298 } else if (!strcasecmp(action, "delete")) {
1299 if (ast_strlen_zero(cat) || (ast_strlen_zero(var) && ast_strlen_zero(line)))
1300 return UNSPECIFIED_ARGUMENT;
1301 if (!(category = ast_category_get(cfg, cat)))
1302 return UNKNOWN_CATEGORY;
1303 if (ast_variable_delete(category, var, match, line))
1304 return FAILURE_DELETE;
1305 } else if (!strcasecmp(action, "append")) {
1306 if (ast_strlen_zero(cat) || ast_strlen_zero(var))
1307 return UNSPECIFIED_ARGUMENT;
1308 if (!(category = ast_category_get(cfg, cat)))
1309 return UNKNOWN_CATEGORY;
1310 if (!(v = ast_variable_new(var, value, dfn)))
1311 return FAILURE_ALLOCATION;
1312 if (object || (match && !strcasecmp(match, "object")))
1314 ast_variable_append(category, v);
1315 } else if (!strcasecmp(action, "insert")) {
1316 if (ast_strlen_zero(cat) || ast_strlen_zero(var) || ast_strlen_zero(line))
1317 return UNSPECIFIED_ARGUMENT;
1318 if (!(category = ast_category_get(cfg, cat)))
1319 return UNKNOWN_CATEGORY;
1320 if (!(v = ast_variable_new(var, value, dfn)))
1321 return FAILURE_ALLOCATION;
1322 ast_variable_insert(category, v, line);
1325 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
1326 return UNKNOWN_ACTION;
1332 static char mandescr_updateconfig[] =
1333 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1334 "configuration elements in Asterisk configuration files.\n"
1335 "Variables (X's represent 6 digit number beginning with 000000):\n"
1336 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1337 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1338 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1339 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,EmptyCat,Update,Delete,Append,Insert)\n"
1340 " Cat-XXXXXX: Category to operate on\n"
1341 " Var-XXXXXX: Variable to work on\n"
1342 " Value-XXXXXX: Value to work on\n"
1343 " Match-XXXXXX: Extra match required to match line\n"
1344 " Line-XXXXXX: Line in category to operate on (used with delete and insert actions)\n";
1346 static int action_updateconfig(struct mansession *s, const struct message *m)
1348 struct ast_config *cfg;
1349 const char *sfn = astman_get_header(m, "SrcFilename");
1350 const char *dfn = astman_get_header(m, "DstFilename");
1352 const char *rld = astman_get_header(m, "Reload");
1353 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1354 enum error_type result;
1356 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1357 astman_send_error(s, m, "Filename not specified");
1360 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
1361 astman_send_error(s, m, "Config file not found");
1364 result = handle_updates(s, m, cfg, dfn);
1366 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1367 res = config_text_file_save(dfn, cfg, "Manager");
1368 ast_config_destroy(cfg);
1370 astman_send_error(s, m, "Save of config failed");
1373 astman_send_ack(s, m, NULL);
1374 if (!ast_strlen_zero(rld)) {
1377 ast_module_reload(rld);
1380 ast_config_destroy(cfg);
1382 case UNKNOWN_ACTION:
1383 astman_send_error(s, m, "Unknown action command");
1385 case UNKNOWN_CATEGORY:
1386 astman_send_error(s, m, "Given category does not exist");
1388 case UNSPECIFIED_CATEGORY:
1389 astman_send_error(s, m, "Category not specified");
1391 case UNSPECIFIED_ARGUMENT:
1392 astman_send_error(s, m, "Problem with category, value, or line (if required)");
1394 case FAILURE_ALLOCATION:
1395 astman_send_error(s, m, "Memory allocation failure, this should not happen");
1397 case FAILURE_DELCAT:
1398 astman_send_error(s, m, "Delete category did not complete successfully");
1400 case FAILURE_EMPTYCAT:
1401 astman_send_error(s, m, "Empty category did not complete successfully");
1403 case FAILURE_UPDATE:
1404 astman_send_error(s, m, "Update did not complete successfully");
1406 case FAILURE_DELETE:
1407 astman_send_error(s, m, "Delete did not complete successfully");
1409 case FAILURE_APPEND:
1410 astman_send_error(s, m, "Append did not complete successfully");
1417 static char mandescr_createconfig[] =
1418 "Description: A 'CreateConfig' action will create an empty file in the\n"
1419 "configuration directory. This action is intended to be used before an\n"
1420 "UpdateConfig action.\n"
1422 " Filename: The configuration filename to create (e.g. foo.conf)\n";
1424 static int action_createconfig(struct mansession *s, const struct message *m)
1427 const char *fn = astman_get_header(m, "Filename");
1428 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
1429 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
1430 ast_str_append(&filepath, 0, "%s", fn);
1432 if ((fd = open(filepath->str, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
1434 astman_send_ack(s, m, "New configuration file created successfully");
1436 astman_send_error(s, m, strerror(errno));
1441 /*! \brief Manager WAITEVENT */
1442 static char mandescr_waitevent[] =
1443 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1444 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1445 "session, events will be generated and queued.\n"
1447 " Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1449 static int action_waitevent(struct mansession *s, const struct message *m)
1451 const char *timeouts = astman_get_header(m, "Timeout");
1455 const char *id = astman_get_header(m, "ActionID");
1458 if (!ast_strlen_zero(id))
1459 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1463 if (!ast_strlen_zero(timeouts)) {
1464 sscanf(timeouts, "%i", &timeout);
1467 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1470 ast_mutex_lock(&s->__lock);
1471 if (s->waiting_thread != AST_PTHREADT_NULL)
1472 pthread_kill(s->waiting_thread, SIGURG);
1474 if (s->managerid) { /* AMI-over-HTTP session */
1476 * Make sure the timeout is within the expire time of the session,
1477 * as the client will likely abort the request if it does not see
1478 * data coming after some amount of time.
1480 time_t now = time(NULL);
1481 int max = s->sessiontimeout - now - 10;
1483 if (max < 0) /* We are already late. Strange but possible. */
1485 if (timeout < 0 || timeout > max)
1487 if (!s->send_events) /* make sure we record events */
1488 s->send_events = -1;
1490 ast_mutex_unlock(&s->__lock);
1492 /* XXX should this go inside the lock ? */
1493 s->waiting_thread = pthread_self(); /* let new events wake up this thread */
1494 ast_debug(1, "Starting waiting for an event!\n");
1496 for (x = 0; x < timeout || timeout < 0; x++) {
1497 ast_mutex_lock(&s->__lock);
1500 /* We can have multiple HTTP session point to the same mansession entry.
1501 * The way we deal with it is not very nice: newcomers kick out the previous
1502 * HTTP session. XXX this needs to be improved.
1504 if (s->waiting_thread != pthread_self())
1508 ast_mutex_unlock(&s->__lock);
1511 if (s->managerid == 0) { /* AMI session */
1512 if (ast_wait_for_input(s->fd, 1000))
1514 } else { /* HTTP session */
1518 ast_debug(1, "Finished waiting for an event!\n");
1519 ast_mutex_lock(&s->__lock);
1520 if (s->waiting_thread == pthread_self()) {
1521 struct eventqent *eqe;
1522 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1523 while ( (eqe = NEW_EVENT(s)) ) {
1525 if (((s->readperm & eqe->category) == eqe->category) &&
1526 ((s->send_events & eqe->category) == eqe->category)) {
1527 astman_append(s, "%s", eqe->eventdata);
1529 s->last_ev = unref_event(s->last_ev);
1532 "Event: WaitEventComplete\r\n"
1535 s->waiting_thread = AST_PTHREADT_NULL;
1537 ast_debug(1, "Abandoning event request!\n");
1539 ast_mutex_unlock(&s->__lock);
1543 static char mandescr_listcommands[] =
1544 "Description: Returns the action name and synopsis for every\n"
1545 " action that is available to the user\n"
1546 "Variables: NONE\n";
1548 /*! \note The actionlock is read-locked by the caller of this function */
1549 static int action_listcommands(struct mansession *s, const struct message *m)
1551 struct manager_action *cur;
1552 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1554 astman_start_ack(s, m);
1555 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1556 if (s->writeperm & cur->authority || cur->authority == 0)
1557 astman_append(s, "%s: %s (Priv: %s)\r\n",
1558 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1560 astman_append(s, "\r\n");
1565 static char mandescr_events[] =
1566 "Description: Enable/Disable sending of events to this manager\n"
1569 " EventMask: 'on' if all events should be sent,\n"
1570 " 'off' if no events should be sent,\n"
1571 " 'system,call,log' to select which flags events should have to be sent.\n";
1573 static int action_events(struct mansession *s, const struct message *m)
1575 const char *mask = astman_get_header(m, "EventMask");
1578 res = set_eventmask(s, mask);
1580 astman_append(s, "Response: Success\r\n"
1583 astman_append(s, "Response: Success\r\n"
1588 static char mandescr_logoff[] =
1589 "Description: Logoff this manager session\n"
1590 "Variables: NONE\n";
1592 static int action_logoff(struct mansession *s, const struct message *m)
1594 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1598 static int action_login(struct mansession *s, const struct message *m)
1600 if (authenticate(s, m)) {
1602 astman_send_error(s, m, "Authentication failed");
1605 s->authenticated = 1;
1606 if (manager_displayconnects(s))
1607 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1608 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1609 astman_send_ack(s, m, "Authentication accepted");
1613 static int action_challenge(struct mansession *s, const struct message *m)
1615 const char *authtype = astman_get_header(m, "AuthType");
1617 if (!strcasecmp(authtype, "MD5")) {
1618 if (ast_strlen_zero(s->challenge))
1619 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1620 ast_mutex_lock(&s->__lock);
1621 astman_start_ack(s, m);
1622 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1623 ast_mutex_unlock(&s->__lock);
1625 astman_send_error(s, m, "Must specify AuthType");
1630 static char mandescr_hangup[] =
1631 "Description: Hangup a channel\n"
1633 " Channel: The channel name to be hungup\n";
1635 static int action_hangup(struct mansession *s, const struct message *m)
1637 struct ast_channel *c = NULL;
1638 const char *name = astman_get_header(m, "Channel");
1639 if (ast_strlen_zero(name)) {
1640 astman_send_error(s, m, "No channel specified");
1643 c = ast_get_channel_by_name_locked(name);
1645 astman_send_error(s, m, "No such channel");
1648 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1649 ast_channel_unlock(c);
1650 astman_send_ack(s, m, "Channel Hungup");
1654 static char mandescr_setvar[] =
1655 "Description: Set a global or local channel variable.\n"
1656 "Variables: (Names marked with * are required)\n"
1657 " Channel: Channel to set variable for\n"
1658 " *Variable: Variable name\n"
1661 static int action_setvar(struct mansession *s, const struct message *m)
1663 struct ast_channel *c = NULL;
1664 const char *name = astman_get_header(m, "Channel");
1665 const char *varname = astman_get_header(m, "Variable");
1666 const char *varval = astman_get_header(m, "Value");
1668 if (ast_strlen_zero(varname)) {
1669 astman_send_error(s, m, "No variable specified");
1673 if (!ast_strlen_zero(name)) {
1674 c = ast_get_channel_by_name_locked(name);
1676 astman_send_error(s, m, "No such channel");
1681 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1684 ast_channel_unlock(c);
1686 astman_send_ack(s, m, "Variable Set");
1691 static char mandescr_getvar[] =
1692 "Description: Get the value of a global or local channel variable.\n"
1693 "Variables: (Names marked with * are required)\n"
1694 " Channel: Channel to read variable from\n"
1695 " *Variable: Variable name\n"
1696 " ActionID: Optional Action id for message matching.\n";
1698 static int action_getvar(struct mansession *s, const struct message *m)
1700 struct ast_channel *c = NULL;
1701 const char *name = astman_get_header(m, "Channel");
1702 const char *varname = astman_get_header(m, "Variable");
1704 char workspace[1024] = "";
1706 if (ast_strlen_zero(varname)) {
1707 astman_send_error(s, m, "No variable specified");
1711 if (!ast_strlen_zero(name)) {
1712 c = ast_get_channel_by_name_locked(name);
1714 astman_send_error(s, m, "No such channel");
1719 if (varname[strlen(varname) - 1] == ')') {
1720 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1723 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1727 ast_channel_unlock(c);
1728 astman_start_ack(s, m);
1729 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1734 static char mandescr_status[] =
1735 "Description: Lists channel status along with requested channel vars.\n"
1736 "Variables: (Names marked with * are required)\n"
1737 " *Channel: Name of the channel to query for status\n"
1738 " Variables: Comma ',' separated list of variables to include\n"
1739 " ActionID: Optional ID for this transaction\n"
1740 "Will return the status information of each channel along with the\n"
1741 "value for the specified channel variables.\n";
1744 /*! \brief Manager "status" command to show channels */
1745 /* Needs documentation... */
1746 static int action_status(struct mansession *s, const struct message *m)
1748 const char *name = astman_get_header(m, "Channel");
1749 const char *cvariables = astman_get_header(m, "Variables");
1750 char *variables = ast_strdupa(S_OR(cvariables, ""));
1751 struct ast_channel *c;
1753 struct timeval now = ast_tvnow();
1754 long elapsed_seconds = 0;
1756 int all = ast_strlen_zero(name); /* set if we want all channels */
1757 const char *id = astman_get_header(m, "ActionID");
1759 AST_DECLARE_APP_ARGS(vars,
1760 AST_APP_ARG(name)[100];
1762 struct ast_str *str = ast_str_create(1000);
1764 if (!ast_strlen_zero(id))
1765 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1770 c = ast_channel_walk_locked(NULL);
1772 c = ast_get_channel_by_name_locked(name);
1774 astman_send_error(s, m, "No such channel");
1778 astman_send_ack(s, m, "Channel status will follow");
1780 if (!ast_strlen_zero(cvariables)) {
1781 AST_STANDARD_APP_ARGS(vars, variables);
1784 /* if we look by name, we break after the first iteration */
1786 if (!ast_strlen_zero(cvariables)) {
1789 for (i = 0; i < vars.argc; i++) {
1790 char valbuf[512], *ret = NULL;
1792 if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
1793 if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
1798 pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
1801 ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
1807 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1812 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1816 "Privilege: Call\r\n"
1818 "CallerIDNum: %s\r\n"
1819 "CallerIDName: %s\r\n"
1820 "Accountcode: %s\r\n"
1821 "ChannelState: %d\r\n"
1822 "ChannelStateDesc: %s\r\n"
1833 S_OR(c->cid.cid_num, ""),
1834 S_OR(c->cid.cid_name, ""),
1837 ast_state2str(c->_state), c->context,
1838 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, str->str, idText);
1842 "Privilege: Call\r\n"
1844 "CallerIDNum: %s\r\n"
1845 "CallerIDName: %s\r\n"
1854 S_OR(c->cid.cid_num, "<unknown>"),
1855 S_OR(c->cid.cid_name, "<unknown>"),
1857 ast_state2str(c->_state), bridge, c->uniqueid, str->str, idText);
1859 ast_channel_unlock(c);
1862 c = ast_channel_walk_locked(c);
1865 "Event: StatusComplete\r\n"
1868 "\r\n", idText, channels);
1873 static char mandescr_sendtext[] =
1874 "Description: Sends A Text Message while in a call.\n"
1875 "Variables: (Names marked with * are required)\n"
1876 " *Channel: Channel to send message to\n"
1877 " *Message: Message to send\n"
1878 " ActionID: Optional Action id for message matching.\n";
1880 static int action_sendtext(struct mansession *s, const struct message *m)
1882 struct ast_channel *c = NULL;
1883 const char *name = astman_get_header(m, "Channel");
1884 const char *textmsg = astman_get_header(m, "Message");
1887 if (ast_strlen_zero(name)) {
1888 astman_send_error(s, m, "No channel specified");
1892 if (ast_strlen_zero(textmsg)) {
1893 astman_send_error(s, m, "No Message specified");
1897 c = ast_get_channel_by_name_locked(name);
1899 astman_send_error(s, m, "No such channel");
1903 res = ast_sendtext(c, textmsg);
1904 ast_channel_unlock(c);
1907 astman_send_ack(s, m, "Success");
1909 astman_send_error(s, m, "Failure");
1914 static char mandescr_redirect[] =
1915 "Description: Redirect (transfer) a call.\n"
1916 "Variables: (Names marked with * are required)\n"
1917 " *Channel: Channel to redirect\n"
1918 " ExtraChannel: Second call leg to transfer (optional)\n"
1919 " *Exten: Extension to transfer to\n"
1920 " *Context: Context to transfer to\n"
1921 " *Priority: Priority to transfer to\n"
1922 " ActionID: Optional Action id for message matching.\n";
1924 /*! \brief action_redirect: The redirect manager command */
1925 static int action_redirect(struct mansession *s, const struct message *m)
1927 const char *name = astman_get_header(m, "Channel");
1928 const char *name2 = astman_get_header(m, "ExtraChannel");
1929 const char *exten = astman_get_header(m, "Exten");
1930 const char *context = astman_get_header(m, "Context");
1931 const char *priority = astman_get_header(m, "Priority");
1932 struct ast_channel *chan, *chan2 = NULL;
1936 if (ast_strlen_zero(name)) {
1937 astman_send_error(s, m, "Channel not specified");
1940 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1941 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1942 astman_send_error(s, m, "Invalid priority\n");
1946 /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1947 chan = ast_get_channel_by_name_locked(name);
1950 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1951 astman_send_error(s, m, buf);
1954 if (ast_check_hangup(chan)) {
1955 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1956 ast_channel_unlock(chan);
1959 if (!ast_strlen_zero(name2))
1960 chan2 = ast_get_channel_by_name_locked(name2);
1961 if (chan2 && ast_check_hangup(chan2)) {
1962 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1963 ast_channel_unlock(chan);
1964 ast_channel_unlock(chan2);
1967 res = ast_async_goto(chan, context, exten, pi);
1969 if (!ast_strlen_zero(name2)) {
1971 res = ast_async_goto(chan2, context, exten, pi);
1975 astman_send_ack(s, m, "Dual Redirect successful");
1977 astman_send_error(s, m, "Secondary redirect failed");
1979 astman_send_ack(s, m, "Redirect successful");
1981 astman_send_error(s, m, "Redirect failed");
1983 ast_channel_unlock(chan);
1985 ast_channel_unlock(chan2);
1989 static char mandescr_atxfer[] =
1990 "Description: Attended transfer.\n"
1991 "Variables: (Names marked with * are required)\n"
1992 " *Channel: Transferer's channel\n"
1993 " *Exten: Extension to transfer to\n"
1994 " *Context: Context to transfer to\n"
1995 " *Priority: Priority to transfer to\n"
1996 " ActionID: Optional Action id for message matching.\n";
1998 static int action_atxfer(struct mansession *s, const struct message *m)
2000 const char *name = astman_get_header(m, "Channel");
2001 const char *exten = astman_get_header(m, "Exten");
2002 const char *context = astman_get_header(m, "Context");
2003 const char *priority = astman_get_header(m, "Priority");
2004 struct ast_channel *chan = NULL;
2005 struct ast_call_feature *atxfer_feature = NULL;
2006 char *feature_code = NULL;
2007 int priority_int = 0;
2009 if (ast_strlen_zero(name)) {
2010 astman_send_error(s, m, "No channel specified\n");
2013 if (ast_strlen_zero(exten)) {
2014 astman_send_error(s, m, "No extension specified\n");
2017 if (ast_strlen_zero(context)) {
2018 astman_send_error(s, m, "No context specified\n");
2021 if (ast_strlen_zero(priority)) {
2022 astman_send_error(s, m, "No priority specified\n");
2026 if (sscanf(priority, "%d", &priority_int) != 1 && (priority_int = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2027 astman_send_error(s, m, "Invalid Priority\n");
2031 if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
2032 astman_send_error(s, m, "No attended transfer feature found\n");
2036 if (!(chan = ast_get_channel_by_name_locked(name))) {
2037 astman_send_error(s, m, "Channel specified does not exist\n");
2041 for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
2042 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2043 ast_queue_frame(chan, &f);
2046 for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
2047 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2048 ast_queue_frame(chan, &f);
2051 astman_send_ack(s, m, "Atxfer successfully queued\n");
2052 ast_channel_unlock(chan);
2057 static char mandescr_command[] =
2058 "Description: Run a CLI command.\n"
2059 "Variables: (Names marked with * are required)\n"
2060 " *Command: Asterisk CLI command to run\n"
2061 " ActionID: Optional Action id for message matching.\n";
2063 /*! \brief Manager command "command" - execute CLI command */
2064 static int action_command(struct mansession *s, const struct message *m)
2066 const char *cmd = astman_get_header(m, "Command");
2067 const char *id = astman_get_header(m, "ActionID");
2068 char *buf, *final_buf;
2069 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
2070 int fd = mkstemp(template), i = 0;
2073 for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
2074 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
2075 astman_send_error(s, m, "Command blacklisted");
2080 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
2081 if (!ast_strlen_zero(id))
2082 astman_append(s, "ActionID: %s\r\n", id);
2083 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
2084 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
2085 l = lseek(fd, 0, SEEK_END); /* how many chars available */
2087 /* This has a potential to overflow the stack. Hence, use the heap. */
2088 buf = ast_calloc(1, l + 1);
2089 final_buf = ast_calloc(1, l + 1);
2091 lseek(fd, 0, SEEK_SET);
2095 term_strip(final_buf, buf, l);
2096 final_buf[l] = '\0';
2098 astman_append(s, "%s", S_OR(final_buf, buf));
2103 astman_append(s, "--END COMMAND--\r\n\r\n");
2105 ast_free(final_buf);
2109 /* helper function for originate */
2110 struct fast_originate_helper {
2111 char tech[AST_MAX_EXTENSION];
2112 char data[AST_MAX_EXTENSION];
2114 char app[AST_MAX_APP];
2115 char appdata[AST_MAX_EXTENSION];
2116 char cid_name[AST_MAX_EXTENSION];
2117 char cid_num[AST_MAX_EXTENSION];
2118 char context[AST_MAX_CONTEXT];
2119 char exten[AST_MAX_EXTENSION];
2120 char idtext[AST_MAX_EXTENSION];
2121 char account[AST_MAX_ACCOUNT_CODE];
2123 struct ast_variable *vars;
2126 static void *fast_originate(void *data)
2128 struct fast_originate_helper *in = data;
2131 struct ast_channel *chan = NULL;
2132 char requested_channel[AST_CHANNEL_NAME];
2134 if (!ast_strlen_zero(in->app)) {
2135 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
2136 S_OR(in->cid_num, NULL),
2137 S_OR(in->cid_name, NULL),
2138 in->vars, in->account, &chan);
2140 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
2141 S_OR(in->cid_num, NULL),
2142 S_OR(in->cid_name, NULL),
2143 in->vars, in->account, &chan);
2147 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
2148 /* Tell the manager what happened with the channel */
2149 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
2157 "CallerIDNum: %s\r\n"
2158 "CallerIDName: %s\r\n",
2159 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
2160 chan ? chan->uniqueid : "<null>",
2161 S_OR(in->cid_num, "<unknown>"),
2162 S_OR(in->cid_name, "<unknown>")
2165 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2167 ast_channel_unlock(chan);
2172 static char mandescr_originate[] =
2173 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
2174 " Application/Data\n"
2175 "Variables: (Names marked with * are required)\n"
2176 " *Channel: Channel name to call\n"
2177 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
2178 " Context: Context to use (requires 'Exten' and 'Priority')\n"
2179 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
2180 " Application: Application to use\n"
2181 " Data: Data to use (requires 'Application')\n"
2182 " Timeout: How long to wait for call to be answered (in ms)\n"
2183 " CallerID: Caller ID to be set on the outgoing channel\n"
2184 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
2185 " Account: Account code\n"
2186 " Async: Set to 'true' for fast origination\n";
2188 static int action_originate(struct mansession *s, const struct message *m)
2190 const char *name = astman_get_header(m, "Channel");
2191 const char *exten = astman_get_header(m, "Exten");
2192 const char *context = astman_get_header(m, "Context");
2193 const char *priority = astman_get_header(m, "Priority");
2194 const char *timeout = astman_get_header(m, "Timeout");
2195 const char *callerid = astman_get_header(m, "CallerID");
2196 const char *account = astman_get_header(m, "Account");
2197 const char *app = astman_get_header(m, "Application");
2198 const char *appdata = astman_get_header(m, "Data");
2199 const char *async = astman_get_header(m, "Async");
2200 const char *id = astman_get_header(m, "ActionID");
2201 struct ast_variable *vars = astman_get_variables(m);
2203 char *l = NULL, *n = NULL;
2213 astman_send_error(s, m, "Channel not specified");
2216 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2217 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2218 astman_send_error(s, m, "Invalid priority\n");
2222 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2223 astman_send_error(s, m, "Invalid timeout\n");
2226 ast_copy_string(tmp, name, sizeof(tmp));
2228 data = strchr(tmp, '/');
2230 astman_send_error(s, m, "Invalid channel\n");
2234 ast_copy_string(tmp2, callerid, sizeof(tmp2));
2235 ast_callerid_parse(tmp2, &n, &l);
2237 if (ast_strlen_zero(n))
2241 ast_shrink_phone_number(l);
2242 if (ast_strlen_zero(l))
2245 if (ast_true(async)) {
2246 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2250 if (!ast_strlen_zero(id))
2251 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2252 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2253 ast_copy_string(fast->data, data, sizeof(fast->data));
2254 ast_copy_string(fast->app, app, sizeof(fast->app));
2255 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2257 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2259 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2261 ast_copy_string(fast->context, context, sizeof(fast->context));
2262 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2263 ast_copy_string(fast->account, account, sizeof(fast->account));
2265 fast->priority = pi;
2266 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2272 } else if (!ast_strlen_zero(app)) {
2273 /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
2274 if (!(s->writeperm & EVENT_FLAG_SYSTEM)
2276 strcasestr(app, "system") == 0 || /* System(rm -rf /)
2277 TrySystem(rm -rf /) */
2278 strcasestr(app, "exec") || /* Exec(System(rm -rf /))
2279 TryExec(System(rm -rf /)) */
2280 strcasestr(app, "agi") || /* AGI(/bin/rm,-rf /)
2281 EAGI(/bin/rm,-rf /) */
2282 strstr(appdata, "SHELL") || /* NoOp(${SHELL(rm -rf /)}) */
2283 strstr(appdata, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
2285 astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
2288 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2290 if (exten && context && pi)
2291 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2293 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2298 astman_send_ack(s, m, "Originate successfully queued");
2300 astman_send_error(s, m, "Originate failed");
2304 /*! \brief Help text for manager command mailboxstatus
2306 static char mandescr_mailboxstatus[] =
2307 "Description: Checks a voicemail account for status.\n"
2308 "Variables: (Names marked with * are required)\n"
2309 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2310 " ActionID: Optional ActionID for message matching.\n"
2311 "Returns number of messages.\n"
2312 " Message: Mailbox Status\n"
2313 " Mailbox: <mailboxid>\n"
2314 " Waiting: <count>\n"
2317 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2319 const char *mailbox = astman_get_header(m, "Mailbox");
2322 if (ast_strlen_zero(mailbox)) {
2323 astman_send_error(s, m, "Mailbox not specified");
2326 ret = ast_app_has_voicemail(mailbox, NULL);
2327 astman_start_ack(s, m);
2328 astman_append(s, "Message: Mailbox Status\r\n"
2330 "Waiting: %d\r\n\r\n", mailbox, ret);
2334 static char mandescr_mailboxcount[] =
2335 "Description: Checks a voicemail account for new messages.\n"
2336 "Variables: (Names marked with * are required)\n"
2337 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2338 " ActionID: Optional ActionID for message matching.\n"
2339 "Returns number of urgent, new and old messages.\n"
2340 " Message: Mailbox Message Count\n"
2341 " Mailbox: <mailboxid>\n"
2342 " UrgentMessages: <count>\n"
2343 " NewMessages: <count>\n"
2344 " OldMessages: <count>\n"
2346 static int action_mailboxcount(struct mansession *s, const struct message *m)
2348 const char *mailbox = astman_get_header(m, "Mailbox");
2349 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
2351 if (ast_strlen_zero(mailbox)) {
2352 astman_send_error(s, m, "Mailbox not specified");
2355 ast_app_inboxcount(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
2356 astman_start_ack(s, m);
2357 astman_append(s, "Message: Mailbox Message Count\r\n"
2359 "UrgMessages: %d\r\n"
2360 "NewMessages: %d\r\n"
2361 "OldMessages: %d\r\n"
2363 mailbox, urgentmsgs, newmsgs, oldmsgs);
2367 static char mandescr_extensionstate[] =
2368 "Description: Report the extension state for given extension.\n"
2369 " If the extension has a hint, will use devicestate to check\n"
2370 " the status of the device connected to the extension.\n"
2371 "Variables: (Names marked with * are required)\n"
2372 " *Exten: Extension to check state on\n"
2373 " *Context: Context for extension\n"
2374 " ActionId: Optional ID for this transaction\n"
2375 "Will return an \"Extension Status\" message.\n"
2376 "The response will include the hint for the extension and the status.\n";
2378 static int action_extensionstate(struct mansession *s, const struct message *m)
2380 const char *exten = astman_get_header(m, "Exten");
2381 const char *context = astman_get_header(m, "Context");
2382 char hint[256] = "";
2384 if (ast_strlen_zero(exten)) {
2385 astman_send_error(s, m, "Extension not specified");
2388 if (ast_strlen_zero(context))
2389 context = "default";
2390 status = ast_extension_state(NULL, context, exten);
2391 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2392 astman_start_ack(s, m);
2393 astman_append(s, "Message: Extension Status\r\n"
2397 "Status: %d\r\n\r\n",
2398 exten, context, hint, status);
2402 static char mandescr_timeout[] =
2403 "Description: Hangup a channel after a certain time.\n"
2404 "Variables: (Names marked with * are required)\n"
2405 " *Channel: Channel name to hangup\n"
2406 " *Timeout: Maximum duration of the call (sec)\n"
2407 "Acknowledges set time with 'Timeout Set' message\n";
2409 static int action_timeout(struct mansession *s, const struct message *m)
2411 struct ast_channel *c;
2412 const char *name = astman_get_header(m, "Channel");
2413 double timeout = atof(astman_get_header(m, "Timeout"));
2414 struct timeval tv = { timeout, 0 };
2416 if (ast_strlen_zero(name)) {
2417 astman_send_error(s, m, "No channel specified");
2420 if (!timeout || timeout < 0) {
2421 astman_send_error(s, m, "No timeout specified");
2424 c = ast_get_channel_by_name_locked(name);
2426 astman_send_error(s, m, "No such channel");
2430 tv.tv_usec = (timeout - tv.tv_sec) * 1000000.0;
2431 ast_channel_setwhentohangup_tv(c, tv);
2432 ast_channel_unlock(c);
2433 astman_send_ack(s, m, "Timeout Set");
2438 * Send any applicable events to the client listening on this socket.
2439 * Wait only for a finite time on each event, and drop all events whether
2440 * they are successfully sent or not.
2442 static int process_events(struct mansession *s)
2446 ast_mutex_lock(&s->__lock);
2448 struct eventqent *eqe;
2450 while ( (eqe = NEW_EVENT(s)) ) {
2452 if (!ret && s->authenticated &&
2453 (s->readperm & eqe->category) == eqe->category &&
2454 (s->send_events & eqe->category) == eqe->category) {
2455 if (send_string(s, eqe->eventdata) < 0)
2456 ret = -1; /* don't send more */
2458 s->last_ev = unref_event(s->last_ev);
2461 ast_mutex_unlock(&s->__lock);
2465 static char mandescr_userevent[] =
2466 "Description: Send an event to manager sessions.\n"
2467 "Variables: (Names marked with * are required)\n"
2468 " *UserEvent: EventStringToSend\n"
2469 " Header1: Content1\n"
2470 " HeaderN: ContentN\n";
2472 static int action_userevent(struct mansession *s, const struct message *m)
2474 const char *event = astman_get_header(m, "UserEvent");
2475 char body[2048] = "";
2477 for (x = 0; x < m->hdrcount; x++) {
2478 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2479 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2480 bodylen += strlen(m->headers[x]);
2481 ast_copy_string(body + bodylen, "\r\n", 3);
2486 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2490 static char mandescr_coresettings[] =
2491 "Description: Query for Core PBX settings.\n"
2492 "Variables: (Names marked with * are optional)\n"
2493 " *ActionID: ActionID of this transaction\n";
2495 /*! \brief Show PBX core settings information */
2496 static int action_coresettings(struct mansession *s, const struct message *m)
2498 const char *actionid = astman_get_header(m, "ActionID");
2501 if (!ast_strlen_zero(actionid))
2502 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2506 astman_append(s, "Response: Success\r\n"
2508 "AMIversion: %s\r\n"
2509 "AsteriskVersion: %s\r\n"
2510 "SystemName: %s\r\n"
2511 "CoreMaxCalls: %d\r\n"
2512 "CoreMaxLoadAvg: %f\r\n"
2513 "CoreRunUser: %s\r\n"
2514 "CoreRunGroup: %s\r\n"
2515 "CoreMaxFilehandles: %d\r\n"
2516 "CoreRealTimeEnabled: %s\r\n"
2517 "CoreCDRenabled: %s\r\n"
2518 "CoreHTTPenabled: %s\r\n"
2523 ast_config_AST_SYSTEM_NAME,
2526 ast_config_AST_RUN_USER,
2527 ast_config_AST_RUN_GROUP,
2529 ast_realtime_enabled() ? "Yes" : "No",
2530 check_cdr_enabled() ? "Yes" : "No",
2531 check_webmanager_enabled() ? "Yes" : "No"
2536 static char mandescr_corestatus[] =
2537 "Description: Query for Core PBX status.\n"
2538 "Variables: (Names marked with * are optional)\n"
2539 " *ActionID: ActionID of this transaction\n";
2541 /*! \brief Show PBX core status information */
2542 static int action_corestatus(struct mansession *s, const struct message *m)
2544 const char *actionid = astman_get_header(m, "ActionID");
2546 char startuptime[150];
2547 char reloadtime[150];
2550 if (!ast_strlen_zero(actionid))
2551 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2555 ast_localtime(&ast_startuptime, &tm, NULL);
2556 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2557 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2558 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2560 astman_append(s, "Response: Success\r\n"
2562 "CoreStartupTime: %s\r\n"
2563 "CoreReloadTime: %s\r\n"
2564 "CoreCurrentCalls: %d\r\n"
2569 ast_active_channels()
2574 static char mandescr_reload[] =
2575 "Description: Send a reload event.\n"
2576 "Variables: (Names marked with * are optional)\n"
2577 " *ActionID: ActionID of this transaction\n"
2578 " *Module: Name of the module to reload\n";
2580 /*! \brief Send a reload event */
2581 static int action_reload(struct mansession *s, const struct message *m)
2583 const char *module = astman_get_header(m, "Module");
2584 int res = ast_module_reload(S_OR(module, NULL));
2587 astman_send_ack(s, m, "Module Reloaded");
2589 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2593 static char mandescr_coreshowchannels[] =
2594 "Description: List currently defined channels and some information\n"
2597 " ActionID: Optional Action id for message matching.\n";
2599 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2600 * and some information about them. */
2601 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2603 const char *actionid = astman_get_header(m, "ActionID");
2604 char actionidtext[256];
2605 struct ast_channel *c = NULL;
2607 int duration, durh, durm, durs;
2609 if (!ast_strlen_zero(actionid))
2610 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2612 actionidtext[0] = '\0';
2614 astman_send_listack(s, m, "Channels will follow", "start");
2616 while ((c = ast_channel_walk_locked(c)) != NULL) {
2617 struct ast_channel *bc = ast_bridged_channel(c);
2618 char durbuf[10] = "";
2620 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2621 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2622 durh = duration / 3600;
2623 durm = (duration % 3600) / 60;
2624 durs = duration % 60;
2625 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2634 "ChannelState: %d\r\n"
2635 "ChannelStateDesc: %s\r\n"
2636 "Application: %s\r\n"
2637 "ApplicationData: %s\r\n"
2638 "CallerIDnum: %s\r\n"
2640 "AccountCode: %s\r\n"
2641 "BridgedChannel: %s\r\n"
2642 "BridgedUniqueID: %s\r\n"
2643 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2644 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2645 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2646 ast_channel_unlock(c);
2651 "Event: CoreShowChannelsComplete\r\n"
2652 "EventList: Complete\r\n"
2655 "\r\n", numchans, actionidtext);
2660 static char mandescr_modulecheck[] =
2661 "Description: Checks if Asterisk module is loaded\n"
2663 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2664 " Module: <name> Asterisk module name (not including extension)\n"
2666 "Will return Success/Failure\n"
2667 "For success returns, the module revision number is included.\n";
2669 /* Manager function to check if module is loaded */
2670 static int manager_modulecheck(struct mansession *s, const struct message *m)
2673 const char *module = astman_get_header(m, "Module");
2674 const char *id = astman_get_header(m, "ActionID");
2676 #if !defined(LOW_MEMORY)
2677 const char *version;
2679 char filename[PATH_MAX];
2682 ast_copy_string(filename, module, sizeof(filename));
2683 if ((cut = strchr(filename, '.'))) {
2686 cut = filename + strlen(filename);
2688 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
2689 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2690 res = ast_module_check(filename);
2692 astman_send_error(s, m, "Module not loaded");
2695 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
2696 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2697 #if !defined(LOW_MEMORY)
2698 version = ast_file_version_find(filename);
2701 if (!ast_strlen_zero(id))
2702 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2705 astman_append(s, "Response: Success\r\n%s", idText);
2706 #if !defined(LOW_MEMORY)
2707 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2712 static char mandescr_moduleload[] =
2713 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2715 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2716 " Module: <name> Asterisk module name (including .so extension)\n"
2717 " or subsystem identifier:\n"
2718 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2719 " LoadType: load | unload | reload\n"
2720 " The operation to be done on module\n"
2721 " If no module is specified for a reload loadtype, all modules are reloaded";
2723 static int manager_moduleload(struct mansession *s, const struct message *m)
2726 const char *module = astman_get_header(m, "Module");
2727 const char *loadtype = astman_get_header(m, "LoadType");
2729 if (!loadtype || strlen(loadtype) == 0)
2730 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2731 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2732 astman_send_error(s, m, "Need module name");
2734 if (!strcasecmp(loadtype, "load")) {
2735 res = ast_load_resource(module);
2737 astman_send_error(s, m, "Could not load module.");
2739 astman_send_ack(s, m, "Module loaded.");
2740 } else if (!strcasecmp(loadtype, "unload")) {
2741 res = ast_unload_resource(module, AST_FORCE_SOFT);
2743 astman_send_error(s, m, "Could not unload module.");
2745 astman_send_ack(s, m, "Module unloaded.");
2746 } else if (!strcasecmp(loadtype, "reload")) {
2747 if (module != NULL) {
2748 res = ast_module_reload(module);
2750 astman_send_error(s, m, "No such module.");
2752 astman_send_error(s, m, "Module does not support reload action.");
2754 astman_send_ack(s, m, "Module reloaded.");
2756 ast_module_reload(NULL); /* Reload all modules */
2757 astman_send_ack(s, m, "All modules reloaded");
2760 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2765 * Done with the action handlers here, we start with the code in charge
2766 * of accepting connections and serving them.
2767 * accept_thread() forks a new thread for each connection, session_do(),
2768 * which in turn calls get_input() repeatedly until a full message has
2769 * been accumulated, and then invokes process_message() to pass it to
2770 * the appropriate handler.
2774 * Process an AMI message, performing desired action.
2775 * Return 0 on success, -1 on error that require the session to be destroyed.
2777 static int process_message(struct mansession *s, const struct message *m)
2779 char action[80] = "";
2781 struct manager_action *tmp;
2782 const char *user = astman_get_header(m, "Username");
2784 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2785 ast_debug(1, "Manager received command '%s'\n", action);
2787 if (ast_strlen_zero(action)) {
2788 ast_mutex_lock(&s->__lock);
2789 astman_send_error(s, m, "Missing action in request");
2790 ast_mutex_unlock(&s->__lock);
2794 if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2795 ast_mutex_lock(&s->__lock);
2796 astman_send_error(s, m, "Permission denied");
2797 ast_mutex_unlock(&s->__lock);
2801 if (!allowmultiplelogin && !s->authenticated && user &&
2802 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2803 if (check_manager_session_inuse(user)) {
2805 ast_mutex_lock(&s->__lock);
2806 astman_send_error(s, m, "Login Already In Use");
2807 ast_mutex_unlock(&s->__lock);
2812 AST_RWLIST_RDLOCK(&actions);
2813 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2814 if (strcasecmp(action, tmp->action))
2816 if (s->writeperm & tmp->authority || tmp->authority == 0)
2817 ret = tmp->func(s, m);
2819 astman_send_error(s, m, "Permission denied");
2822 AST_RWLIST_UNLOCK(&actions);
2826 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2827 ast_mutex_lock(&s->__lock);
2828 astman_send_error(s, m, buf);
2829 ast_mutex_unlock(&s->__lock);
2833 /* Once done with our message, deliver any pending events */
2834 return process_events(s);
2838 * Read one full line (including crlf) from the manager socket.
2840 * \r\n is the only valid terminator for the line.
2841 * (Note that, later, '\0' will be considered as the end-of-line marker,
2842 * so everything between the '\0' and the '\r\n' will not be used).
2843 * Also note that we assume output to have at least "maxlen" space.
2846 static int get_input(struct mansession *s, char *output)
2849 int maxlen = sizeof(s->inbuf) - 1;
2850 char *src = s->inbuf;
2853 * Look for \r\n within the buffer. If found, copy to the output
2854 * buffer and return, trimming the \r\n (not used afterwards).
2856 for (x = 0; x < s->inlen; x++) {
2857 int cr; /* set if we have \r */
2858 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2859 cr = 2; /* Found. Update length to include \r\n */
2860 else if (src[x] == '\n')
2861 cr = 1; /* also accept \n only */
2864 memmove(output, src, x); /*... but trim \r\n */
2865 output[x] = '\0'; /* terminate the string */
2866 x += cr; /* number of bytes used */
2867 s->inlen -= x; /* remaining size */
2868 memmove(src, src + x, s->inlen); /* remove used bytes */
2871 if (s->inlen >= maxlen) {
2872 /* no crlf found, and buffer full - sorry, too long for us */
2873 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2878 /* XXX do we really need this locking ? */
2879 ast_mutex_lock(&s->__lock);
2880 if (s->pending_event) {
2881 s->pending_event = 0;
2882 ast_mutex_unlock(&s->__lock);
2885 s->waiting_thread = pthread_self();
2886 ast_mutex_unlock(&s->__lock);
2888 res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
2890 ast_mutex_lock(&s->__lock);
2891 s->waiting_thread = AST_PTHREADT_NULL;
2892 ast_mutex_unlock(&s->__lock);
2895 /* If we get a signal from some other thread (typically because
2896 * there are new events queued), return 0 to notify the caller.
2898 if (errno == EINTR || errno == EAGAIN)
2900 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2903 ast_mutex_lock(&s->__lock);
2904 res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2906 res = -1; /* error return */
2909 src[s->inlen] = '\0';
2912 ast_mutex_unlock(&s->__lock);
2916 static int do_message(struct mansession *s)
2918 struct message m = { 0 };
2919 char header_buf[sizeof(s->inbuf)] = { '\0' };
2923 /* Check if any events are pending and do them if needed */
2924 if (process_events(s))
2926 res = get_input(s, header_buf);
2929 } else if (res > 0) {
2930 if (ast_strlen_zero(header_buf))
2931 return process_message(s, &m) ? -1 : 0;
2932 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2933 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2940 /*! \brief The body of the individual manager session.
2941 * Call get_input() to read one line at a time
2942 * (or be woken up on new events), collect the lines in a
2943 * message until found an empty line, and execute the request.
2944 * In any case, deliver events asynchronously through process_events()
2945 * (called from here if no line is available, or at the end of
2946 * process_message(). )
2948 static void *session_do(void *data)
2950 struct ast_tcptls_session_instance *ser = data;
2951 struct mansession *s = ast_calloc(1, sizeof(*s));
2958 s->writetimeout = 100;
2959 s->waiting_thread = AST_PTHREADT_NULL;
2961 flags = fcntl(ser->fd, F_GETFL);
2962 if (!block_sockets) /* make sure socket is non-blocking */
2963 flags |= O_NONBLOCK;
2965 flags &= ~O_NONBLOCK;
2966 fcntl(ser->fd, F_SETFL, flags);
2968 ast_mutex_init(&s->__lock);
2969 s->send_events = -1;
2970 /* these fields duplicate those in the 'ser' structure */
2973 s->sin = ser->requestor;
2975 AST_LIST_LOCK(&sessions);
2976 AST_LIST_INSERT_HEAD(&sessions, s, list);
2977 ast_atomic_fetchadd_int(&num_sessions, 1);
2978 AST_LIST_UNLOCK(&sessions);
2979 /* Hook to the tail of the event queue */
2980 s->last_ev = grab_last();
2982 astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
2984 if ((res = do_message(s)) < 0)
2987 /* session is over, explain why and terminate */
2988 if (s->authenticated) {
2989 if (manager_displayconnects(s))
2990 ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2991 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2993 if (displayconnects)
2994 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2995 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2998 /* It is possible under certain circumstances for this session thread
2999 to complete its work and exit *before* the thread that created it
3000 has finished executing the ast_pthread_create_background() function.
3001 If this occurs, some versions of glibc appear to act in a buggy
3002 fashion and attempt to write data into memory that it thinks belongs
3003 to the thread but is in fact not owned by the thread (or may have
3004 been freed completely).
3006 Causing this thread to yield to other threads at least one time
3007 appears to work around this bug.
3014 ser = ast_tcptls_session_instance_destroy(ser);
3018 /*! \brief remove at most n_max stale session from the list. */
3019 static void purge_sessions(int n_max)
3021 struct mansession *s;
3022 time_t now = time(NULL);
3024 AST_LIST_LOCK(&sessions);
3025 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
3026 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
3027 AST_LIST_REMOVE_CURRENT(list);
3028 ast_atomic_fetchadd_int(&num_sessions, -1);
3029 if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
3030 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
3031 s->username, ast_inet_ntoa(s->sin.sin_addr));
3033 free_session(s); /* XXX outside ? */
3038 AST_LIST_TRAVERSE_SAFE_END;
3039 AST_LIST_UNLOCK(&sessions);
3043 * events are appended to a queue from where they
3044 * can be dispatched to clients.
3046 static int append_event(const char *str, int category)
3048 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
3049 static int seq; /* sequence number */
3054 /* need to init all fields, because ast_malloc() does not */
3056 tmp->category = category;
3057 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
3058 AST_LIST_NEXT(tmp, eq_next) = NULL;
3059 strcpy(tmp->eventdata, str);
3061 AST_LIST_LOCK(&all_events);
3062 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
3063 AST_LIST_UNLOCK(&all_events);
3068 /* XXX see if can be moved inside the function */
3069 AST_THREADSTORAGE(manager_event_buf);
3070 #define MANAGER_EVENT_BUF_INITSIZE 256
3072 /*! \brief manager_event: Send AMI event to client */
3073 int __manager_event(int category, const char *event,
3074 const char *file, int line, const char *func, const char *fmt, ...)
3076 struct mansession *s;
3077 struct manager_custom_hook *hook;
3078 struct ast_str *auth = ast_str_alloca(80);
3079 const char *cat_str;
3082 struct ast_str *buf;
3084 /* Abort if there aren't any manager sessions */
3088 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
3091 cat_str = authority_to_str(category, &auth);
3092 ast_str_set(&buf, 0,
3093 "Event: %s\r\nPrivilege: %s\r\n",
3096 if (timestampevents) {
3098 ast_str_append(&buf, 0,
3099 "Timestamp: %ld.%06lu\r\n",
3100 now.tv_sec, (unsigned long) now.tv_usec);
3102 if (manager_debug) {
3104 ast_str_append(&buf, 0,
3105 "SequenceNumber: %d\r\n",
3106 ast_atomic_fetchadd_int(&seq, 1));
3107 ast_str_append(&buf, 0,
3108 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
3112 ast_str_append_va(&buf, 0, fmt, ap);
3115 ast_str_append(&buf, 0, "\r\n");
3117 append_event(buf->str, category);
3119 /* Wake up any sleeping sessions */
3120 AST_LIST_LOCK(&sessions);
3121 AST_LIST_TRAVERSE(&sessions, s, list) {
3122 ast_mutex_lock(&s->__lock);
3123 if (s->waiting_thread != AST_PTHREADT_NULL)
3124 pthread_kill(s->waiting_thread, SIGURG);
3126 /* We have an event to process, but the mansession is
3127 * not waiting for it. We still need to indicate that there
3128 * is an event waiting so that get_input processes the pending
3129 * event instead of polling.
3131 s->pending_event = 1;
3132 ast_mutex_unlock(&s->__lock);
3134 AST_LIST_UNLOCK(&sessions);
3136 AST_RWLIST_RDLOCK(&manager_hooks);
3137 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
3138 hook->helper(category, event, buf->str);
3140 AST_RWLIST_UNLOCK(&manager_hooks);
3146 * support functions to register/unregister AMI action handlers,
3148 int ast_manager_unregister(char *action)
3150 struct manager_action *cur;
3152 AST_RWLIST_WRLOCK(&actions);
3153 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
3154 if (!strcasecmp(action, cur->action)) {
3155 AST_RWLIST_REMOVE_CURRENT(list);
3157 ast_verb(2, "Manager unregistered action %s\n", action);
3161 AST_RWLIST_TRAVERSE_SAFE_END;
3162 AST_RWLIST_UNLOCK(&actions);
3167 static int manager_state_cb(char *context, char *exten, int state, void *data)
3169 /* Notify managers of change */
3171 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
3173 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
3177 static int ast_manager_register_struct(struct manager_action *act)
3179 struct manager_action *cur, *prev = NULL;
3181 AST_RWLIST_WRLOCK(&actions);
3182 AST_RWLIST_TRAVERSE(&actions, cur, list) {
3183 int ret = strcasecmp(cur->action, act->action);
3185 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
3186 AST_RWLIST_UNLOCK(&actions);
3189 if (ret > 0) { /* Insert these alphabetically */
3196 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
3198 AST_RWLIST_INSERT_HEAD(&actions, act, list);
3200 ast_verb(2, "Manager registered action %s\n", act->action);
3202 AST_RWLIST_UNLOCK(&actions);
3207 /*! \brief register a new command with manager, including online help. This is
3208 the preferred way to register a manager command */
3209 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
3211 struct manager_action *cur = NULL;
3213 if (!(cur = ast_calloc(1, sizeof(*cur))))
3216 cur->action = action;
3217 cur->authority = auth;
3219 cur->synopsis = synopsis;
3220 cur->description = description;
3222 ast_manager_register_struct(cur);
3227 END Doxygen group */
3230 * The following are support functions for AMI-over-http.
3231 * The common entry point is generic_http_callback(),
3232 * which extracts HTTP header and URI fields and reformats
3233 * them into AMI messages, locates a proper session
3234 * (using the mansession_id Cookie or GET variable),
3235 * and calls process_message() as for regular AMI clients.
3236 * When done, the output (which goes to a temporary file)
3237 * is read back into a buffer and reformatted as desired,
3238 * then fed back to the client over the original socket.
3241 enum output_format {
3247 static char *contenttype[] = {
3248 [FORMAT_RAW] = "plain",
3249 [FORMAT_HTML] = "html",
3250 [FORMAT_XML] = "xml",
3254 * locate an http session in the list. The search key (ident) is
3255 * the value of the mansession_id cookie (0 is not valid and means
3256 * a session on the AMI socket).
3258 static struct mansession *find_session(uint32_t ident)
3260 struct mansession *s;
3265 AST_LIST_LOCK(&sessions);
3266 AST_LIST_TRAVERSE(&sessions, s, list) {
3267 ast_mutex_lock(&s->__lock);
3268 if (s->managerid == ident && !s->needdestroy) {
3269 ast_atomic_fetchadd_int(&s->inuse, 1);
3272 ast_mutex_unlock(&s->__lock);
3274 AST_LIST_UNLOCK(&sessions);
3279 int astman_verify_session_readpermissions(uint32_t ident, int perm)
3282 struct mansession *s;
3284 AST_LIST_LOCK(&sessions);
3285 AST_LIST_TRAVERSE(&sessions, s, list) {
3286 ast_mutex_lock(&s->__lock);
3287 if ((s->managerid == ident) && (s->readperm & perm)) {
3289 ast_mutex_unlock(&s->__lock);
3292 ast_mutex_unlock(&s->__lock);
3294 AST_LIST_UNLOCK(&sessions);
3298 int astman_verify_session_writepermissions(uint32_t ident, int perm)
3301 struct mansession *s;
3303 AST_LIST_LOCK(&sessions);
3304 AST_LIST_TRAVERSE(&sessions, s, list) {
3305 ast_mutex_lock(&s->__lock);
3306 if ((s->managerid == ident) && (s->writeperm & perm)) {
3308 ast_mutex_unlock(&s->__lock);
3311 ast_mutex_unlock(&s->__lock);
3313 AST_LIST_UNLOCK(&sessions);
3318 * convert to xml with various conversion:
3319 * mode & 1 -> lowercase;
3320 * mode & 2 -> replace non-alphanumeric chars with underscore
3322 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
3324 /* store in a local buffer to avoid calling ast_str_append too often */
3327 int space = sizeof(buf);
3328 /* repeat until done and nothing to flush */
3329 for ( ; *src || dst != buf ; src++) {
3330 if (*src == '\0' || space < 10) { /* flush */
3332 ast_str_append(out, 0, "%s", buf);
3334 space = sizeof(buf);
3339 if ( (mode & 2) && !isalnum(*src)) {
3346 strcpy(dst, "<");
3351 strcpy(dst, ">");
3356 strcpy(dst, """);
3361 strcpy(dst, "'");
3366 strcpy(dst, "&");
3372 *dst++ = mode ? tolower(*src) : *src;
3378 struct variable_count {
3383 static int compress_char(char c)
3388 else if (c >= 'a' && c <= 'z')
3396 static int variable_count_hash_fn(const void *vvc, const int flags)
3398 const struct variable_count *vc = vvc;
3400 for (i = 0; i < 5; i++) {
3401 if (vc->varname[i] == '\0')
3403 res += compress_char(vc->varname[i]) << (i * 6);
3408 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
3410 /* Due to the simplicity of struct variable_count, it makes no difference
3411 * if you pass in objects or strings, the same operation applies. This is
3412 * due to the fact that the hash occurs on the first element, which means
3413 * the address of both the struct and the string are exactly the same. */
3414 struct variable_count *vc = obj;
3416 return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
3419 /*! \brief Convert the input into XML or HTML.
3420 * The input is supposed to be a sequence of lines of the form
3422 * optionally followed by a blob of unformatted text.
3423 * A blank line is a section separator. Basically, this is a
3424 * mixture of the format of Manager Interface and CLI commands.
3425 * The unformatted text is considered as a single value of a field
3426 * named 'Opaque-data'.
3428 * At the moment the output format is the following (but it may
3429 * change depending on future requirements so don't count too
3430 * much on it when writing applications):
3432 * General: the unformatted text is used as a value of
3433 * XML output: to be completed
3436 * Each section is within <response type="object" id="xxx">
3437 * where xxx is taken from ajaxdest variable or defaults to unknown
3438 * Each row is reported as an attribute Name="value" of an XML
3439 * entity named from the variable ajaxobjtype, default to "generic"
3443 * each Name-value pair is output as a single row of a two-column table.
3444 * Sections (blank lines in the input) are separated by a <HR>
3447 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
3449 struct ast_variable *v;
3450 const char *dest = NULL;
3452 const char *objtype = NULL;
3453 int in_data = 0; /* parsing data */
3455 int xml = (format == FORMAT_XML);
3456 struct variable_count *vc = NULL;
3457 struct ao2_container *vco = NULL;
3459 for (v = vars; v; v = v->next) {
3460 if (!dest && !strcasecmp(v->name, "ajaxdest"))
3462 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
3468 objtype = "generic";
3470 /* we want to stop when we find an empty line */
3472 val = strsep(&in, "\r\n"); /* mark start and end of line */
3473 if (in && *in == '\n') /* remove trailing \n if any */
3475 ast_trim_blanks(val);
3476 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
3477 if (ast_strlen_zero(val)) {
3478 if (in_data) { /* close data */
3479 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3483 ast_str_append(out, 0, xml ? " /></response>\n" :
3484 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3492 /* we expect Name: value lines */
3496 var = strsep(&val, ":");
3497 if (val) { /* found the field name */
3498 val = ast_skip_blanks(val);
3499 ast_trim_blanks(var);
3500 } else { /* field name not found, move to opaque mode */
3502 var = "Opaque-data";
3508 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
3510 ast_str_append(out, 0, "<body>\n");
3511 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
3515 if (!in_data) { /* build appropriate line start */
3516 ast_str_append(out, 0, xml ? " " : "<tr><td>");
3517 if ((vc = ao2_find(vco, var, 0)))
3520 /* Create a new entry for this one */
3521 vc = ao2_alloc(sizeof(*vc), NULL);
3526 xml_copy_escape(out, var, xml ? 1 | 2 : 0);
3528 ast_str_append(out, 0, "-%d", vc->count);
3530 ast_str_append(out, 0, xml ? "='" : "</td><td>");
3531 if (!strcmp(var, "Opaque-data"))
3534 xml_copy_escape(out, val, 0); /* data field */
3536 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3538 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
3541 ast_str_append(out, 0, xml ? " /></response>\n" :
3542 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3547 static struct ast_str *generic_http_callback(enum output_format format,
3548 struct sockaddr_in *requestor, const char *uri, enum ast_http_method method,
3549 struct ast_variable *params, int *status,
3550 char **title, int *contentlength)
3552 struct mansession *s = NULL;
3555 struct ast_variable *v;
3556 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
3557 struct ast_str *out = NULL;
3558 struct message m = { 0 };
3562 for (v = params; v; v = v->next) {
3563 if (!strcasecmp(v->name, "mansession_id")) {
3564 sscanf(v->value, "%x", &ident);
3569 if (!(s = find_session(ident))) {
3570 /* Create new session.
3571 * While it is not in the list we don't need any locking
3573 if (!(s = ast_calloc(1, sizeof(*s)))) {
3575 goto generic_callback_out;
3577 s->sin = *requestor;
3579 s->waiting_thread = AST_PTHREADT_NULL;
3581 ast_mutex_init(&s->__lock);
3582 ast_mutex_lock(&s->__lock);
3584 /*!\note There is approximately a 1 in 1.8E19 chance that the following
3585 * calculation will produce 0, which is an invalid ID, but due to the
3586 * properties of the rand() function (and the constantcy of s), that
3587 * won't happen twice in a row.
3589 while ((s->managerid = rand() ^ (unsigned long) s) == 0);
3590 s->last_ev = grab_last();
3591 AST_LIST_LOCK(&sessions);
3592 AST_LIST_INSERT_HEAD(&sessions, s, list);
3593 ast_atomic_fetchadd_int(&num_sessions, 1);
3594 AST_LIST_UNLOCK(&sessions);
3597 ast_mutex_unlock(&s->__lock);
3599 if (!(out = ast_str_create(1024))) {
3601 goto generic_callback_out;
3604 s->fd = mkstemp(template); /* create a temporary file for command output */
3606 s->f = fdopen(s->fd, "w+");
3608 for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
3609 hdrlen = strlen(v->name) + strlen(v->value) + 3;
3610 m.headers[m.hdrcount] = alloca(hdrlen);
3611 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
3615 if (process_message(s, &m)) {
3616 if (s->authenticated) {
3617 if (manager_displayconnects(s))
3618 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3619 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3621 if (displayconnects)
3622 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
3623 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
3628 ast_str_append(&out, 0,
3629 "Content-type: text/%s\r\n"
3630 "Cache-Control: no-cache;\r\n"
3631 "Set-Cookie: mansession_id=\"%08x\"; Version=\"1\"; Max-Age=%d\r\n"
3633 contenttype[format],
3634 s->managerid, httptimeout);
3636 if (format == FORMAT_XML) {
3637 ast_str_append(&out, 0, "<ajax-response>\n");
3638 } else if (format == FORMAT_HTML) {
3640 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
3641 #define TEST_STRING \
3642 "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
3643 user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
3644 <input type=\"submit\"></form>"
3646 ast_str_append(&out, 0, "<title>Asterisk™ Manager Interface</title>");
3647 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
3648 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
3649 ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
3652 if (s->f != NULL) { /* have temporary output */
3654 size_t l = ftell(s->f);
3657 if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
3658 if (format == FORMAT_XML || format == FORMAT_HTML)
3659 xml_translate(&out, buf, params, format);
3661 ast_str_append(&out, 0, "%s", buf);
3664 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
3665 xml_translate(&out, "", params, format);
3672 if (format == FORMAT_XML) {
3673 ast_str_append(&out, 0, "</ajax-response>\n");
3674 } else if (format == FORMAT_HTML)
3675 ast_str_append(&out, 0, "</table></body>\r\n");
3677 ast_mutex_lock(&s->__lock);
3678 /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */
3679 s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
3681 if (s->needdestroy) {
3682 if (s->inuse == 1) {
3683 ast_debug(1, "Need destroy, doing it now!\n");
3686 ast_debug(1, "Need destroy, but can't do it yet!\n");
3687 if (s->waiting_thread != AST_PTHREADT_NULL)
3688 pthread_kill(s->waiting_thread, SIGURG);
3693 ast_mutex_unlock(&s->__lock);
3697 generic_callback_out:
3699 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
3703 static struct ast_str *manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength)
3705 return generic_http_callback(FORMAT_HTML, &ser->requestor, uri, method, params, status, title, contentlength);
3708 static struct ast_str *mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength)
3710 return generic_http_callback(FORMAT_XML, &ser->requestor, uri, method, params, status, title, contentlength);
3713 static struct ast_str *rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength)
3715 return generic_http_callback(FORMAT_RAW, &ser->requestor, uri, method, params, status, title, contentlength);
3718 struct ast_http_uri rawmanuri = {
3719 .description = "Raw HTTP Manager Event Interface",
3721 .callback = rawman_http_callback,
3727 struct ast_http_uri manageruri = {
3728 .description = "HTML Manager Event Interface",
3730 .callback = manager_http_callback,
3736 struct ast_http_uri managerxmluri = {
3737 .description = "XML Manager Event Interface",