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 #define MAX_BLACKLIST_CMD_LEN 2
144 char *words[AST_MAX_CMD_LEN];
145 } command_blacklist[] = {
146 {{ "module", "load", NULL }},
147 {{ "module", "unload", NULL }},
151 pthread_t ms_t; /*!< Execution thread, basically useless */
152 ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
153 /* XXX need to document which fields it is protecting */
154 struct sockaddr_in sin; /*!< address we are connecting from */
155 FILE *f; /*!< fdopen() on the underlying fd */
156 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
157 int inuse; /*!< number of HTTP sessions using this entry */
158 int needdestroy; /*!< Whether an HTTP session should be destroyed */
159 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
160 uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
161 time_t sessionstart; /*!< Session start time */
162 time_t sessiontimeout; /*!< Session timeout if HTTP */
163 char username[80]; /*!< Logged in username */
164 char challenge[10]; /*!< Authentication challenge */
165 int authenticated; /*!< Authentication status */
166 int readperm; /*!< Authorization for reading */
167 int writeperm; /*!< Authorization for writing */
168 char inbuf[1025]; /*!< Buffer */
169 /* we use the extra byte to add a '\0' and simplify parsing */
170 int inlen; /*!< number of buffered bytes */
171 int send_events; /*!< XXX what ? */
172 struct eventqent *last_ev; /*!< last event processed. */
173 int writetimeout; /*!< Timeout for ast_carefulwrite() */
174 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
175 AST_LIST_ENTRY(mansession) list;
178 #define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
180 static AST_LIST_HEAD_STATIC(sessions, mansession);
182 /*! \brief user descriptor, as read from the config file.
184 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
185 * lines which are not supported here, and readperm/writeperm/writetimeout
188 struct ast_manager_user {
191 struct ast_ha *ha; /*!< ACL setting */
192 int readperm; /*! Authorization for reading */
193 int writeperm; /*! Authorization for writing */
194 int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
195 int displayconnects; /*!< XXX unused */
196 int keep; /*!< mark entries created on a reload */
197 AST_RWLIST_ENTRY(ast_manager_user) list;
200 /*! \brief list of users found in the config file */
201 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
203 /*! \brief list of actions registered */
204 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
206 /*! \brief list of hooks registered */
207 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
209 /*! \brief Add a custom hook to be called when an event is fired */
210 void ast_manager_register_hook(struct manager_custom_hook *hook)
212 AST_RWLIST_WRLOCK(&manager_hooks);
213 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
214 AST_RWLIST_UNLOCK(&manager_hooks);
218 /*! \brief Delete a custom hook to be called when an event is fired */
219 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
221 AST_RWLIST_WRLOCK(&manager_hooks);
222 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
223 AST_RWLIST_UNLOCK(&manager_hooks);
228 * Event list management functions.
229 * We assume that the event list always has at least one element,
230 * and the delete code will not remove the last entry even if the
234 static time_t __deb(time_t start, const char *msg)
236 time_t now = time(NULL);
237 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
238 if (start != 0 && now - start > 5)
239 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
243 static void LOCK_EVENTS(void)
245 time_t start = __deb(0, "about to lock events");
246 AST_LIST_LOCK(&all_events);
247 __deb(start, "done lock events");
250 static void UNLOCK_EVENTS(void)
252 __deb(0, "about to unlock events");
253 AST_LIST_UNLOCK(&all_events);
256 static void LOCK_SESS(void)
258 time_t start = __deb(0, "about to lock sessions");
259 AST_LIST_LOCK(&sessions);
260 __deb(start, "done lock sessions");
263 static void UNLOCK_SESS(void)
265 __deb(0, "about to unlock sessions");
266 AST_LIST_UNLOCK(&sessions);
270 int check_manager_enabled()
272 return manager_enabled;
275 int check_webmanager_enabled()
277 return (webmanager_enabled && manager_enabled);
281 * Grab a reference to the last event, update usecount as needed.
282 * Can handle a NULL pointer.
284 static struct eventqent *grab_last(void)
286 struct eventqent *ret;
288 AST_LIST_LOCK(&all_events);
289 ret = AST_LIST_LAST(&all_events);
290 /* the list is never empty now, but may become so when
291 * we optimize it in the future, so be prepared.
294 ast_atomic_fetchadd_int(&ret->usecount, 1);
295 AST_LIST_UNLOCK(&all_events);
300 * Purge unused events. Remove elements from the head
301 * as long as their usecount is 0 and there is a next element.
303 static void purge_events(void)
305 struct eventqent *ev;
307 AST_LIST_LOCK(&all_events);
308 while ( (ev = AST_LIST_FIRST(&all_events)) &&
309 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
310 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
313 AST_LIST_UNLOCK(&all_events);
317 * helper functions to convert back and forth between
318 * string and numeric representation of set of flags
320 static struct permalias {
324 { EVENT_FLAG_SYSTEM, "system" },
325 { EVENT_FLAG_CALL, "call" },
326 { EVENT_FLAG_LOG, "log" },
327 { EVENT_FLAG_VERBOSE, "verbose" },
328 { EVENT_FLAG_COMMAND, "command" },
329 { EVENT_FLAG_AGENT, "agent" },
330 { EVENT_FLAG_USER, "user" },
331 { EVENT_FLAG_CONFIG, "config" },
332 { EVENT_FLAG_DTMF, "dtmf" },
333 { EVENT_FLAG_REPORTING, "reporting" },
334 { EVENT_FLAG_CDR, "cdr" },
335 { EVENT_FLAG_DIALPLAN, "dialplan" },
336 { EVENT_FLAG_ORIGINATE, "originate" },
341 /*! \brief Convert authority code to a list of options */
342 static char *authority_to_str(int authority, struct ast_str **res)
348 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
349 if (authority & perms[i].num) {
350 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
355 if ((*res)->used == 0) /* replace empty string with something sensible */
356 ast_str_append(res, 0, "<none>");
361 /*! Tells you if smallstr exists inside bigstr
362 which is delim by delim and uses no buf or stringsep
363 ast_instring("this|that|more","this",'|') == 1;
365 feel free to move this to app.c -anthm */
366 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
368 const char *val = bigstr, *next;
371 if ((next = strchr(val, delim))) {
372 if (!strncmp(val, smallstr, (next - val)))
377 return !strcmp(smallstr, val);
378 } while (*(val = (next + 1)));
383 static int get_perm(const char *instr)
390 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
391 if (ast_instring(instr, perms[x].label, ','))
399 * A number returns itself, false returns 0, true returns all flags,
400 * other strings return the flags that are set.
402 static int strings_to_mask(const char *string)
406 if (ast_strlen_zero(string))
409 for (p = string; *p; p++)
410 if (*p < '0' || *p > '9')
412 if (!p) /* all digits */
414 if (ast_false(string))
416 if (ast_true(string)) { /* all permissions */
418 for (x = 0; x<sizeof(perms) / sizeof(perms[0]); x++)
422 return get_perm(string);
425 static int check_manager_session_inuse(const char *name)
427 struct mansession *session = NULL;
429 AST_LIST_LOCK(&sessions);
430 AST_LIST_TRAVERSE(&sessions, session, list) {
431 if (!strcasecmp(session->username, name))
434 AST_LIST_UNLOCK(&sessions);
436 return session ? 1 : 0;
441 * lookup an entry in the list of registered users.
442 * must be called with the list lock held.
444 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
446 struct ast_manager_user *user = NULL;
448 AST_RWLIST_TRAVERSE(&users, user, list)
449 if (!strcasecmp(user->username, name))
454 /*! \brief Get displayconnects config option.
455 * \param s manager session to get parameter from.
456 * \return displayconnects config option value.
458 static int manager_displayconnects (struct mansession *s)
460 struct ast_manager_user *user = NULL;
463 AST_RWLIST_RDLOCK(&users);
464 if ((user = get_manager_by_name_locked (s->username)))
465 ret = user->displayconnects;
466 AST_RWLIST_UNLOCK(&users);
471 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
473 struct manager_action *cur;
474 struct ast_str *authority;
479 e->command = "manager show command";
481 "Usage: manager show command <actionname>\n"
482 " Shows the detailed description for a specific Asterisk manager interface command.\n";
487 AST_RWLIST_RDLOCK(&actions);
488 AST_RWLIST_TRAVERSE(&actions, cur, list) {
489 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
490 ret = ast_strdup(cur->action);
491 break; /* make sure we exit even if ast_strdup() returns NULL */
494 AST_RWLIST_UNLOCK(&actions);
497 authority = ast_str_alloca(80);
499 return CLI_SHOWUSAGE;
501 AST_RWLIST_RDLOCK(&actions);
502 AST_RWLIST_TRAVERSE(&actions, cur, list) {
503 for (num = 3; num < a->argc; num++) {
504 if (!strcasecmp(cur->action, a->argv[num])) {
505 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
506 cur->action, cur->synopsis,
507 authority_to_str(cur->authority, &authority),
508 S_OR(cur->description, ""));
512 AST_RWLIST_UNLOCK(&actions);
517 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
521 e->command = "manager debug [on|off]";
522 e->usage = "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
528 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
529 else if (a->argc == 3) {
530 if (!strcasecmp(a->argv[2], "on"))
532 else if (!strcasecmp(a->argv[2], "off"))
535 return CLI_SHOWUSAGE;
540 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
542 struct ast_manager_user *user = NULL;
545 struct ast_str *rauthority = ast_str_alloca(80);
546 struct ast_str *wauthority = ast_str_alloca(80);
550 e->command = "manager show user";
552 " Usage: manager show user <user>\n"
553 " Display all information related to the manager user specified.\n";
560 AST_RWLIST_RDLOCK(&users);
561 AST_RWLIST_TRAVERSE(&users, user, list) {
562 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
563 ret = ast_strdup(user->username);
567 AST_RWLIST_UNLOCK(&users);
572 return CLI_SHOWUSAGE;
574 AST_RWLIST_RDLOCK(&users);
576 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
577 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
578 AST_RWLIST_UNLOCK(&users);
582 ast_cli(a->fd, "\n");
589 "displayconnects: %s\n",
590 (user->username ? user->username : "(N/A)"),
591 (user->secret ? "<Set>" : "(N/A)"),
592 (user->ha ? "yes" : "no"),
593 authority_to_str(user->readperm, &rauthority),
594 authority_to_str(user->writeperm, &wauthority),
595 (user->displayconnects ? "yes" : "no"));
597 AST_RWLIST_UNLOCK(&users);
603 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
605 struct ast_manager_user *user = NULL;
609 e->command = "manager show users";
611 "Usage: manager show users\n"
612 " Prints a listing of all managers that are currently configured on that\n"
619 return CLI_SHOWUSAGE;
621 AST_RWLIST_RDLOCK(&users);
623 /* If there are no users, print out something along those lines */
624 if (AST_RWLIST_EMPTY(&users)) {
625 ast_cli(a->fd, "There are no manager users.\n");
626 AST_RWLIST_UNLOCK(&users);
630 ast_cli(a->fd, "\nusername\n--------\n");
632 AST_RWLIST_TRAVERSE(&users, user, list) {
633 ast_cli(a->fd, "%s\n", user->username);
637 AST_RWLIST_UNLOCK(&users);
639 ast_cli(a->fd, "-------------------\n");
640 ast_cli(a->fd, "%d manager users configured.\n", count_amu);
646 /*! \brief CLI command manager list commands */
647 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
649 struct manager_action *cur;
650 struct ast_str *authority;
651 #define HSMC_FORMAT " %-15.15s %-15.15s %-55.55s\n"
654 e->command = "manager show commands";
656 "Usage: manager show commands\n"
657 " Prints a listing of all the available Asterisk manager interface commands.\n";
662 authority = ast_str_alloca(80);
663 ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
664 ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
666 AST_RWLIST_RDLOCK(&actions);
667 AST_RWLIST_TRAVERSE(&actions, cur, list)
668 ast_cli(a->fd, HSMC_FORMAT, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
669 AST_RWLIST_UNLOCK(&actions);
674 /*! \brief CLI command manager list connected */
675 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
677 struct mansession *s;
678 time_t now = time(NULL);
679 #define HSMCONN_FORMAT1 " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
680 #define HSMCONN_FORMAT2 " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
684 e->command = "manager show connected";
686 "Usage: manager show connected\n"
687 " Prints a listing of the users that are currently connected to the\n"
688 "Asterisk manager interface.\n";
694 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
696 AST_LIST_LOCK(&sessions);
697 AST_LIST_TRAVERSE(&sessions, s, list) {
698 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);
701 AST_LIST_UNLOCK(&sessions);
703 ast_cli(a->fd, "%d users connected.\n", count);
708 /*! \brief CLI command manager list eventq */
709 /* Should change to "manager show connected" */
710 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
715 e->command = "manager show eventq";
717 "Usage: manager show eventq\n"
718 " Prints a listing of all events pending in the Asterisk manger\n"
724 AST_LIST_LOCK(&all_events);
725 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
726 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
727 ast_cli(a->fd, "Category: %d\n", s->category);
728 ast_cli(a->fd, "Event:\n%s", s->eventdata);
730 AST_LIST_UNLOCK(&all_events);
735 /*! \brief CLI command manager reload */
736 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
740 e->command = "manager reload";
742 "Usage: manager reload\n"
743 " Reloads the manager configuration.\n";
749 return CLI_SHOWUSAGE;
755 static struct ast_cli_entry cli_manager[] = {
756 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
757 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
758 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
759 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
760 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
761 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
762 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
763 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
767 * Decrement the usecount for the event; if it goes to zero,
768 * (why check for e->next ?) wakeup the
769 * main thread, which is in charge of freeing the record.
770 * Returns the next record.
772 static struct eventqent *unref_event(struct eventqent *e)
774 ast_atomic_fetchadd_int(&e->usecount, -1);
775 return AST_LIST_NEXT(e, eq_next);
778 static void ref_event(struct eventqent *e)
780 ast_atomic_fetchadd_int(&e->usecount, 1);
784 * destroy a session, leaving the usecount
786 static void free_session(struct mansession *s)
788 struct eventqent *eqe = s->last_ev;
791 ast_mutex_destroy(&s->__lock);
796 static void destroy_session(struct mansession *s)
798 AST_LIST_LOCK(&sessions);
799 AST_LIST_REMOVE(&sessions, s, list);
800 ast_atomic_fetchadd_int(&num_sessions, -1);
802 AST_LIST_UNLOCK(&sessions);
806 * Generic function to return either the first or the last matching header
807 * from a list of variables, possibly skipping empty strings.
808 * At the moment there is only one use of this function in this file,
809 * so we make it static.
811 #define GET_HEADER_FIRST_MATCH 0
812 #define GET_HEADER_LAST_MATCH 1
813 #define GET_HEADER_SKIP_EMPTY 2
814 static const char *__astman_get_header(const struct message *m, char *var, int mode)
816 int x, l = strlen(var);
817 const char *result = "";
819 for (x = 0; x < m->hdrcount; x++) {
820 const char *h = m->headers[x];
821 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ') {
822 const char *x = h + l + 2;
823 /* found a potential candidate */
824 if (mode & GET_HEADER_SKIP_EMPTY && ast_strlen_zero(x))
825 continue; /* not interesting */
826 if (mode & GET_HEADER_LAST_MATCH)
827 result = x; /* record the last match so far */
837 * Return the first matching variable from an array.
838 * This is the legacy function and is implemented in therms of
839 * __astman_get_header().
841 const char *astman_get_header(const struct message *m, char *var)
843 return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
847 struct ast_variable *astman_get_variables(const struct message *m)
850 struct ast_variable *head = NULL, *cur;
852 AST_DECLARE_APP_ARGS(args,
853 AST_APP_ARG(vars)[32];
856 varlen = strlen("Variable: ");
858 for (x = 0; x < m->hdrcount; x++) {
859 char *parse, *var, *val;
861 if (strncasecmp("Variable: ", m->headers[x], varlen))
863 parse = ast_strdupa(m->headers[x] + varlen);
865 AST_STANDARD_APP_ARGS(args, parse);
868 for (y = 0; y < args.argc; y++) {
871 var = val = ast_strdupa(args.vars[y]);
873 if (!val || ast_strlen_zero(var))
875 cur = ast_variable_new(var, val, "");
885 * helper function to send a string to the socket.
886 * Return -1 on error (e.g. buffer full).
888 static int send_string(struct mansession *s, char *string)
890 int len = strlen(string); /* residual length */
892 struct timeval start = ast_tvnow();
898 n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
899 if (n == len /* ok */ || n < 0 /* error */)
901 len -= n; /* skip already written data */
905 n = -1; /* error marker */
906 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
907 if (elapsed > s->writetimeout)
909 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
913 return n < 0 ? -1 : 0;
917 * \brief thread local buffer for astman_append
919 * \note This can not be defined within the astman_append() function
920 * because it declares a couple of functions that get used to
921 * initialize the thread local storage key.
923 AST_THREADSTORAGE(astman_append_buf);
924 /*! \brief initial allocated size for the astman_append_buf */
925 #define ASTMAN_APPEND_BUF_INITSIZE 256
928 * utility functions for creating AMI replies
930 void astman_append(struct mansession *s, const char *fmt, ...)
935 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
939 ast_str_set_va(&buf, 0, fmt, ap);
943 send_string(s, buf->str);
945 ast_verbose("fd == -1 in astman_append, should not happen\n");
948 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
949 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
950 hold the session lock _or_ be running in an action callback (in which case s->busy will
951 be non-zero). In either of these cases, there is no need to lock-protect the session's
952 fd, since no other output will be sent (events will be queued), and no input will
953 be read until either the current action finishes or get_input() obtains the session
957 /*! \brief send a response with an optional message,
958 * and terminate it with an empty line.
959 * m is used only to grab the 'ActionID' field.
961 * Use the explicit constant MSG_MOREDATA to remove the empty line.
962 * XXX MSG_MOREDATA should go to a header file.
964 #define MSG_MOREDATA ((char *)astman_send_response)
965 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
967 const char *id = astman_get_header(m, "ActionID");
969 astman_append(s, "Response: %s\r\n", resp);
970 if (!ast_strlen_zero(id))
971 astman_append(s, "ActionID: %s\r\n", id);
973 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
974 if (msg == MSG_MOREDATA)
977 astman_append(s, "Message: %s\r\n\r\n", msg);
979 astman_append(s, "\r\n");
982 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
984 astman_send_response_full(s, m, resp, msg, NULL);
987 void astman_send_error(struct mansession *s, const struct message *m, char *error)
989 astman_send_response_full(s, m, "Error", error, NULL);
992 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
994 astman_send_response_full(s, m, "Success", msg, NULL);
997 static void astman_start_ack(struct mansession *s, const struct message *m)
999 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
1002 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
1004 astman_send_response_full(s, m, "Success", msg, listflag);
1009 Rather than braindead on,off this now can also accept a specific int mask value
1010 or a ',' delim list of mask strings (the same as manager.conf) -anthm
1012 static int set_eventmask(struct mansession *s, const char *eventmask)
1014 int maskint = strings_to_mask(eventmask);
1016 ast_mutex_lock(&s->__lock);
1018 s->send_events = maskint;
1019 ast_mutex_unlock(&s->__lock);
1025 * Here we start with action_ handlers for AMI actions,
1026 * and the internal functions used by them.
1027 * Generally, the handlers are called action_foo()
1030 /* helper function for action_login() */
1031 static int authenticate(struct mansession *s, const struct message *m)
1033 const char *username = astman_get_header(m, "Username");
1034 const char *password = astman_get_header(m, "Secret");
1036 struct ast_manager_user *user = NULL;
1038 if (ast_strlen_zero(username)) /* missing username */
1041 /* locate user in locked state */
1042 AST_RWLIST_WRLOCK(&users);
1044 if (!(user = get_manager_by_name_locked(username))) {
1045 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1046 } else if (user->ha && !ast_apply_ha(user->ha, &(s->sin))) {
1047 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1048 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
1049 const char *key = astman_get_header(m, "Key");
1050 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) && user->secret) {
1053 char md5key[256] = "";
1054 struct MD5Context md5;
1055 unsigned char digest[16];
1058 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1059 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
1060 MD5Final(digest, &md5);
1061 for (x = 0; x < 16; x++)
1062 len += sprintf(md5key + len, "%2.2x", digest[x]);
1063 if (!strcmp(md5key, key))
1066 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
1067 S_OR(s->challenge, ""));
1069 } else if (password && user->secret && !strcmp(password, user->secret))
1073 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1074 AST_RWLIST_UNLOCK(&users);
1080 ast_copy_string(s->username, username, sizeof(s->username));
1081 s->readperm = user->readperm;
1082 s->writeperm = user->writeperm;
1083 s->writetimeout = user->writetimeout;
1084 s->sessionstart = time(NULL);
1085 set_eventmask(s, astman_get_header(m, "Events"));
1087 AST_RWLIST_UNLOCK(&users);
1091 /*! \brief Manager PING */
1092 static char mandescr_ping[] =
1093 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1094 " manager connection open.\n"
1095 "Variables: NONE\n";
1097 static int action_ping(struct mansession *s, const struct message *m)
1099 astman_append(s, "Response: Success\r\n"
1104 static char mandescr_getconfig[] =
1105 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1106 "file by category and contents or optionally by specified category only.\n"
1107 "Variables: (Names marked with * are required)\n"
1108 " *Filename: Configuration filename (e.g. foo.conf)\n"
1109 " Category: Category in configuration file\n";
1111 static int action_getconfig(struct mansession *s, const struct message *m)
1113 struct ast_config *cfg;
1114 const char *fn = astman_get_header(m, "Filename");
1115 const char *category = astman_get_header(m, "Category");
1118 char *cur_category = NULL;
1119 struct ast_variable *v;
1120 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1122 if (ast_strlen_zero(fn)) {
1123 astman_send_error(s, m, "Filename not specified");
1126 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1127 astman_send_error(s, m, "Config file not found");
1131 astman_start_ack(s, m);
1132 while ((cur_category = ast_category_browse(cfg, cur_category))) {
1133 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
1135 astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
1136 for (v = ast_variable_browse(cfg, cur_category); v; v = v->next)
1137 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1141 if (!ast_strlen_zero(category) && catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1142 astman_append(s, "No categories found");
1143 ast_config_destroy(cfg);
1144 astman_append(s, "\r\n");
1149 static char mandescr_listcategories[] =
1150 "Description: A 'ListCategories' action will dump the categories in\n"
1153 " Filename: Configuration filename (e.g. foo.conf)\n";
1155 static int action_listcategories(struct mansession *s, const struct message *m)
1157 struct ast_config *cfg;
1158 const char *fn = astman_get_header(m, "Filename");
1159 char *category = NULL;
1160 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1163 if (ast_strlen_zero(fn)) {
1164 astman_send_error(s, m, "Filename not specified");
1167 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1168 astman_send_error(s, m, "Config file not found or file has invalid syntax");
1171 astman_start_ack(s, m);
1172 while ((category = ast_category_browse(cfg, category))) {
1173 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1176 if (catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1177 astman_append(s, "Error: no categories found");
1178 ast_config_destroy(cfg);
1179 astman_append(s, "\r\n");
1187 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1188 static void json_escape(char *out, const char *in)
1191 if (*in == '\\' || *in == '\"')
1198 static char mandescr_getconfigjson[] =
1199 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1200 "file by category and contents in JSON format. This only makes sense to be used\n"
1201 "using rawman over the HTTP interface.\n"
1203 " Filename: Configuration filename (e.g. foo.conf)\n";
1205 static int action_getconfigjson(struct mansession *s, const struct message *m)
1207 struct ast_config *cfg;
1208 const char *fn = astman_get_header(m, "Filename");
1209 char *category = NULL;
1210 struct ast_variable *v;
1213 unsigned int buf_len = 0;
1214 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1216 if (ast_strlen_zero(fn)) {
1217 astman_send_error(s, m, "Filename not specified");
1221 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1222 astman_send_error(s, m, "Config file not found");
1227 buf = alloca(buf_len);
1229 astman_start_ack(s, m);
1230 astman_append(s, "JSON: {");
1231 while ((category = ast_category_browse(cfg, category))) {
1233 if (buf_len < 2 * strlen(category) + 1) {
1235 buf = alloca(buf_len);
1237 json_escape(buf, category);
1238 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1241 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1243 astman_append(s, ",");
1244 if (buf_len < 2 * strlen(v->name) + 1) {
1246 buf = alloca(buf_len);
1248 json_escape(buf, v->name);
1249 astman_append(s, "\"%s", buf);
1250 if (buf_len < 2 * strlen(v->value) + 1) {
1252 buf = alloca(buf_len);
1254 json_escape(buf, v->value);
1255 astman_append(s, "%s\"", buf);
1259 astman_append(s, "]");
1261 astman_append(s, "}\r\n\r\n");
1263 ast_config_destroy(cfg);
1268 /* helper function for action_updateconfig */
1269 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1273 const char *action, *cat, *var, *value, *match, *line;
1274 struct ast_category *category;
1275 struct ast_variable *v;
1277 for (x = 0; x < 100000; x++) {
1278 unsigned int object = 0;
1280 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1281 action = astman_get_header(m, hdr);
1282 if (ast_strlen_zero(action))
1284 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1285 cat = astman_get_header(m, hdr);
1286 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1287 var = astman_get_header(m, hdr);
1288 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1289 value = astman_get_header(m, hdr);
1290 if (!ast_strlen_zero(value) && *value == '>') {
1294 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1295 match = astman_get_header(m, hdr);
1296 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
1297 line = astman_get_header(m, hdr);
1298 if (!strcasecmp(action, "newcat")) {
1299 if (ast_strlen_zero(cat))
1300 return UNSPECIFIED_CATEGORY;
1301 if (!(category = ast_category_new(cat, dfn, -1)))
1302 return FAILURE_ALLOCATION;
1303 if (ast_strlen_zero(match)) {
1304 ast_category_append(cfg, category);
1306 ast_category_insert(cfg, category, match);
1307 } else if (!strcasecmp(action, "renamecat")) {
1308 if (ast_strlen_zero(cat) || ast_strlen_zero(value))
1309 return UNSPECIFIED_ARGUMENT;
1310 if (!(category = ast_category_get(cfg, cat)))
1311 return UNKNOWN_CATEGORY;
1312 ast_category_rename(category, value);
1313 } else if (!strcasecmp(action, "delcat")) {
1314 if (ast_strlen_zero(cat))
1315 return UNSPECIFIED_CATEGORY;
1316 if (ast_category_delete(cfg, cat))
1317 return FAILURE_DELCAT;
1318 } else if (!strcasecmp(action, "emptycat")) {
1319 if (ast_strlen_zero(cat))
1320 return UNSPECIFIED_CATEGORY;
1321 if (ast_category_empty(cfg, cat))
1322 return FAILURE_EMPTYCAT;
1323 } else if (!strcasecmp(action, "update")) {
1324 if (ast_strlen_zero(cat) || ast_strlen_zero(var))
1325 return UNSPECIFIED_ARGUMENT;
1326 if (!(category = ast_category_get(cfg,cat)))
1327 return UNKNOWN_CATEGORY;
1328 if (ast_variable_update(category, var, value, match, object))
1329 return FAILURE_UPDATE;
1330 } else if (!strcasecmp(action, "delete")) {
1331 if (ast_strlen_zero(cat) || (ast_strlen_zero(var) && ast_strlen_zero(line)))
1332 return UNSPECIFIED_ARGUMENT;
1333 if (!(category = ast_category_get(cfg, cat)))
1334 return UNKNOWN_CATEGORY;
1335 if (ast_variable_delete(category, var, match, line))
1336 return FAILURE_DELETE;
1337 } else if (!strcasecmp(action, "append")) {
1338 if (ast_strlen_zero(cat) || ast_strlen_zero(var))
1339 return UNSPECIFIED_ARGUMENT;
1340 if (!(category = ast_category_get(cfg, cat)))
1341 return UNKNOWN_CATEGORY;
1342 if (!(v = ast_variable_new(var, value, dfn)))
1343 return FAILURE_ALLOCATION;
1344 if (object || (match && !strcasecmp(match, "object")))
1346 ast_variable_append(category, v);
1347 } else if (!strcasecmp(action, "insert")) {
1348 if (ast_strlen_zero(cat) || ast_strlen_zero(var) || ast_strlen_zero(line))
1349 return UNSPECIFIED_ARGUMENT;
1350 if (!(category = ast_category_get(cfg, cat)))
1351 return UNKNOWN_CATEGORY;
1352 if (!(v = ast_variable_new(var, value, dfn)))
1353 return FAILURE_ALLOCATION;
1354 ast_variable_insert(category, v, line);
1357 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
1358 return UNKNOWN_ACTION;
1364 static char mandescr_updateconfig[] =
1365 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1366 "configuration elements in Asterisk configuration files.\n"
1367 "Variables (X's represent 6 digit number beginning with 000000):\n"
1368 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1369 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1370 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1371 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,EmptyCat,Update,Delete,Append,Insert)\n"
1372 " Cat-XXXXXX: Category to operate on\n"
1373 " Var-XXXXXX: Variable to work on\n"
1374 " Value-XXXXXX: Value to work on\n"
1375 " Match-XXXXXX: Extra match required to match line\n"
1376 " Line-XXXXXX: Line in category to operate on (used with delete and insert actions)\n";
1378 static int action_updateconfig(struct mansession *s, const struct message *m)
1380 struct ast_config *cfg;
1381 const char *sfn = astman_get_header(m, "SrcFilename");
1382 const char *dfn = astman_get_header(m, "DstFilename");
1384 const char *rld = astman_get_header(m, "Reload");
1385 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1386 enum error_type result;
1388 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1389 astman_send_error(s, m, "Filename not specified");
1392 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
1393 astman_send_error(s, m, "Config file not found");
1396 result = handle_updates(s, m, cfg, dfn);
1398 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1399 res = config_text_file_save(dfn, cfg, "Manager");
1400 ast_config_destroy(cfg);
1402 astman_send_error(s, m, "Save of config failed");
1405 astman_send_ack(s, m, NULL);
1406 if (!ast_strlen_zero(rld)) {
1409 ast_module_reload(rld);
1412 ast_config_destroy(cfg);
1414 case UNKNOWN_ACTION:
1415 astman_send_error(s, m, "Unknown action command");
1417 case UNKNOWN_CATEGORY:
1418 astman_send_error(s, m, "Given category does not exist");
1420 case UNSPECIFIED_CATEGORY:
1421 astman_send_error(s, m, "Category not specified");
1423 case UNSPECIFIED_ARGUMENT:
1424 astman_send_error(s, m, "Problem with category, value, or line (if required)");
1426 case FAILURE_ALLOCATION:
1427 astman_send_error(s, m, "Memory allocation failure, this should not happen");
1429 case FAILURE_DELCAT:
1430 astman_send_error(s, m, "Delete category did not complete successfully");
1432 case FAILURE_EMPTYCAT:
1433 astman_send_error(s, m, "Empty category did not complete successfully");
1435 case FAILURE_UPDATE:
1436 astman_send_error(s, m, "Update did not complete successfully");
1438 case FAILURE_DELETE:
1439 astman_send_error(s, m, "Delete did not complete successfully");
1441 case FAILURE_APPEND:
1442 astman_send_error(s, m, "Append did not complete successfully");
1449 static char mandescr_createconfig[] =
1450 "Description: A 'CreateConfig' action will create an empty file in the\n"
1451 "configuration directory. This action is intended to be used before an\n"
1452 "UpdateConfig action.\n"
1454 " Filename: The configuration filename to create (e.g. foo.conf)\n";
1456 static int action_createconfig(struct mansession *s, const struct message *m)
1459 const char *fn = astman_get_header(m, "Filename");
1460 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
1461 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
1462 ast_str_append(&filepath, 0, "%s", fn);
1464 if ((fd = open(filepath->str, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
1466 astman_send_ack(s, m, "New configuration file created successfully");
1468 astman_send_error(s, m, strerror(errno));
1473 /*! \brief Manager WAITEVENT */
1474 static char mandescr_waitevent[] =
1475 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1476 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1477 "session, events will be generated and queued.\n"
1479 " Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1481 static int action_waitevent(struct mansession *s, const struct message *m)
1483 const char *timeouts = astman_get_header(m, "Timeout");
1487 const char *id = astman_get_header(m, "ActionID");
1490 if (!ast_strlen_zero(id))
1491 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1495 if (!ast_strlen_zero(timeouts)) {
1496 sscanf(timeouts, "%i", &timeout);
1499 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1502 ast_mutex_lock(&s->__lock);
1503 if (s->waiting_thread != AST_PTHREADT_NULL)
1504 pthread_kill(s->waiting_thread, SIGURG);
1506 if (s->managerid) { /* AMI-over-HTTP session */
1508 * Make sure the timeout is within the expire time of the session,
1509 * as the client will likely abort the request if it does not see
1510 * data coming after some amount of time.
1512 time_t now = time(NULL);
1513 int max = s->sessiontimeout - now - 10;
1515 if (max < 0) /* We are already late. Strange but possible. */
1517 if (timeout < 0 || timeout > max)
1519 if (!s->send_events) /* make sure we record events */
1520 s->send_events = -1;
1522 ast_mutex_unlock(&s->__lock);
1524 /* XXX should this go inside the lock ? */
1525 s->waiting_thread = pthread_self(); /* let new events wake up this thread */
1526 ast_debug(1, "Starting waiting for an event!\n");
1528 for (x = 0; x < timeout || timeout < 0; x++) {
1529 ast_mutex_lock(&s->__lock);
1532 /* We can have multiple HTTP session point to the same mansession entry.
1533 * The way we deal with it is not very nice: newcomers kick out the previous
1534 * HTTP session. XXX this needs to be improved.
1536 if (s->waiting_thread != pthread_self())
1540 ast_mutex_unlock(&s->__lock);
1543 if (s->managerid == 0) { /* AMI session */
1544 if (ast_wait_for_input(s->fd, 1000))
1546 } else { /* HTTP session */
1550 ast_debug(1, "Finished waiting for an event!\n");
1551 ast_mutex_lock(&s->__lock);
1552 if (s->waiting_thread == pthread_self()) {
1553 struct eventqent *eqe;
1554 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1555 while ( (eqe = NEW_EVENT(s)) ) {
1557 if (((s->readperm & eqe->category) == eqe->category) &&
1558 ((s->send_events & eqe->category) == eqe->category)) {
1559 astman_append(s, "%s", eqe->eventdata);
1561 s->last_ev = unref_event(s->last_ev);
1564 "Event: WaitEventComplete\r\n"
1567 s->waiting_thread = AST_PTHREADT_NULL;
1569 ast_debug(1, "Abandoning event request!\n");
1571 ast_mutex_unlock(&s->__lock);
1575 static char mandescr_listcommands[] =
1576 "Description: Returns the action name and synopsis for every\n"
1577 " action that is available to the user\n"
1578 "Variables: NONE\n";
1580 /*! \note The actionlock is read-locked by the caller of this function */
1581 static int action_listcommands(struct mansession *s, const struct message *m)
1583 struct manager_action *cur;
1584 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1586 astman_start_ack(s, m);
1587 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1588 if (s->writeperm & cur->authority || cur->authority == 0)
1589 astman_append(s, "%s: %s (Priv: %s)\r\n",
1590 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1592 astman_append(s, "\r\n");
1597 static char mandescr_events[] =
1598 "Description: Enable/Disable sending of events to this manager\n"
1601 " EventMask: 'on' if all events should be sent,\n"
1602 " 'off' if no events should be sent,\n"
1603 " 'system,call,log' to select which flags events should have to be sent.\n";
1605 static int action_events(struct mansession *s, const struct message *m)
1607 const char *mask = astman_get_header(m, "EventMask");
1610 res = set_eventmask(s, mask);
1612 astman_append(s, "Response: Success\r\n"
1615 astman_append(s, "Response: Success\r\n"
1620 static char mandescr_logoff[] =
1621 "Description: Logoff this manager session\n"
1622 "Variables: NONE\n";
1624 static int action_logoff(struct mansession *s, const struct message *m)
1626 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1630 static int action_login(struct mansession *s, const struct message *m)
1632 if (authenticate(s, m)) {
1634 astman_send_error(s, m, "Authentication failed");
1637 s->authenticated = 1;
1638 if (manager_displayconnects(s))
1639 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1640 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1641 astman_send_ack(s, m, "Authentication accepted");
1645 static int action_challenge(struct mansession *s, const struct message *m)
1647 const char *authtype = astman_get_header(m, "AuthType");
1649 if (!strcasecmp(authtype, "MD5")) {
1650 if (ast_strlen_zero(s->challenge))
1651 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1652 ast_mutex_lock(&s->__lock);
1653 astman_start_ack(s, m);
1654 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1655 ast_mutex_unlock(&s->__lock);
1657 astman_send_error(s, m, "Must specify AuthType");
1662 static char mandescr_hangup[] =
1663 "Description: Hangup a channel\n"
1665 " Channel: The channel name to be hungup\n";
1667 static int action_hangup(struct mansession *s, const struct message *m)
1669 struct ast_channel *c = NULL;
1670 const char *name = astman_get_header(m, "Channel");
1671 if (ast_strlen_zero(name)) {
1672 astman_send_error(s, m, "No channel specified");
1675 c = ast_get_channel_by_name_locked(name);
1677 astman_send_error(s, m, "No such channel");
1680 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1681 ast_channel_unlock(c);
1682 astman_send_ack(s, m, "Channel Hungup");
1686 static char mandescr_setvar[] =
1687 "Description: Set a global or local channel variable.\n"
1688 "Variables: (Names marked with * are required)\n"
1689 " Channel: Channel to set variable for\n"
1690 " *Variable: Variable name\n"
1693 static int action_setvar(struct mansession *s, const struct message *m)
1695 struct ast_channel *c = NULL;
1696 const char *name = astman_get_header(m, "Channel");
1697 const char *varname = astman_get_header(m, "Variable");
1698 const char *varval = astman_get_header(m, "Value");
1700 if (ast_strlen_zero(varname)) {
1701 astman_send_error(s, m, "No variable specified");
1705 if (!ast_strlen_zero(name)) {
1706 c = ast_get_channel_by_name_locked(name);
1708 astman_send_error(s, m, "No such channel");
1713 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1716 ast_channel_unlock(c);
1718 astman_send_ack(s, m, "Variable Set");
1723 static char mandescr_getvar[] =
1724 "Description: Get the value of a global or local channel variable.\n"
1725 "Variables: (Names marked with * are required)\n"
1726 " Channel: Channel to read variable from\n"
1727 " *Variable: Variable name\n"
1728 " ActionID: Optional Action id for message matching.\n";
1730 static int action_getvar(struct mansession *s, const struct message *m)
1732 struct ast_channel *c = NULL;
1733 const char *name = astman_get_header(m, "Channel");
1734 const char *varname = astman_get_header(m, "Variable");
1736 char workspace[1024] = "";
1738 if (ast_strlen_zero(varname)) {
1739 astman_send_error(s, m, "No variable specified");
1743 if (!ast_strlen_zero(name)) {
1744 c = ast_get_channel_by_name_locked(name);
1746 astman_send_error(s, m, "No such channel");
1751 if (varname[strlen(varname) - 1] == ')') {
1752 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1755 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1759 ast_channel_unlock(c);
1760 astman_start_ack(s, m);
1761 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1766 static char mandescr_status[] =
1767 "Description: Lists channel status along with requested channel vars.\n"
1768 "Variables: (Names marked with * are required)\n"
1769 " *Channel: Name of the channel to query for status\n"
1770 " Variables: Comma ',' separated list of variables to include\n"
1771 " ActionID: Optional ID for this transaction\n"
1772 "Will return the status information of each channel along with the\n"
1773 "value for the specified channel variables.\n";
1776 /*! \brief Manager "status" command to show channels */
1777 /* Needs documentation... */
1778 static int action_status(struct mansession *s, const struct message *m)
1780 const char *name = astman_get_header(m, "Channel");
1781 const char *cvariables = astman_get_header(m, "Variables");
1782 char *variables = ast_strdupa(S_OR(cvariables, ""));
1783 struct ast_channel *c;
1785 struct timeval now = ast_tvnow();
1786 long elapsed_seconds = 0;
1788 int all = ast_strlen_zero(name); /* set if we want all channels */
1789 const char *id = astman_get_header(m, "ActionID");
1791 AST_DECLARE_APP_ARGS(vars,
1792 AST_APP_ARG(name)[100];
1794 struct ast_str *str = ast_str_create(1000);
1796 if (!ast_strlen_zero(id))
1797 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1802 c = ast_channel_walk_locked(NULL);
1804 c = ast_get_channel_by_name_locked(name);
1806 astman_send_error(s, m, "No such channel");
1810 astman_send_ack(s, m, "Channel status will follow");
1812 if (!ast_strlen_zero(cvariables)) {
1813 AST_STANDARD_APP_ARGS(vars, variables);
1816 /* if we look by name, we break after the first iteration */
1818 if (!ast_strlen_zero(cvariables)) {
1821 for (i = 0; i < vars.argc; i++) {
1822 char valbuf[512], *ret = NULL;
1824 if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
1825 if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
1830 pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
1833 ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
1839 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1844 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1848 "Privilege: Call\r\n"
1850 "CallerIDNum: %s\r\n"
1851 "CallerIDName: %s\r\n"
1852 "Accountcode: %s\r\n"
1853 "ChannelState: %d\r\n"
1854 "ChannelStateDesc: %s\r\n"
1865 S_OR(c->cid.cid_num, ""),
1866 S_OR(c->cid.cid_name, ""),
1869 ast_state2str(c->_state), c->context,
1870 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, str->str, idText);
1874 "Privilege: Call\r\n"
1876 "CallerIDNum: %s\r\n"
1877 "CallerIDName: %s\r\n"
1886 S_OR(c->cid.cid_num, "<unknown>"),
1887 S_OR(c->cid.cid_name, "<unknown>"),
1889 ast_state2str(c->_state), bridge, c->uniqueid, str->str, idText);
1891 ast_channel_unlock(c);
1894 c = ast_channel_walk_locked(c);
1897 "Event: StatusComplete\r\n"
1900 "\r\n", idText, channels);
1905 static char mandescr_sendtext[] =
1906 "Description: Sends A Text Message while in a call.\n"
1907 "Variables: (Names marked with * are required)\n"
1908 " *Channel: Channel to send message to\n"
1909 " *Message: Message to send\n"
1910 " ActionID: Optional Action id for message matching.\n";
1912 static int action_sendtext(struct mansession *s, const struct message *m)
1914 struct ast_channel *c = NULL;
1915 const char *name = astman_get_header(m, "Channel");
1916 const char *textmsg = astman_get_header(m, "Message");
1919 if (ast_strlen_zero(name)) {
1920 astman_send_error(s, m, "No channel specified");
1924 if (ast_strlen_zero(textmsg)) {
1925 astman_send_error(s, m, "No Message specified");
1929 c = ast_get_channel_by_name_locked(name);
1931 astman_send_error(s, m, "No such channel");
1935 res = ast_sendtext(c, textmsg);
1936 ast_channel_unlock(c);
1939 astman_send_ack(s, m, "Success");
1941 astman_send_error(s, m, "Failure");
1946 static char mandescr_redirect[] =
1947 "Description: Redirect (transfer) a call.\n"
1948 "Variables: (Names marked with * are required)\n"
1949 " *Channel: Channel to redirect\n"
1950 " ExtraChannel: Second call leg to transfer (optional)\n"
1951 " *Exten: Extension to transfer to\n"
1952 " *Context: Context to transfer to\n"
1953 " *Priority: Priority to transfer to\n"
1954 " ActionID: Optional Action id for message matching.\n";
1956 /*! \brief action_redirect: The redirect manager command */
1957 static int action_redirect(struct mansession *s, const struct message *m)
1959 const char *name = astman_get_header(m, "Channel");
1960 const char *name2 = astman_get_header(m, "ExtraChannel");
1961 const char *exten = astman_get_header(m, "Exten");
1962 const char *context = astman_get_header(m, "Context");
1963 const char *priority = astman_get_header(m, "Priority");
1964 struct ast_channel *chan, *chan2 = NULL;
1968 if (ast_strlen_zero(name)) {
1969 astman_send_error(s, m, "Channel not specified");
1972 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1973 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1974 astman_send_error(s, m, "Invalid priority\n");
1978 /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1979 chan = ast_get_channel_by_name_locked(name);
1982 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1983 astman_send_error(s, m, buf);
1986 if (ast_check_hangup(chan)) {
1987 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1988 ast_channel_unlock(chan);
1991 if (!ast_strlen_zero(name2))
1992 chan2 = ast_get_channel_by_name_locked(name2);
1993 if (chan2 && ast_check_hangup(chan2)) {
1994 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1995 ast_channel_unlock(chan);
1996 ast_channel_unlock(chan2);
1999 res = ast_async_goto(chan, context, exten, pi);
2001 if (!ast_strlen_zero(name2)) {
2003 res = ast_async_goto(chan2, context, exten, pi);
2007 astman_send_ack(s, m, "Dual Redirect successful");
2009 astman_send_error(s, m, "Secondary redirect failed");
2011 astman_send_ack(s, m, "Redirect successful");
2013 astman_send_error(s, m, "Redirect failed");
2015 ast_channel_unlock(chan);
2017 ast_channel_unlock(chan2);
2021 static char mandescr_atxfer[] =
2022 "Description: Attended transfer.\n"
2023 "Variables: (Names marked with * are required)\n"
2024 " *Channel: Transferer's channel\n"
2025 " *Exten: Extension to transfer to\n"
2026 " *Context: Context to transfer to\n"
2027 " *Priority: Priority to transfer to\n"
2028 " ActionID: Optional Action id for message matching.\n";
2030 static int action_atxfer(struct mansession *s, const struct message *m)
2032 const char *name = astman_get_header(m, "Channel");
2033 const char *exten = astman_get_header(m, "Exten");
2034 const char *context = astman_get_header(m, "Context");
2035 const char *priority = astman_get_header(m, "Priority");
2036 struct ast_channel *chan = NULL;
2037 struct ast_call_feature *atxfer_feature = NULL;
2038 char *feature_code = NULL;
2039 int priority_int = 0;
2041 if (ast_strlen_zero(name)) {
2042 astman_send_error(s, m, "No channel specified\n");
2045 if (ast_strlen_zero(exten)) {
2046 astman_send_error(s, m, "No extension specified\n");
2049 if (ast_strlen_zero(context)) {
2050 astman_send_error(s, m, "No context specified\n");
2053 if (ast_strlen_zero(priority)) {
2054 astman_send_error(s, m, "No priority specified\n");
2058 if (sscanf(priority, "%d", &priority_int) != 1 && (priority_int = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2059 astman_send_error(s, m, "Invalid Priority\n");
2063 if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
2064 astman_send_error(s, m, "No attended transfer feature found\n");
2068 if (!(chan = ast_get_channel_by_name_locked(name))) {
2069 astman_send_error(s, m, "Channel specified does not exist\n");
2073 for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
2074 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2075 ast_queue_frame(chan, &f);
2078 for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
2079 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2080 ast_queue_frame(chan, &f);
2083 astman_send_ack(s, m, "Atxfer successfully queued\n");
2084 ast_channel_unlock(chan);
2089 static int check_blacklist(const char *cmd)
2091 char *cmd_copy, *cur_cmd;
2092 char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
2095 cmd_copy = ast_strdupa(cmd);
2096 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
2097 cur_cmd = ast_strip(cur_cmd);
2098 if (ast_strlen_zero(cur_cmd)) {
2103 cmd_words[i] = cur_cmd;
2106 for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
2109 for (j = 0; command_blacklist[i].words[j]; j++) {
2110 if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
2124 static char mandescr_command[] =
2125 "Description: Run a CLI command.\n"
2126 "Variables: (Names marked with * are required)\n"
2127 " *Command: Asterisk CLI command to run\n"
2128 " ActionID: Optional Action id for message matching.\n";
2130 /*! \brief Manager command "command" - execute CLI command */
2131 static int action_command(struct mansession *s, const struct message *m)
2133 const char *cmd = astman_get_header(m, "Command");
2134 const char *id = astman_get_header(m, "ActionID");
2135 char *buf, *final_buf;
2136 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
2137 int fd = mkstemp(template);
2140 if (ast_strlen_zero(cmd)) {
2141 astman_send_error(s, m, "No command provided");
2145 if (check_blacklist(cmd)) {
2146 astman_send_error(s, m, "Command blacklisted");
2150 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
2151 if (!ast_strlen_zero(id))
2152 astman_append(s, "ActionID: %s\r\n", id);
2153 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
2154 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
2155 l = lseek(fd, 0, SEEK_END); /* how many chars available */
2157 /* This has a potential to overflow the stack. Hence, use the heap. */
2158 buf = ast_calloc(1, l + 1);
2159 final_buf = ast_calloc(1, l + 1);
2161 lseek(fd, 0, SEEK_SET);
2165 term_strip(final_buf, buf, l);
2166 final_buf[l] = '\0';
2168 astman_append(s, "%s", S_OR(final_buf, buf));
2173 astman_append(s, "--END COMMAND--\r\n\r\n");
2175 ast_free(final_buf);
2179 /*! \brief helper function for originate */
2180 struct fast_originate_helper {
2181 char tech[AST_MAX_EXTENSION];
2182 char data[AST_MAX_EXTENSION];
2184 int format; /*!< Codecs used for a call */
2185 char app[AST_MAX_APP];
2186 char appdata[AST_MAX_EXTENSION];
2187 char cid_name[AST_MAX_EXTENSION];
2188 char cid_num[AST_MAX_EXTENSION];
2189 char context[AST_MAX_CONTEXT];
2190 char exten[AST_MAX_EXTENSION];
2191 char idtext[AST_MAX_EXTENSION];
2192 char account[AST_MAX_ACCOUNT_CODE];
2194 struct ast_variable *vars;
2197 static void *fast_originate(void *data)
2199 struct fast_originate_helper *in = data;
2202 struct ast_channel *chan = NULL;
2203 char requested_channel[AST_CHANNEL_NAME];
2205 if (!ast_strlen_zero(in->app)) {
2206 res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
2207 S_OR(in->cid_num, NULL),
2208 S_OR(in->cid_name, NULL),
2209 in->vars, in->account, &chan);
2211 res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
2212 S_OR(in->cid_num, NULL),
2213 S_OR(in->cid_name, NULL),
2214 in->vars, in->account, &chan);
2218 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
2219 /* Tell the manager what happened with the channel */
2220 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
2228 "CallerIDNum: %s\r\n"
2229 "CallerIDName: %s\r\n",
2230 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
2231 chan ? chan->uniqueid : "<null>",
2232 S_OR(in->cid_num, "<unknown>"),
2233 S_OR(in->cid_name, "<unknown>")
2236 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2238 ast_channel_unlock(chan);
2243 static char mandescr_originate[] =
2244 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
2245 " Application/Data\n"
2246 "Variables: (Names marked with * are required)\n"
2247 " *Channel: Channel name to call\n"
2248 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
2249 " Context: Context to use (requires 'Exten' and 'Priority')\n"
2250 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
2251 " Application: Application to use\n"
2252 " Data: Data to use (requires 'Application')\n"
2253 " Timeout: How long to wait for call to be answered (in ms)\n"
2254 " CallerID: Caller ID to be set on the outgoing channel\n"
2255 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
2256 " Account: Account code\n"
2257 " Async: Set to 'true' for fast origination\n";
2259 static int action_originate(struct mansession *s, const struct message *m)
2261 const char *name = astman_get_header(m, "Channel");
2262 const char *exten = astman_get_header(m, "Exten");
2263 const char *context = astman_get_header(m, "Context");
2264 const char *priority = astman_get_header(m, "Priority");
2265 const char *timeout = astman_get_header(m, "Timeout");
2266 const char *callerid = astman_get_header(m, "CallerID");
2267 const char *account = astman_get_header(m, "Account");
2268 const char *app = astman_get_header(m, "Application");
2269 const char *appdata = astman_get_header(m, "Data");
2270 const char *async = astman_get_header(m, "Async");
2271 const char *id = astman_get_header(m, "ActionID");
2272 const char *codecs = astman_get_header(m, "Codecs");
2273 struct ast_variable *vars = astman_get_variables(m);
2275 char *l = NULL, *n = NULL;
2282 int format = AST_FORMAT_SLINEAR;
2286 astman_send_error(s, m, "Channel not specified");
2289 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2290 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2291 astman_send_error(s, m, "Invalid priority\n");
2295 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2296 astman_send_error(s, m, "Invalid timeout\n");
2299 ast_copy_string(tmp, name, sizeof(tmp));
2301 data = strchr(tmp, '/');
2303 astman_send_error(s, m, "Invalid channel\n");
2307 ast_copy_string(tmp2, callerid, sizeof(tmp2));
2308 ast_callerid_parse(tmp2, &n, &l);
2310 if (ast_strlen_zero(n))
2314 ast_shrink_phone_number(l);
2315 if (ast_strlen_zero(l))
2318 if (!ast_strlen_zero(codecs)) {
2320 ast_parse_allow_disallow(NULL, &format, codecs, 1);
2322 if (ast_true(async)) {
2323 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2327 if (!ast_strlen_zero(id))
2328 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2329 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2330 ast_copy_string(fast->data, data, sizeof(fast->data));
2331 ast_copy_string(fast->app, app, sizeof(fast->app));
2332 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2334 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2336 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2338 ast_copy_string(fast->context, context, sizeof(fast->context));
2339 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2340 ast_copy_string(fast->account, account, sizeof(fast->account));
2341 fast->format = format;
2343 fast->priority = pi;
2344 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2350 } else if (!ast_strlen_zero(app)) {
2351 /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
2352 if (!(s->writeperm & EVENT_FLAG_SYSTEM)
2354 strcasestr(app, "system") == 0 || /* System(rm -rf /)
2355 TrySystem(rm -rf /) */
2356 strcasestr(app, "exec") || /* Exec(System(rm -rf /))
2357 TryExec(System(rm -rf /)) */
2358 strcasestr(app, "agi") || /* AGI(/bin/rm,-rf /)
2359 EAGI(/bin/rm,-rf /) */
2360 strstr(appdata, "SHELL") || /* NoOp(${SHELL(rm -rf /)}) */
2361 strstr(appdata, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
2363 astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
2366 res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2368 if (exten && context && pi)
2369 res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2371 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2376 astman_send_ack(s, m, "Originate successfully queued");
2378 astman_send_error(s, m, "Originate failed");
2382 /*! \brief Help text for manager command mailboxstatus
2384 static char mandescr_mailboxstatus[] =
2385 "Description: Checks a voicemail account for status.\n"
2386 "Variables: (Names marked with * are required)\n"
2387 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2388 " ActionID: Optional ActionID for message matching.\n"
2389 "Returns number of messages.\n"
2390 " Message: Mailbox Status\n"
2391 " Mailbox: <mailboxid>\n"
2392 " Waiting: <count>\n"
2395 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2397 const char *mailbox = astman_get_header(m, "Mailbox");
2400 if (ast_strlen_zero(mailbox)) {
2401 astman_send_error(s, m, "Mailbox not specified");
2404 ret = ast_app_has_voicemail(mailbox, NULL);
2405 astman_start_ack(s, m);
2406 astman_append(s, "Message: Mailbox Status\r\n"
2408 "Waiting: %d\r\n\r\n", mailbox, ret);
2412 static char mandescr_mailboxcount[] =
2413 "Description: Checks a voicemail account for new messages.\n"
2414 "Variables: (Names marked with * are required)\n"
2415 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2416 " ActionID: Optional ActionID for message matching.\n"
2417 "Returns number of urgent, new and old messages.\n"
2418 " Message: Mailbox Message Count\n"
2419 " Mailbox: <mailboxid>\n"
2420 " UrgentMessages: <count>\n"
2421 " NewMessages: <count>\n"
2422 " OldMessages: <count>\n"
2424 static int action_mailboxcount(struct mansession *s, const struct message *m)
2426 const char *mailbox = astman_get_header(m, "Mailbox");
2427 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
2429 if (ast_strlen_zero(mailbox)) {
2430 astman_send_error(s, m, "Mailbox not specified");
2433 ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
2434 astman_start_ack(s, m);
2435 astman_append(s, "Message: Mailbox Message Count\r\n"
2437 "UrgMessages: %d\r\n"
2438 "NewMessages: %d\r\n"
2439 "OldMessages: %d\r\n"
2441 mailbox, urgentmsgs, newmsgs, oldmsgs);
2445 static char mandescr_extensionstate[] =
2446 "Description: Report the extension state for given extension.\n"
2447 " If the extension has a hint, will use devicestate to check\n"
2448 " the status of the device connected to the extension.\n"
2449 "Variables: (Names marked with * are required)\n"
2450 " *Exten: Extension to check state on\n"
2451 " *Context: Context for extension\n"
2452 " ActionId: Optional ID for this transaction\n"
2453 "Will return an \"Extension Status\" message.\n"
2454 "The response will include the hint for the extension and the status.\n";
2456 static int action_extensionstate(struct mansession *s, const struct message *m)
2458 const char *exten = astman_get_header(m, "Exten");
2459 const char *context = astman_get_header(m, "Context");
2460 char hint[256] = "";
2462 if (ast_strlen_zero(exten)) {
2463 astman_send_error(s, m, "Extension not specified");
2466 if (ast_strlen_zero(context))
2467 context = "default";
2468 status = ast_extension_state(NULL, context, exten);
2469 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2470 astman_start_ack(s, m);
2471 astman_append(s, "Message: Extension Status\r\n"
2475 "Status: %d\r\n\r\n",
2476 exten, context, hint, status);
2480 static char mandescr_timeout[] =
2481 "Description: Hangup a channel after a certain time.\n"
2482 "Variables: (Names marked with * are required)\n"
2483 " *Channel: Channel name to hangup\n"
2484 " *Timeout: Maximum duration of the call (sec)\n"
2485 "Acknowledges set time with 'Timeout Set' message\n";
2487 static int action_timeout(struct mansession *s, const struct message *m)
2489 struct ast_channel *c;
2490 const char *name = astman_get_header(m, "Channel");
2491 double timeout = atof(astman_get_header(m, "Timeout"));
2492 struct timeval tv = { timeout, 0 };
2494 if (ast_strlen_zero(name)) {
2495 astman_send_error(s, m, "No channel specified");
2498 if (!timeout || timeout < 0) {
2499 astman_send_error(s, m, "No timeout specified");
2502 c = ast_get_channel_by_name_locked(name);
2504 astman_send_error(s, m, "No such channel");
2508 tv.tv_usec = (timeout - tv.tv_sec) * 1000000.0;
2509 ast_channel_setwhentohangup_tv(c, tv);
2510 ast_channel_unlock(c);
2511 astman_send_ack(s, m, "Timeout Set");
2516 * Send any applicable events to the client listening on this socket.
2517 * Wait only for a finite time on each event, and drop all events whether
2518 * they are successfully sent or not.
2520 static int process_events(struct mansession *s)
2524 ast_mutex_lock(&s->__lock);
2526 struct eventqent *eqe;
2528 while ( (eqe = NEW_EVENT(s)) ) {
2530 if (!ret && s->authenticated &&
2531 (s->readperm & eqe->category) == eqe->category &&
2532 (s->send_events & eqe->category) == eqe->category) {
2533 if (send_string(s, eqe->eventdata) < 0)
2534 ret = -1; /* don't send more */
2536 s->last_ev = unref_event(s->last_ev);
2539 ast_mutex_unlock(&s->__lock);
2543 static char mandescr_userevent[] =
2544 "Description: Send an event to manager sessions.\n"
2545 "Variables: (Names marked with * are required)\n"
2546 " *UserEvent: EventStringToSend\n"
2547 " Header1: Content1\n"
2548 " HeaderN: ContentN\n";
2550 static int action_userevent(struct mansession *s, const struct message *m)
2552 const char *event = astman_get_header(m, "UserEvent");
2553 char body[2048] = "";
2555 for (x = 0; x < m->hdrcount; x++) {
2556 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2557 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2558 bodylen += strlen(m->headers[x]);
2559 ast_copy_string(body + bodylen, "\r\n", 3);
2564 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2568 static char mandescr_coresettings[] =
2569 "Description: Query for Core PBX settings.\n"
2570 "Variables: (Names marked with * are optional)\n"
2571 " *ActionID: ActionID of this transaction\n";
2573 /*! \brief Show PBX core settings information */
2574 static int action_coresettings(struct mansession *s, const struct message *m)
2576 const char *actionid = astman_get_header(m, "ActionID");
2579 if (!ast_strlen_zero(actionid))
2580 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2584 astman_append(s, "Response: Success\r\n"
2586 "AMIversion: %s\r\n"
2587 "AsteriskVersion: %s\r\n"
2588 "SystemName: %s\r\n"
2589 "CoreMaxCalls: %d\r\n"
2590 "CoreMaxLoadAvg: %f\r\n"
2591 "CoreRunUser: %s\r\n"
2592 "CoreRunGroup: %s\r\n"
2593 "CoreMaxFilehandles: %d\r\n"
2594 "CoreRealTimeEnabled: %s\r\n"
2595 "CoreCDRenabled: %s\r\n"
2596 "CoreHTTPenabled: %s\r\n"
2601 ast_config_AST_SYSTEM_NAME,
2604 ast_config_AST_RUN_USER,
2605 ast_config_AST_RUN_GROUP,
2607 ast_realtime_enabled() ? "Yes" : "No",
2608 check_cdr_enabled() ? "Yes" : "No",
2609 check_webmanager_enabled() ? "Yes" : "No"
2614 static char mandescr_corestatus[] =
2615 "Description: Query for Core PBX status.\n"
2616 "Variables: (Names marked with * are optional)\n"
2617 " *ActionID: ActionID of this transaction\n";
2619 /*! \brief Show PBX core status information */
2620 static int action_corestatus(struct mansession *s, const struct message *m)
2622 const char *actionid = astman_get_header(m, "ActionID");
2624 char startuptime[150];
2625 char reloadtime[150];
2628 if (!ast_strlen_zero(actionid))
2629 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2633 ast_localtime(&ast_startuptime, &tm, NULL);
2634 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2635 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2636 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2638 astman_append(s, "Response: Success\r\n"
2640 "CoreStartupTime: %s\r\n"
2641 "CoreReloadTime: %s\r\n"
2642 "CoreCurrentCalls: %d\r\n"
2647 ast_active_channels()
2652 static char mandescr_reload[] =
2653 "Description: Send a reload event.\n"
2654 "Variables: (Names marked with * are optional)\n"
2655 " *ActionID: ActionID of this transaction\n"
2656 " *Module: Name of the module to reload\n";
2658 /*! \brief Send a reload event */
2659 static int action_reload(struct mansession *s, const struct message *m)
2661 const char *module = astman_get_header(m, "Module");
2662 int res = ast_module_reload(S_OR(module, NULL));
2665 astman_send_ack(s, m, "Module Reloaded");
2667 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2671 static char mandescr_coreshowchannels[] =
2672 "Description: List currently defined channels and some information\n"
2675 " ActionID: Optional Action id for message matching.\n";
2677 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2678 * and some information about them. */
2679 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2681 const char *actionid = astman_get_header(m, "ActionID");
2682 char actionidtext[256];
2683 struct ast_channel *c = NULL;
2685 int duration, durh, durm, durs;
2687 if (!ast_strlen_zero(actionid))
2688 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2690 actionidtext[0] = '\0';
2692 astman_send_listack(s, m, "Channels will follow", "start");
2694 while ((c = ast_channel_walk_locked(c)) != NULL) {
2695 struct ast_channel *bc = ast_bridged_channel(c);
2696 char durbuf[10] = "";
2698 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2699 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2700 durh = duration / 3600;
2701 durm = (duration % 3600) / 60;
2702 durs = duration % 60;
2703 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2712 "ChannelState: %d\r\n"
2713 "ChannelStateDesc: %s\r\n"
2714 "Application: %s\r\n"
2715 "ApplicationData: %s\r\n"
2716 "CallerIDnum: %s\r\n"
2718 "AccountCode: %s\r\n"
2719 "BridgedChannel: %s\r\n"
2720 "BridgedUniqueID: %s\r\n"
2723 c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2724 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2725 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "",
2728 ast_channel_unlock(c);
2733 "Event: CoreShowChannelsComplete\r\n"
2734 "EventList: Complete\r\n"
2737 "\r\n", numchans, actionidtext);
2742 static char mandescr_modulecheck[] =
2743 "Description: Checks if Asterisk module is loaded\n"
2745 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2746 " Module: <name> Asterisk module name (not including extension)\n"
2748 "Will return Success/Failure\n"
2749 "For success returns, the module revision number is included.\n";
2751 /* Manager function to check if module is loaded */
2752 static int manager_modulecheck(struct mansession *s, const struct message *m)
2755 const char *module = astman_get_header(m, "Module");
2756 const char *id = astman_get_header(m, "ActionID");
2758 #if !defined(LOW_MEMORY)
2759 const char *version;
2761 char filename[PATH_MAX];
2764 ast_copy_string(filename, module, sizeof(filename));
2765 if ((cut = strchr(filename, '.'))) {
2768 cut = filename + strlen(filename);
2770 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
2771 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2772 res = ast_module_check(filename);
2774 astman_send_error(s, m, "Module not loaded");
2777 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
2778 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2779 #if !defined(LOW_MEMORY)
2780 version = ast_file_version_find(filename);
2783 if (!ast_strlen_zero(id))
2784 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2787 astman_append(s, "Response: Success\r\n%s", idText);
2788 #if !defined(LOW_MEMORY)
2789 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2794 static char mandescr_moduleload[] =
2795 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2797 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2798 " Module: <name> Asterisk module name (including .so extension)\n"
2799 " or subsystem identifier:\n"
2800 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2801 " LoadType: load | unload | reload\n"
2802 " The operation to be done on module\n"
2803 " If no module is specified for a reload loadtype, all modules are reloaded";
2805 static int manager_moduleload(struct mansession *s, const struct message *m)
2808 const char *module = astman_get_header(m, "Module");
2809 const char *loadtype = astman_get_header(m, "LoadType");
2811 if (!loadtype || strlen(loadtype) == 0)
2812 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2813 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2814 astman_send_error(s, m, "Need module name");
2816 if (!strcasecmp(loadtype, "load")) {
2817 res = ast_load_resource(module);
2819 astman_send_error(s, m, "Could not load module.");
2821 astman_send_ack(s, m, "Module loaded.");
2822 } else if (!strcasecmp(loadtype, "unload")) {
2823 res = ast_unload_resource(module, AST_FORCE_SOFT);
2825 astman_send_error(s, m, "Could not unload module.");
2827 astman_send_ack(s, m, "Module unloaded.");
2828 } else if (!strcasecmp(loadtype, "reload")) {
2829 if (module != NULL) {
2830 res = ast_module_reload(module);
2832 astman_send_error(s, m, "No such module.");
2834 astman_send_error(s, m, "Module does not support reload action.");
2836 astman_send_ack(s, m, "Module reloaded.");
2838 ast_module_reload(NULL); /* Reload all modules */
2839 astman_send_ack(s, m, "All modules reloaded");
2842 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2847 * Done with the action handlers here, we start with the code in charge
2848 * of accepting connections and serving them.
2849 * accept_thread() forks a new thread for each connection, session_do(),
2850 * which in turn calls get_input() repeatedly until a full message has
2851 * been accumulated, and then invokes process_message() to pass it to
2852 * the appropriate handler.
2856 * Process an AMI message, performing desired action.
2857 * Return 0 on success, -1 on error that require the session to be destroyed.
2859 static int process_message(struct mansession *s, const struct message *m)
2861 char action[80] = "";
2863 struct manager_action *tmp;
2864 const char *user = astman_get_header(m, "Username");
2866 ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
2867 ast_debug(1, "Manager received command '%s'\n", action);
2869 if (ast_strlen_zero(action)) {
2870 ast_mutex_lock(&s->__lock);
2871 astman_send_error(s, m, "Missing action in request");
2872 ast_mutex_unlock(&s->__lock);
2876 if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2877 ast_mutex_lock(&s->__lock);
2878 astman_send_error(s, m, "Permission denied");
2879 ast_mutex_unlock(&s->__lock);
2883 if (!allowmultiplelogin && !s->authenticated && user &&
2884 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2885 if (check_manager_session_inuse(user)) {
2887 ast_mutex_lock(&s->__lock);
2888 astman_send_error(s, m, "Login Already In Use");
2889 ast_mutex_unlock(&s->__lock);
2894 AST_RWLIST_RDLOCK(&actions);
2895 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2896 if (strcasecmp(action, tmp->action))
2898 if (s->writeperm & tmp->authority || tmp->authority == 0)
2899 ret = tmp->func(s, m);
2901 astman_send_error(s, m, "Permission denied");
2904 AST_RWLIST_UNLOCK(&actions);
2908 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2909 ast_mutex_lock(&s->__lock);
2910 astman_send_error(s, m, buf);
2911 ast_mutex_unlock(&s->__lock);
2915 /* Once done with our message, deliver any pending events */
2916 return process_events(s);
2920 * Read one full line (including crlf) from the manager socket.
2922 * \r\n is the only valid terminator for the line.
2923 * (Note that, later, '\0' will be considered as the end-of-line marker,
2924 * so everything between the '\0' and the '\r\n' will not be used).
2925 * Also note that we assume output to have at least "maxlen" space.
2928 static int get_input(struct mansession *s, char *output)
2931 int maxlen = sizeof(s->inbuf) - 1;
2932 char *src = s->inbuf;
2935 * Look for \r\n within the buffer. If found, copy to the output
2936 * buffer and return, trimming the \r\n (not used afterwards).
2938 for (x = 0; x < s->inlen; x++) {
2939 int cr; /* set if we have \r */
2940 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2941 cr = 2; /* Found. Update length to include \r\n */
2942 else if (src[x] == '\n')
2943 cr = 1; /* also accept \n only */
2946 memmove(output, src, x); /*... but trim \r\n */
2947 output[x] = '\0'; /* terminate the string */
2948 x += cr; /* number of bytes used */
2949 s->inlen -= x; /* remaining size */
2950 memmove(src, src + x, s->inlen); /* remove used bytes */
2953 if (s->inlen >= maxlen) {
2954 /* no crlf found, and buffer full - sorry, too long for us */
2955 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2960 /* XXX do we really need this locking ? */
2961 ast_mutex_lock(&s->__lock);
2962 if (s->pending_event) {
2963 s->pending_event = 0;
2964 ast_mutex_unlock(&s->__lock);
2967 s->waiting_thread = pthread_self();
2968 ast_mutex_unlock(&s->__lock);
2970 res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
2972 ast_mutex_lock(&s->__lock);
2973 s->waiting_thread = AST_PTHREADT_NULL;
2974 ast_mutex_unlock(&s->__lock);
2977 /* If we get a signal from some other thread (typically because
2978 * there are new events queued), return 0 to notify the caller.
2980 if (errno == EINTR || errno == EAGAIN)
2982 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2985 ast_mutex_lock(&s->__lock);
2986 res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2988 res = -1; /* error return */
2991 src[s->inlen] = '\0';
2994 ast_mutex_unlock(&s->__lock);
2998 static int do_message(struct mansession *s)
3000 struct message m = { 0 };
3001 char header_buf[sizeof(s->inbuf)] = { '\0' };
3005 /* Check if any events are pending and do them if needed */
3006 if (process_events(s))
3008 res = get_input(s, header_buf);
3011 } else if (res > 0) {
3012 if (ast_strlen_zero(header_buf))
3013 return process_message(s, &m) ? -1 : 0;
3014 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
3015 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
3022 /*! \brief The body of the individual manager session.
3023 * Call get_input() to read one line at a time
3024 * (or be woken up on new events), collect the lines in a
3025 * message until found an empty line, and execute the request.
3026 * In any case, deliver events asynchronously through process_events()
3027 * (called from here if no line is available, or at the end of
3028 * process_message(). )
3030 static void *session_do(void *data)
3032 struct ast_tcptls_session_instance *ser = data;
3033 struct mansession *s = ast_calloc(1, sizeof(*s));
3040 s->writetimeout = 100;
3041 s->waiting_thread = AST_PTHREADT_NULL;
3043 flags = fcntl(ser->fd, F_GETFL);
3044 if (!block_sockets) /* make sure socket is non-blocking */
3045 flags |= O_NONBLOCK;
3047 flags &= ~O_NONBLOCK;
3048 fcntl(ser->fd, F_SETFL, flags);
3050 ast_mutex_init(&s->__lock);
3051 s->send_events = -1;
3052 /* these fields duplicate those in the 'ser' structure */
3055 s->sin = ser->requestor;
3057 AST_LIST_LOCK(&sessions);
3058 AST_LIST_INSERT_HEAD(&sessions, s, list);
3059 ast_atomic_fetchadd_int(&num_sessions, 1);
3060 AST_LIST_UNLOCK(&sessions);
3061 /* Hook to the tail of the event queue */
3062 s->last_ev = grab_last();
3064 astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
3066 if ((res = do_message(s)) < 0)
3069 /* session is over, explain why and terminate */
3070 if (s->authenticated) {
3071 if (manager_displayconnects(s))
3072 ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3073 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3075 if (displayconnects)
3076 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
3077 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
3080 /* It is possible under certain circumstances for this session thread
3081 to complete its work and exit *before* the thread that created it
3082 has finished executing the ast_pthread_create_background() function.
3083 If this occurs, some versions of glibc appear to act in a buggy
3084 fashion and attempt to write data into memory that it thinks belongs
3085 to the thread but is in fact not owned by the thread (or may have
3086 been freed completely).
3088 Causing this thread to yield to other threads at least one time
3089 appears to work around this bug.
3101 /*! \brief remove at most n_max stale session from the list. */
3102 static void purge_sessions(int n_max)
3104 struct mansession *s;
3105 time_t now = time(NULL);
3107 AST_LIST_LOCK(&sessions);
3108 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
3109 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
3110 AST_LIST_REMOVE_CURRENT(list);
3111 ast_atomic_fetchadd_int(&num_sessions, -1);
3112 if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
3113 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
3114 s->username, ast_inet_ntoa(s->sin.sin_addr));
3116 free_session(s); /* XXX outside ? */
3121 AST_LIST_TRAVERSE_SAFE_END;
3122 AST_LIST_UNLOCK(&sessions);
3126 * events are appended to a queue from where they
3127 * can be dispatched to clients.
3129 static int append_event(const char *str, int category)
3131 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
3132 static int seq; /* sequence number */
3137 /* need to init all fields, because ast_malloc() does not */
3139 tmp->category = category;
3140 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
3141 AST_LIST_NEXT(tmp, eq_next) = NULL;
3142 strcpy(tmp->eventdata, str);
3144 AST_LIST_LOCK(&all_events);
3145 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
3146 AST_LIST_UNLOCK(&all_events);
3151 /* XXX see if can be moved inside the function */
3152 AST_THREADSTORAGE(manager_event_buf);
3153 #define MANAGER_EVENT_BUF_INITSIZE 256
3155 /*! \brief manager_event: Send AMI event to client */
3156 int __manager_event(int category, const char *event,
3157 const char *file, int line, const char *func, const char *fmt, ...)
3159 struct mansession *s;
3160 struct manager_custom_hook *hook;
3161 struct ast_str *auth = ast_str_alloca(80);
3162 const char *cat_str;
3165 struct ast_str *buf;
3167 /* Abort if there aren't any manager sessions */
3171 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
3174 cat_str = authority_to_str(category, &auth);
3175 ast_str_set(&buf, 0,
3176 "Event: %s\r\nPrivilege: %s\r\n",
3179 if (timestampevents) {
3181 ast_str_append(&buf, 0,
3182 "Timestamp: %ld.%06lu\r\n",
3183 (long)now.tv_sec, (unsigned long) now.tv_usec);
3185 if (manager_debug) {
3187 ast_str_append(&buf, 0,
3188 "SequenceNumber: %d\r\n",
3189 ast_atomic_fetchadd_int(&seq, 1));
3190 ast_str_append(&buf, 0,
3191 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
3195 ast_str_append_va(&buf, 0, fmt, ap);
3198 ast_str_append(&buf, 0, "\r\n");
3200 append_event(buf->str, category);
3202 /* Wake up any sleeping sessions */
3203 AST_LIST_LOCK(&sessions);
3204 AST_LIST_TRAVERSE(&sessions, s, list) {
3205 ast_mutex_lock(&s->__lock);
3206 if (s->waiting_thread != AST_PTHREADT_NULL)
3207 pthread_kill(s->waiting_thread, SIGURG);
3209 /* We have an event to process, but the mansession is
3210 * not waiting for it. We still need to indicate that there
3211 * is an event waiting so that get_input processes the pending
3212 * event instead of polling.
3214 s->pending_event = 1;
3215 ast_mutex_unlock(&s->__lock);
3217 AST_LIST_UNLOCK(&sessions);
3219 AST_RWLIST_RDLOCK(&manager_hooks);
3220 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
3221 hook->helper(category, event, buf->str);
3223 AST_RWLIST_UNLOCK(&manager_hooks);
3229 * support functions to register/unregister AMI action handlers,
3231 int ast_manager_unregister(char *action)
3233 struct manager_action *cur;
3235 AST_RWLIST_WRLOCK(&actions);
3236 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
3237 if (!strcasecmp(action, cur->action)) {
3238 AST_RWLIST_REMOVE_CURRENT(list);
3240 ast_verb(2, "Manager unregistered action %s\n", action);