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 /*! \brief helper function for originate */
2110 struct fast_originate_helper {
2111 char tech[AST_MAX_EXTENSION];
2112 char data[AST_MAX_EXTENSION];
2114 int format; /*!< Codecs used for a call */
2115 char app[AST_MAX_APP];
2116 char appdata[AST_MAX_EXTENSION];
2117 char cid_name[AST_MAX_EXTENSION];
2118 char cid_num[AST_MAX_EXTENSION];
2119 char context[AST_MAX_CONTEXT];
2120 char exten[AST_MAX_EXTENSION];
2121 char idtext[AST_MAX_EXTENSION];
2122 char account[AST_MAX_ACCOUNT_CODE];
2124 struct ast_variable *vars;
2127 static void *fast_originate(void *data)
2129 struct fast_originate_helper *in = data;
2132 struct ast_channel *chan = NULL;
2133 char requested_channel[AST_CHANNEL_NAME];
2135 if (!ast_strlen_zero(in->app)) {
2136 res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
2137 S_OR(in->cid_num, NULL),
2138 S_OR(in->cid_name, NULL),
2139 in->vars, in->account, &chan);
2141 res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
2142 S_OR(in->cid_num, NULL),
2143 S_OR(in->cid_name, NULL),
2144 in->vars, in->account, &chan);
2148 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
2149 /* Tell the manager what happened with the channel */
2150 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
2158 "CallerIDNum: %s\r\n"
2159 "CallerIDName: %s\r\n",
2160 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
2161 chan ? chan->uniqueid : "<null>",
2162 S_OR(in->cid_num, "<unknown>"),
2163 S_OR(in->cid_name, "<unknown>")
2166 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2168 ast_channel_unlock(chan);
2173 static char mandescr_originate[] =
2174 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
2175 " Application/Data\n"
2176 "Variables: (Names marked with * are required)\n"
2177 " *Channel: Channel name to call\n"
2178 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
2179 " Context: Context to use (requires 'Exten' and 'Priority')\n"
2180 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
2181 " Application: Application to use\n"
2182 " Data: Data to use (requires 'Application')\n"
2183 " Timeout: How long to wait for call to be answered (in ms)\n"
2184 " CallerID: Caller ID to be set on the outgoing channel\n"
2185 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
2186 " Account: Account code\n"
2187 " Async: Set to 'true' for fast origination\n";
2189 static int action_originate(struct mansession *s, const struct message *m)
2191 const char *name = astman_get_header(m, "Channel");
2192 const char *exten = astman_get_header(m, "Exten");
2193 const char *context = astman_get_header(m, "Context");
2194 const char *priority = astman_get_header(m, "Priority");
2195 const char *timeout = astman_get_header(m, "Timeout");
2196 const char *callerid = astman_get_header(m, "CallerID");
2197 const char *account = astman_get_header(m, "Account");
2198 const char *app = astman_get_header(m, "Application");
2199 const char *appdata = astman_get_header(m, "Data");
2200 const char *async = astman_get_header(m, "Async");
2201 const char *id = astman_get_header(m, "ActionID");
2202 const char *codecs = astman_get_header(m, "Codecs");
2203 struct ast_variable *vars = astman_get_variables(m);
2205 char *l = NULL, *n = NULL;
2212 int format = AST_FORMAT_SLINEAR;
2216 astman_send_error(s, m, "Channel not specified");
2219 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2220 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2221 astman_send_error(s, m, "Invalid priority\n");
2225 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2226 astman_send_error(s, m, "Invalid timeout\n");
2229 ast_copy_string(tmp, name, sizeof(tmp));
2231 data = strchr(tmp, '/');
2233 astman_send_error(s, m, "Invalid channel\n");
2237 ast_copy_string(tmp2, callerid, sizeof(tmp2));
2238 ast_callerid_parse(tmp2, &n, &l);
2240 if (ast_strlen_zero(n))
2244 ast_shrink_phone_number(l);
2245 if (ast_strlen_zero(l))
2248 if (!ast_strlen_zero(codecs)) {
2250 ast_parse_allow_disallow(NULL, &format, codecs, 1);
2252 if (ast_true(async)) {
2253 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2257 if (!ast_strlen_zero(id))
2258 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2259 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2260 ast_copy_string(fast->data, data, sizeof(fast->data));
2261 ast_copy_string(fast->app, app, sizeof(fast->app));
2262 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2264 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2266 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2268 ast_copy_string(fast->context, context, sizeof(fast->context));
2269 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2270 ast_copy_string(fast->account, account, sizeof(fast->account));
2271 fast->format = format;
2273 fast->priority = pi;
2274 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2280 } else if (!ast_strlen_zero(app)) {
2281 /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
2282 if (!(s->writeperm & EVENT_FLAG_SYSTEM)
2284 strcasestr(app, "system") == 0 || /* System(rm -rf /)
2285 TrySystem(rm -rf /) */
2286 strcasestr(app, "exec") || /* Exec(System(rm -rf /))
2287 TryExec(System(rm -rf /)) */
2288 strcasestr(app, "agi") || /* AGI(/bin/rm,-rf /)
2289 EAGI(/bin/rm,-rf /) */
2290 strstr(appdata, "SHELL") || /* NoOp(${SHELL(rm -rf /)}) */
2291 strstr(appdata, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
2293 astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
2296 res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2298 if (exten && context && pi)
2299 res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2301 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2306 astman_send_ack(s, m, "Originate successfully queued");
2308 astman_send_error(s, m, "Originate failed");
2312 /*! \brief Help text for manager command mailboxstatus
2314 static char mandescr_mailboxstatus[] =
2315 "Description: Checks a voicemail account for status.\n"
2316 "Variables: (Names marked with * are required)\n"
2317 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2318 " ActionID: Optional ActionID for message matching.\n"
2319 "Returns number of messages.\n"
2320 " Message: Mailbox Status\n"
2321 " Mailbox: <mailboxid>\n"
2322 " Waiting: <count>\n"
2325 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2327 const char *mailbox = astman_get_header(m, "Mailbox");
2330 if (ast_strlen_zero(mailbox)) {
2331 astman_send_error(s, m, "Mailbox not specified");
2334 ret = ast_app_has_voicemail(mailbox, NULL);
2335 astman_start_ack(s, m);
2336 astman_append(s, "Message: Mailbox Status\r\n"
2338 "Waiting: %d\r\n\r\n", mailbox, ret);
2342 static char mandescr_mailboxcount[] =
2343 "Description: Checks a voicemail account for new messages.\n"
2344 "Variables: (Names marked with * are required)\n"
2345 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2346 " ActionID: Optional ActionID for message matching.\n"
2347 "Returns number of urgent, new and old messages.\n"
2348 " Message: Mailbox Message Count\n"
2349 " Mailbox: <mailboxid>\n"
2350 " UrgentMessages: <count>\n"
2351 " NewMessages: <count>\n"
2352 " OldMessages: <count>\n"
2354 static int action_mailboxcount(struct mansession *s, const struct message *m)
2356 const char *mailbox = astman_get_header(m, "Mailbox");
2357 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
2359 if (ast_strlen_zero(mailbox)) {
2360 astman_send_error(s, m, "Mailbox not specified");
2363 ast_app_inboxcount(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
2364 astman_start_ack(s, m);
2365 astman_append(s, "Message: Mailbox Message Count\r\n"
2367 "UrgMessages: %d\r\n"
2368 "NewMessages: %d\r\n"
2369 "OldMessages: %d\r\n"
2371 mailbox, urgentmsgs, newmsgs, oldmsgs);
2375 static char mandescr_extensionstate[] =
2376 "Description: Report the extension state for given extension.\n"
2377 " If the extension has a hint, will use devicestate to check\n"
2378 " the status of the device connected to the extension.\n"
2379 "Variables: (Names marked with * are required)\n"
2380 " *Exten: Extension to check state on\n"
2381 " *Context: Context for extension\n"
2382 " ActionId: Optional ID for this transaction\n"
2383 "Will return an \"Extension Status\" message.\n"
2384 "The response will include the hint for the extension and the status.\n";
2386 static int action_extensionstate(struct mansession *s, const struct message *m)
2388 const char *exten = astman_get_header(m, "Exten");
2389 const char *context = astman_get_header(m, "Context");
2390 char hint[256] = "";
2392 if (ast_strlen_zero(exten)) {
2393 astman_send_error(s, m, "Extension not specified");
2396 if (ast_strlen_zero(context))
2397 context = "default";
2398 status = ast_extension_state(NULL, context, exten);
2399 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2400 astman_start_ack(s, m);
2401 astman_append(s, "Message: Extension Status\r\n"
2405 "Status: %d\r\n\r\n",
2406 exten, context, hint, status);
2410 static char mandescr_timeout[] =
2411 "Description: Hangup a channel after a certain time.\n"
2412 "Variables: (Names marked with * are required)\n"
2413 " *Channel: Channel name to hangup\n"
2414 " *Timeout: Maximum duration of the call (sec)\n"
2415 "Acknowledges set time with 'Timeout Set' message\n";
2417 static int action_timeout(struct mansession *s, const struct message *m)
2419 struct ast_channel *c;
2420 const char *name = astman_get_header(m, "Channel");
2421 double timeout = atof(astman_get_header(m, "Timeout"));
2422 struct timeval tv = { timeout, 0 };
2424 if (ast_strlen_zero(name)) {
2425 astman_send_error(s, m, "No channel specified");
2428 if (!timeout || timeout < 0) {
2429 astman_send_error(s, m, "No timeout specified");
2432 c = ast_get_channel_by_name_locked(name);
2434 astman_send_error(s, m, "No such channel");
2438 tv.tv_usec = (timeout - tv.tv_sec) * 1000000.0;
2439 ast_channel_setwhentohangup_tv(c, tv);
2440 ast_channel_unlock(c);
2441 astman_send_ack(s, m, "Timeout Set");
2446 * Send any applicable events to the client listening on this socket.
2447 * Wait only for a finite time on each event, and drop all events whether
2448 * they are successfully sent or not.
2450 static int process_events(struct mansession *s)
2454 ast_mutex_lock(&s->__lock);
2456 struct eventqent *eqe;
2458 while ( (eqe = NEW_EVENT(s)) ) {
2460 if (!ret && s->authenticated &&
2461 (s->readperm & eqe->category) == eqe->category &&
2462 (s->send_events & eqe->category) == eqe->category) {
2463 if (send_string(s, eqe->eventdata) < 0)
2464 ret = -1; /* don't send more */
2466 s->last_ev = unref_event(s->last_ev);
2469 ast_mutex_unlock(&s->__lock);
2473 static char mandescr_userevent[] =
2474 "Description: Send an event to manager sessions.\n"
2475 "Variables: (Names marked with * are required)\n"
2476 " *UserEvent: EventStringToSend\n"
2477 " Header1: Content1\n"
2478 " HeaderN: ContentN\n";
2480 static int action_userevent(struct mansession *s, const struct message *m)
2482 const char *event = astman_get_header(m, "UserEvent");
2483 char body[2048] = "";
2485 for (x = 0; x < m->hdrcount; x++) {
2486 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2487 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2488 bodylen += strlen(m->headers[x]);
2489 ast_copy_string(body + bodylen, "\r\n", 3);
2494 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2498 static char mandescr_coresettings[] =
2499 "Description: Query for Core PBX settings.\n"
2500 "Variables: (Names marked with * are optional)\n"
2501 " *ActionID: ActionID of this transaction\n";
2503 /*! \brief Show PBX core settings information */
2504 static int action_coresettings(struct mansession *s, const struct message *m)
2506 const char *actionid = astman_get_header(m, "ActionID");
2509 if (!ast_strlen_zero(actionid))
2510 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2514 astman_append(s, "Response: Success\r\n"
2516 "AMIversion: %s\r\n"
2517 "AsteriskVersion: %s\r\n"
2518 "SystemName: %s\r\n"
2519 "CoreMaxCalls: %d\r\n"
2520 "CoreMaxLoadAvg: %f\r\n"
2521 "CoreRunUser: %s\r\n"
2522 "CoreRunGroup: %s\r\n"
2523 "CoreMaxFilehandles: %d\r\n"
2524 "CoreRealTimeEnabled: %s\r\n"
2525 "CoreCDRenabled: %s\r\n"
2526 "CoreHTTPenabled: %s\r\n"
2531 ast_config_AST_SYSTEM_NAME,
2534 ast_config_AST_RUN_USER,
2535 ast_config_AST_RUN_GROUP,
2537 ast_realtime_enabled() ? "Yes" : "No",
2538 check_cdr_enabled() ? "Yes" : "No",
2539 check_webmanager_enabled() ? "Yes" : "No"
2544 static char mandescr_corestatus[] =
2545 "Description: Query for Core PBX status.\n"
2546 "Variables: (Names marked with * are optional)\n"
2547 " *ActionID: ActionID of this transaction\n";
2549 /*! \brief Show PBX core status information */
2550 static int action_corestatus(struct mansession *s, const struct message *m)
2552 const char *actionid = astman_get_header(m, "ActionID");
2554 char startuptime[150];
2555 char reloadtime[150];
2558 if (!ast_strlen_zero(actionid))
2559 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2563 ast_localtime(&ast_startuptime, &tm, NULL);
2564 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2565 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2566 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2568 astman_append(s, "Response: Success\r\n"
2570 "CoreStartupTime: %s\r\n"
2571 "CoreReloadTime: %s\r\n"
2572 "CoreCurrentCalls: %d\r\n"
2577 ast_active_channels()
2582 static char mandescr_reload[] =
2583 "Description: Send a reload event.\n"
2584 "Variables: (Names marked with * are optional)\n"
2585 " *ActionID: ActionID of this transaction\n"
2586 " *Module: Name of the module to reload\n";
2588 /*! \brief Send a reload event */
2589 static int action_reload(struct mansession *s, const struct message *m)
2591 const char *module = astman_get_header(m, "Module");
2592 int res = ast_module_reload(S_OR(module, NULL));
2595 astman_send_ack(s, m, "Module Reloaded");
2597 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2601 static char mandescr_coreshowchannels[] =
2602 "Description: List currently defined channels and some information\n"
2605 " ActionID: Optional Action id for message matching.\n";
2607 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2608 * and some information about them. */
2609 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2611 const char *actionid = astman_get_header(m, "ActionID");
2612 char actionidtext[256];
2613 struct ast_channel *c = NULL;
2615 int duration, durh, durm, durs;
2617 if (!ast_strlen_zero(actionid))
2618 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2620 actionidtext[0] = '\0';
2622 astman_send_listack(s, m, "Channels will follow", "start");
2624 while ((c = ast_channel_walk_locked(c)) != NULL) {
2625 struct ast_channel *bc = ast_bridged_channel(c);
2626 char durbuf[10] = "";
2628 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2629 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2630 durh = duration / 3600;
2631 durm = (duration % 3600) / 60;
2632 durs = duration % 60;
2633 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2642 "ChannelState: %d\r\n"
2643 "ChannelStateDesc: %s\r\n"
2644 "Application: %s\r\n"
2645 "ApplicationData: %s\r\n"
2646 "CallerIDnum: %s\r\n"
2648 "AccountCode: %s\r\n"
2649 "BridgedChannel: %s\r\n"
2650 "BridgedUniqueID: %s\r\n"
2651 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2652 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2653 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2654 ast_channel_unlock(c);
2659 "Event: CoreShowChannelsComplete\r\n"
2660 "EventList: Complete\r\n"
2663 "\r\n", numchans, actionidtext);
2668 static char mandescr_modulecheck[] =
2669 "Description: Checks if Asterisk module is loaded\n"
2671 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2672 " Module: <name> Asterisk module name (not including extension)\n"
2674 "Will return Success/Failure\n"
2675 "For success returns, the module revision number is included.\n";
2677 /* Manager function to check if module is loaded */
2678 static int manager_modulecheck(struct mansession *s, const struct message *m)
2681 const char *module = astman_get_header(m, "Module");
2682 const char *id = astman_get_header(m, "ActionID");
2684 #if !defined(LOW_MEMORY)
2685 const char *version;
2687 char filename[PATH_MAX];
2690 ast_copy_string(filename, module, sizeof(filename));
2691 if ((cut = strchr(filename, '.'))) {
2694 cut = filename + strlen(filename);
2696 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
2697 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2698 res = ast_module_check(filename);
2700 astman_send_error(s, m, "Module not loaded");
2703 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
2704 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2705 #if !defined(LOW_MEMORY)
2706 version = ast_file_version_find(filename);
2709 if (!ast_strlen_zero(id))
2710 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2713 astman_append(s, "Response: Success\r\n%s", idText);
2714 #if !defined(LOW_MEMORY)
2715 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2720 static char mandescr_moduleload[] =
2721 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2723 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2724 " Module: <name> Asterisk module name (including .so extension)\n"
2725 " or subsystem identifier:\n"
2726 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2727 " LoadType: load | unload | reload\n"
2728 " The operation to be done on module\n"
2729 " If no module is specified for a reload loadtype, all modules are reloaded";
2731 static int manager_moduleload(struct mansession *s, const struct message *m)
2734 const char *module = astman_get_header(m, "Module");
2735 const char *loadtype = astman_get_header(m, "LoadType");
2737 if (!loadtype || strlen(loadtype) == 0)
2738 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2739 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2740 astman_send_error(s, m, "Need module name");
2742 if (!strcasecmp(loadtype, "load")) {
2743 res = ast_load_resource(module);
2745 astman_send_error(s, m, "Could not load module.");
2747 astman_send_ack(s, m, "Module loaded.");
2748 } else if (!strcasecmp(loadtype, "unload")) {
2749 res = ast_unload_resource(module, AST_FORCE_SOFT);
2751 astman_send_error(s, m, "Could not unload module.");
2753 astman_send_ack(s, m, "Module unloaded.");
2754 } else if (!strcasecmp(loadtype, "reload")) {
2755 if (module != NULL) {
2756 res = ast_module_reload(module);
2758 astman_send_error(s, m, "No such module.");
2760 astman_send_error(s, m, "Module does not support reload action.");
2762 astman_send_ack(s, m, "Module reloaded.");
2764 ast_module_reload(NULL); /* Reload all modules */
2765 astman_send_ack(s, m, "All modules reloaded");
2768 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2773 * Done with the action handlers here, we start with the code in charge
2774 * of accepting connections and serving them.
2775 * accept_thread() forks a new thread for each connection, session_do(),
2776 * which in turn calls get_input() repeatedly until a full message has
2777 * been accumulated, and then invokes process_message() to pass it to
2778 * the appropriate handler.
2782 * Process an AMI message, performing desired action.
2783 * Return 0 on success, -1 on error that require the session to be destroyed.
2785 static int process_message(struct mansession *s, const struct message *m)
2787 char action[80] = "";
2789 struct manager_action *tmp;
2790 const char *user = astman_get_header(m, "Username");
2792 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2793 ast_debug(1, "Manager received command '%s'\n", action);
2795 if (ast_strlen_zero(action)) {
2796 ast_mutex_lock(&s->__lock);
2797 astman_send_error(s, m, "Missing action in request");
2798 ast_mutex_unlock(&s->__lock);
2802 if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2803 ast_mutex_lock(&s->__lock);
2804 astman_send_error(s, m, "Permission denied");
2805 ast_mutex_unlock(&s->__lock);
2809 if (!allowmultiplelogin && !s->authenticated && user &&
2810 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2811 if (check_manager_session_inuse(user)) {
2813 ast_mutex_lock(&s->__lock);
2814 astman_send_error(s, m, "Login Already In Use");
2815 ast_mutex_unlock(&s->__lock);
2820 AST_RWLIST_RDLOCK(&actions);
2821 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2822 if (strcasecmp(action, tmp->action))
2824 if (s->writeperm & tmp->authority || tmp->authority == 0)
2825 ret = tmp->func(s, m);
2827 astman_send_error(s, m, "Permission denied");
2830 AST_RWLIST_UNLOCK(&actions);
2834 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2835 ast_mutex_lock(&s->__lock);
2836 astman_send_error(s, m, buf);
2837 ast_mutex_unlock(&s->__lock);
2841 /* Once done with our message, deliver any pending events */
2842 return process_events(s);
2846 * Read one full line (including crlf) from the manager socket.
2848 * \r\n is the only valid terminator for the line.
2849 * (Note that, later, '\0' will be considered as the end-of-line marker,
2850 * so everything between the '\0' and the '\r\n' will not be used).
2851 * Also note that we assume output to have at least "maxlen" space.
2854 static int get_input(struct mansession *s, char *output)
2857 int maxlen = sizeof(s->inbuf) - 1;
2858 char *src = s->inbuf;
2861 * Look for \r\n within the buffer. If found, copy to the output
2862 * buffer and return, trimming the \r\n (not used afterwards).
2864 for (x = 0; x < s->inlen; x++) {
2865 int cr; /* set if we have \r */
2866 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2867 cr = 2; /* Found. Update length to include \r\n */
2868 else if (src[x] == '\n')
2869 cr = 1; /* also accept \n only */
2872 memmove(output, src, x); /*... but trim \r\n */
2873 output[x] = '\0'; /* terminate the string */
2874 x += cr; /* number of bytes used */
2875 s->inlen -= x; /* remaining size */
2876 memmove(src, src + x, s->inlen); /* remove used bytes */
2879 if (s->inlen >= maxlen) {
2880 /* no crlf found, and buffer full - sorry, too long for us */
2881 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2886 /* XXX do we really need this locking ? */
2887 ast_mutex_lock(&s->__lock);
2888 if (s->pending_event) {
2889 s->pending_event = 0;
2890 ast_mutex_unlock(&s->__lock);
2893 s->waiting_thread = pthread_self();
2894 ast_mutex_unlock(&s->__lock);
2896 res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
2898 ast_mutex_lock(&s->__lock);
2899 s->waiting_thread = AST_PTHREADT_NULL;
2900 ast_mutex_unlock(&s->__lock);
2903 /* If we get a signal from some other thread (typically because
2904 * there are new events queued), return 0 to notify the caller.
2906 if (errno == EINTR || errno == EAGAIN)
2908 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2911 ast_mutex_lock(&s->__lock);
2912 res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2914 res = -1; /* error return */
2917 src[s->inlen] = '\0';
2920 ast_mutex_unlock(&s->__lock);
2924 static int do_message(struct mansession *s)
2926 struct message m = { 0 };
2927 char header_buf[sizeof(s->inbuf)] = { '\0' };
2931 /* Check if any events are pending and do them if needed */
2932 if (process_events(s))
2934 res = get_input(s, header_buf);
2937 } else if (res > 0) {
2938 if (ast_strlen_zero(header_buf))
2939 return process_message(s, &m) ? -1 : 0;
2940 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2941 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2948 /*! \brief The body of the individual manager session.
2949 * Call get_input() to read one line at a time
2950 * (or be woken up on new events), collect the lines in a
2951 * message until found an empty line, and execute the request.
2952 * In any case, deliver events asynchronously through process_events()
2953 * (called from here if no line is available, or at the end of
2954 * process_message(). )
2956 static void *session_do(void *data)
2958 struct ast_tcptls_session_instance *ser = data;
2959 struct mansession *s = ast_calloc(1, sizeof(*s));
2966 s->writetimeout = 100;
2967 s->waiting_thread = AST_PTHREADT_NULL;
2969 flags = fcntl(ser->fd, F_GETFL);
2970 if (!block_sockets) /* make sure socket is non-blocking */
2971 flags |= O_NONBLOCK;
2973 flags &= ~O_NONBLOCK;
2974 fcntl(ser->fd, F_SETFL, flags);
2976 ast_mutex_init(&s->__lock);
2977 s->send_events = -1;
2978 /* these fields duplicate those in the 'ser' structure */
2981 s->sin = ser->requestor;
2983 AST_LIST_LOCK(&sessions);
2984 AST_LIST_INSERT_HEAD(&sessions, s, list);
2985 ast_atomic_fetchadd_int(&num_sessions, 1);
2986 AST_LIST_UNLOCK(&sessions);
2987 /* Hook to the tail of the event queue */
2988 s->last_ev = grab_last();
2990 astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
2992 if ((res = do_message(s)) < 0)
2995 /* session is over, explain why and terminate */
2996 if (s->authenticated) {
2997 if (manager_displayconnects(s))
2998 ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2999 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3001 if (displayconnects)
3002 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
3003 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
3006 /* It is possible under certain circumstances for this session thread
3007 to complete its work and exit *before* the thread that created it
3008 has finished executing the ast_pthread_create_background() function.
3009 If this occurs, some versions of glibc appear to act in a buggy
3010 fashion and attempt to write data into memory that it thinks belongs
3011 to the thread but is in fact not owned by the thread (or may have
3012 been freed completely).
3014 Causing this thread to yield to other threads at least one time
3015 appears to work around this bug.
3022 ser = ast_tcptls_session_instance_destroy(ser);
3026 /*! \brief remove at most n_max stale session from the list. */
3027 static void purge_sessions(int n_max)
3029 struct mansession *s;
3030 time_t now = time(NULL);
3032 AST_LIST_LOCK(&sessions);
3033 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
3034 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
3035 AST_LIST_REMOVE_CURRENT(list);
3036 ast_atomic_fetchadd_int(&num_sessions, -1);
3037 if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
3038 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
3039 s->username, ast_inet_ntoa(s->sin.sin_addr));
3041 free_session(s); /* XXX outside ? */
3046 AST_LIST_TRAVERSE_SAFE_END;
3047 AST_LIST_UNLOCK(&sessions);
3051 * events are appended to a queue from where they
3052 * can be dispatched to clients.
3054 static int append_event(const char *str, int category)
3056 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
3057 static int seq; /* sequence number */
3062 /* need to init all fields, because ast_malloc() does not */
3064 tmp->category = category;
3065 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
3066 AST_LIST_NEXT(tmp, eq_next) = NULL;
3067 strcpy(tmp->eventdata, str);
3069 AST_LIST_LOCK(&all_events);
3070 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
3071 AST_LIST_UNLOCK(&all_events);
3076 /* XXX see if can be moved inside the function */
3077 AST_THREADSTORAGE(manager_event_buf);
3078 #define MANAGER_EVENT_BUF_INITSIZE 256
3080 /*! \brief manager_event: Send AMI event to client */
3081 int __manager_event(int category, const char *event,
3082 const char *file, int line, const char *func, const char *fmt, ...)
3084 struct mansession *s;
3085 struct manager_custom_hook *hook;
3086 struct ast_str *auth = ast_str_alloca(80);
3087 const char *cat_str;
3090 struct ast_str *buf;
3092 /* Abort if there aren't any manager sessions */
3096 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
3099 cat_str = authority_to_str(category, &auth);
3100 ast_str_set(&buf, 0,
3101 "Event: %s\r\nPrivilege: %s\r\n",
3104 if (timestampevents) {
3106 ast_str_append(&buf, 0,
3107 "Timestamp: %ld.%06lu\r\n",
3108 now.tv_sec, (unsigned long) now.tv_usec);
3110 if (manager_debug) {
3112 ast_str_append(&buf, 0,
3113 "SequenceNumber: %d\r\n",
3114 ast_atomic_fetchadd_int(&seq, 1));
3115 ast_str_append(&buf, 0,
3116 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
3120 ast_str_append_va(&buf, 0, fmt, ap);
3123 ast_str_append(&buf, 0, "\r\n");
3125 append_event(buf->str, category);
3127 /* Wake up any sleeping sessions */
3128 AST_LIST_LOCK(&sessions);
3129 AST_LIST_TRAVERSE(&sessions, s, list) {
3130 ast_mutex_lock(&s->__lock);
3131 if (s->waiting_thread != AST_PTHREADT_NULL)
3132 pthread_kill(s->waiting_thread, SIGURG);
3134 /* We have an event to process, but the mansession is
3135 * not waiting for it. We still need to indicate that there
3136 * is an event waiting so that get_input processes the pending
3137 * event instead of polling.
3139 s->pending_event = 1;
3140 ast_mutex_unlock(&s->__lock);
3142 AST_LIST_UNLOCK(&sessions);
3144 AST_RWLIST_RDLOCK(&manager_hooks);
3145 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
3146 hook->helper(category, event, buf->str);
3148 AST_RWLIST_UNLOCK(&manager_hooks);
3154 * support functions to register/unregister AMI action handlers,
3156 int ast_manager_unregister(char *action)
3158 struct manager_action *cur;
3160 AST_RWLIST_WRLOCK(&actions);
3161 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
3162 if (!strcasecmp(action, cur->action)) {
3163 AST_RWLIST_REMOVE_CURRENT(list);
3165 ast_verb(2, "Manager unregistered action %s\n", action);
3169 AST_RWLIST_TRAVERSE_SAFE_END;
3170 AST_RWLIST_UNLOCK(&actions);
3175 static int manager_state_cb(char *context, char *exten, int state, void *data)
3177 /* Notify managers of change */
3179 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
3181 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
3185 static int ast_manager_register_struct(struct manager_action *act)
3187 struct manager_action *cur, *prev = NULL;
3189 AST_RWLIST_WRLOCK(&actions);
3190 AST_RWLIST_TRAVERSE(&actions, cur, list) {
3191 int ret = strcasecmp(cur->action, act->action);
3193 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
3194 AST_RWLIST_UNLOCK(&actions);
3197 if (ret > 0) { /* Insert these alphabetically */
3204 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
3206 AST_RWLIST_INSERT_HEAD(&actions, act, list);
3208 ast_verb(2, "Manager registered action %s\n", act->action);
3210 AST_RWLIST_UNLOCK(&actions);
3215 /*! \brief register a new command with manager, including online help. This is
3216 the preferred way to register a manager command */
3217 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
3219 struct manager_action *cur = NULL;
3221 if (!(cur = ast_calloc(1, sizeof(*cur))))
3224 cur->action = action;
3225 cur->authority = auth;
3227 cur->synopsis = synopsis;
3228 cur->description = description;
3230 ast_manager_register_struct(cur);
3235 END Doxygen group */
3238 * The following are support functions for AMI-over-http.
3239 * The common entry point is generic_http_callback(),
3240 * which extracts HTTP header and URI fields and reformats
3241 * them into AMI messages, locates a proper session
3242 * (using the mansession_id Cookie or GET variable),
3243 * and calls process_message() as for regular AMI clients.
3244 * When done, the output (which goes to a temporary file)
3245 * is read back into a buffer and reformatted as desired,
3246 * then fed back to the client over the original socket.