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"
94 * Linked list of events.
95 * Global events are appended to the list by append_event().
96 * The usecount is the number of stored pointers to the element,
97 * excluding the list pointers. So an element that is only in
98 * the list has a usecount of 0, not 1.
100 * Clients have a pointer to the last event processed, and for each
101 * of these clients we track the usecount of the elements.
102 * If we have a pointer to an entry in the list, it is safe to navigate
103 * it forward because elements will not be deleted, but only appended.
104 * The worst that can happen is seeing the pointer still NULL.
106 * When the usecount of an element drops to 0, and the element is the
107 * first in the list, we can remove it. Removal is done within the
108 * main thread, which is woken up for the purpose.
110 * For simplicity of implementation, we make sure the list is never empty.
113 int usecount; /*!< # of clients who still need the event */
115 unsigned int seq; /*!< sequence number */
116 AST_LIST_ENTRY(eventqent) eq_next;
117 char eventdata[1]; /*!< really variable size, allocated by append_event() */
120 static AST_LIST_HEAD_STATIC(all_events, eventqent);
122 static int displayconnects = 1;
123 static int allowmultiplelogin = 1;
124 static int timestampevents;
125 static int httptimeout = 60;
126 static int manager_enabled = 0;
127 static int webmanager_enabled = 0;
129 static int block_sockets;
130 static int num_sessions;
132 static int manager_debug; /*!< enable some debugging code in the manager */
135 * Descriptor for a manager session, either on the AMI socket or over HTTP.
138 * AMI session have managerid == 0; the entry is created upon a connect,
139 * and destroyed with the socket.
140 * HTTP sessions have managerid != 0, the value is used as a search key
141 * to lookup sessions (using the mansession_id cookie).
143 #define MAX_BLACKLIST_CMD_LEN 2
145 char *words[AST_MAX_CMD_LEN];
146 } command_blacklist[] = {
147 {{ "module", "load", NULL }},
148 {{ "module", "unload", NULL }},
152 pthread_t ms_t; /*!< Execution thread, basically useless */
153 ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
154 /* XXX need to document which fields it is protecting */
155 struct sockaddr_in sin; /*!< address we are connecting from */
156 FILE *f; /*!< fdopen() on the underlying fd */
157 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
158 int inuse; /*!< number of HTTP sessions using this entry */
159 int needdestroy; /*!< Whether an HTTP session should be destroyed */
160 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
161 uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
162 time_t sessionstart; /*!< Session start time */
163 time_t sessiontimeout; /*!< Session timeout if HTTP */
164 char username[80]; /*!< Logged in username */
165 char challenge[10]; /*!< Authentication challenge */
166 int authenticated; /*!< Authentication status */
167 int readperm; /*!< Authorization for reading */
168 int writeperm; /*!< Authorization for writing */
169 char inbuf[1025]; /*!< Buffer */
170 /* we use the extra byte to add a '\0' and simplify parsing */
171 int inlen; /*!< number of buffered bytes */
172 int send_events; /*!< XXX what ? */
173 struct eventqent *last_ev; /*!< last event processed. */
174 int writetimeout; /*!< Timeout for ast_carefulwrite() */
175 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
176 AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
177 AST_LIST_ENTRY(mansession) list;
180 #define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
182 static AST_LIST_HEAD_STATIC(sessions, mansession);
184 /*! \brief user descriptor, as read from the config file.
186 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
187 * lines which are not supported here, and readperm/writeperm/writetimeout
190 struct ast_manager_user {
193 struct ast_ha *ha; /*!< ACL setting */
194 int readperm; /*! Authorization for reading */
195 int writeperm; /*! Authorization for writing */
196 int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
197 int displayconnects; /*!< XXX unused */
198 int keep; /*!< mark entries created on a reload */
199 AST_RWLIST_ENTRY(ast_manager_user) list;
202 /*! \brief list of users found in the config file */
203 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
205 /*! \brief list of actions registered */
206 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
208 /*! \brief list of hooks registered */
209 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
211 /*! \brief Add a custom hook to be called when an event is fired */
212 void ast_manager_register_hook(struct manager_custom_hook *hook)
214 AST_RWLIST_WRLOCK(&manager_hooks);
215 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
216 AST_RWLIST_UNLOCK(&manager_hooks);
220 /*! \brief Delete a custom hook to be called when an event is fired */
221 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
223 AST_RWLIST_WRLOCK(&manager_hooks);
224 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
225 AST_RWLIST_UNLOCK(&manager_hooks);
230 * Event list management functions.
231 * We assume that the event list always has at least one element,
232 * and the delete code will not remove the last entry even if the
236 static time_t __deb(time_t start, const char *msg)
238 time_t now = time(NULL);
239 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
240 if (start != 0 && now - start > 5)
241 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
245 static void LOCK_EVENTS(void)
247 time_t start = __deb(0, "about to lock events");
248 AST_LIST_LOCK(&all_events);
249 __deb(start, "done lock events");
252 static void UNLOCK_EVENTS(void)
254 __deb(0, "about to unlock events");
255 AST_LIST_UNLOCK(&all_events);
258 static void LOCK_SESS(void)
260 time_t start = __deb(0, "about to lock sessions");
261 AST_LIST_LOCK(&sessions);
262 __deb(start, "done lock sessions");
265 static void UNLOCK_SESS(void)
267 __deb(0, "about to unlock sessions");
268 AST_LIST_UNLOCK(&sessions);
272 int check_manager_enabled()
274 return manager_enabled;
277 int check_webmanager_enabled()
279 return (webmanager_enabled && manager_enabled);
283 * Grab a reference to the last event, update usecount as needed.
284 * Can handle a NULL pointer.
286 static struct eventqent *grab_last(void)
288 struct eventqent *ret;
290 AST_LIST_LOCK(&all_events);
291 ret = AST_LIST_LAST(&all_events);
292 /* the list is never empty now, but may become so when
293 * we optimize it in the future, so be prepared.
296 ast_atomic_fetchadd_int(&ret->usecount, 1);
297 AST_LIST_UNLOCK(&all_events);
302 * Purge unused events. Remove elements from the head
303 * as long as their usecount is 0 and there is a next element.
305 static void purge_events(void)
307 struct eventqent *ev;
309 AST_LIST_LOCK(&all_events);
310 while ( (ev = AST_LIST_FIRST(&all_events)) &&
311 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
312 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
315 AST_LIST_UNLOCK(&all_events);
319 * helper functions to convert back and forth between
320 * string and numeric representation of set of flags
322 static struct permalias {
326 { EVENT_FLAG_SYSTEM, "system" },
327 { EVENT_FLAG_CALL, "call" },
328 { EVENT_FLAG_LOG, "log" },
329 { EVENT_FLAG_VERBOSE, "verbose" },
330 { EVENT_FLAG_COMMAND, "command" },
331 { EVENT_FLAG_AGENT, "agent" },
332 { EVENT_FLAG_USER, "user" },
333 { EVENT_FLAG_CONFIG, "config" },
334 { EVENT_FLAG_DTMF, "dtmf" },
335 { EVENT_FLAG_REPORTING, "reporting" },
336 { EVENT_FLAG_CDR, "cdr" },
337 { EVENT_FLAG_DIALPLAN, "dialplan" },
338 { EVENT_FLAG_ORIGINATE, "originate" },
339 { EVENT_FLAG_AGI, "agi" },
344 /*! \brief Convert authority code to a list of options */
345 static char *authority_to_str(int authority, struct ast_str **res)
351 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
352 if (authority & perms[i].num) {
353 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
358 if ((*res)->used == 0) /* replace empty string with something sensible */
359 ast_str_append(res, 0, "<none>");
364 /*! Tells you if smallstr exists inside bigstr
365 which is delim by delim and uses no buf or stringsep
366 ast_instring("this|that|more","this",'|') == 1;
368 feel free to move this to app.c -anthm */
369 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
371 const char *val = bigstr, *next;
374 if ((next = strchr(val, delim))) {
375 if (!strncmp(val, smallstr, (next - val)))
380 return !strcmp(smallstr, val);
381 } while (*(val = (next + 1)));
386 static int get_perm(const char *instr)
393 for (x = 0; x < ARRAY_LEN(perms); x++) {
394 if (ast_instring(instr, perms[x].label, ','))
402 * A number returns itself, false returns 0, true returns all flags,
403 * other strings return the flags that are set.
405 static int strings_to_mask(const char *string)
409 if (ast_strlen_zero(string))
412 for (p = string; *p; p++)
413 if (*p < '0' || *p > '9')
415 if (!p) /* all digits */
417 if (ast_false(string))
419 if (ast_true(string)) { /* all permissions */
421 for (x = 0; x < ARRAY_LEN(perms); x++)
425 return get_perm(string);
428 static int check_manager_session_inuse(const char *name)
430 struct mansession *session = NULL;
432 AST_LIST_LOCK(&sessions);
433 AST_LIST_TRAVERSE(&sessions, session, list) {
434 if (!strcasecmp(session->username, name))
437 AST_LIST_UNLOCK(&sessions);
439 return session ? 1 : 0;
444 * lookup an entry in the list of registered users.
445 * must be called with the list lock held.
447 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
449 struct ast_manager_user *user = NULL;
451 AST_RWLIST_TRAVERSE(&users, user, list)
452 if (!strcasecmp(user->username, name))
457 /*! \brief Get displayconnects config option.
458 * \param s manager session to get parameter from.
459 * \return displayconnects config option value.
461 static int manager_displayconnects (struct mansession *s)
463 struct ast_manager_user *user = NULL;
466 AST_RWLIST_RDLOCK(&users);
467 if ((user = get_manager_by_name_locked (s->username)))
468 ret = user->displayconnects;
469 AST_RWLIST_UNLOCK(&users);
474 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
476 struct manager_action *cur;
477 struct ast_str *authority;
482 e->command = "manager show command";
484 "Usage: manager show command <actionname>\n"
485 " Shows the detailed description for a specific Asterisk manager interface command.\n";
490 AST_RWLIST_RDLOCK(&actions);
491 AST_RWLIST_TRAVERSE(&actions, cur, list) {
492 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
493 ret = ast_strdup(cur->action);
494 break; /* make sure we exit even if ast_strdup() returns NULL */
497 AST_RWLIST_UNLOCK(&actions);
500 authority = ast_str_alloca(80);
502 return CLI_SHOWUSAGE;
504 AST_RWLIST_RDLOCK(&actions);
505 AST_RWLIST_TRAVERSE(&actions, cur, list) {
506 for (num = 3; num < a->argc; num++) {
507 if (!strcasecmp(cur->action, a->argv[num])) {
508 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
509 cur->action, cur->synopsis,
510 authority_to_str(cur->authority, &authority),
511 S_OR(cur->description, ""));
515 AST_RWLIST_UNLOCK(&actions);
520 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
524 e->command = "manager set debug [on|off]";
525 e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
531 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
532 else if (a->argc == 4) {
533 if (!strcasecmp(a->argv[3], "on"))
535 else if (!strcasecmp(a->argv[3], "off"))
538 return CLI_SHOWUSAGE;
543 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
545 struct ast_manager_user *user = NULL;
548 struct ast_str *rauthority = ast_str_alloca(80);
549 struct ast_str *wauthority = ast_str_alloca(80);
553 e->command = "manager show user";
555 " Usage: manager show user <user>\n"
556 " Display all information related to the manager user specified.\n";
563 AST_RWLIST_RDLOCK(&users);
564 AST_RWLIST_TRAVERSE(&users, user, list) {
565 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
566 ret = ast_strdup(user->username);
570 AST_RWLIST_UNLOCK(&users);
575 return CLI_SHOWUSAGE;
577 AST_RWLIST_RDLOCK(&users);
579 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
580 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
581 AST_RWLIST_UNLOCK(&users);
585 ast_cli(a->fd, "\n");
592 "displayconnects: %s\n",
593 (user->username ? user->username : "(N/A)"),
594 (user->secret ? "<Set>" : "(N/A)"),
595 (user->ha ? "yes" : "no"),
596 authority_to_str(user->readperm, &rauthority),
597 authority_to_str(user->writeperm, &wauthority),
598 (user->displayconnects ? "yes" : "no"));
600 AST_RWLIST_UNLOCK(&users);
606 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
608 struct ast_manager_user *user = NULL;
612 e->command = "manager show users";
614 "Usage: manager show users\n"
615 " Prints a listing of all managers that are currently configured on that\n"
622 return CLI_SHOWUSAGE;
624 AST_RWLIST_RDLOCK(&users);
626 /* If there are no users, print out something along those lines */
627 if (AST_RWLIST_EMPTY(&users)) {
628 ast_cli(a->fd, "There are no manager users.\n");
629 AST_RWLIST_UNLOCK(&users);
633 ast_cli(a->fd, "\nusername\n--------\n");
635 AST_RWLIST_TRAVERSE(&users, user, list) {
636 ast_cli(a->fd, "%s\n", user->username);
640 AST_RWLIST_UNLOCK(&users);
642 ast_cli(a->fd, "-------------------\n");
643 ast_cli(a->fd, "%d manager users configured.\n", count_amu);
649 /*! \brief CLI command manager list commands */
650 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
652 struct manager_action *cur;
653 struct ast_str *authority;
654 #define HSMC_FORMAT " %-15.15s %-15.15s %-55.55s\n"
657 e->command = "manager show commands";
659 "Usage: manager show commands\n"
660 " Prints a listing of all the available Asterisk manager interface commands.\n";
665 authority = ast_str_alloca(80);
666 ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
667 ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
669 AST_RWLIST_RDLOCK(&actions);
670 AST_RWLIST_TRAVERSE(&actions, cur, list)
671 ast_cli(a->fd, HSMC_FORMAT, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
672 AST_RWLIST_UNLOCK(&actions);
677 /*! \brief CLI command manager list connected */
678 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
680 struct mansession *s;
681 time_t now = time(NULL);
682 #define HSMCONN_FORMAT1 " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
683 #define HSMCONN_FORMAT2 " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
687 e->command = "manager show connected";
689 "Usage: manager show connected\n"
690 " Prints a listing of the users that are currently connected to the\n"
691 "Asterisk manager interface.\n";
697 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
699 AST_LIST_LOCK(&sessions);
700 AST_LIST_TRAVERSE(&sessions, s, list) {
701 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);
704 AST_LIST_UNLOCK(&sessions);
706 ast_cli(a->fd, "%d users connected.\n", count);
711 /*! \brief CLI command manager list eventq */
712 /* Should change to "manager show connected" */
713 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
718 e->command = "manager show eventq";
720 "Usage: manager show eventq\n"
721 " Prints a listing of all events pending in the Asterisk manger\n"
727 AST_LIST_LOCK(&all_events);
728 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
729 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
730 ast_cli(a->fd, "Category: %d\n", s->category);
731 ast_cli(a->fd, "Event:\n%s", s->eventdata);
733 AST_LIST_UNLOCK(&all_events);
738 /*! \brief CLI command manager reload */
739 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
743 e->command = "manager reload";
745 "Usage: manager reload\n"
746 " Reloads the manager configuration.\n";
752 return CLI_SHOWUSAGE;
758 static struct ast_cli_entry cli_manager[] = {
759 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
760 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
761 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
762 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
763 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
764 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
765 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
766 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
770 * Decrement the usecount for the event; if it goes to zero,
771 * (why check for e->next ?) wakeup the
772 * main thread, which is in charge of freeing the record.
773 * Returns the next record.
775 static struct eventqent *unref_event(struct eventqent *e)
777 ast_atomic_fetchadd_int(&e->usecount, -1);
778 return AST_LIST_NEXT(e, eq_next);
781 static void ref_event(struct eventqent *e)
783 ast_atomic_fetchadd_int(&e->usecount, 1);
787 * destroy a session, leaving the usecount
789 static void free_session(struct mansession *s)
791 struct eventqent *eqe = s->last_ev;
792 struct ast_datastore *datastore;
794 /* Get rid of each of the data stores on the session */
795 while ((datastore = AST_LIST_REMOVE_HEAD(&s->datastores, entry))) {
796 /* Free the data store */
797 ast_datastore_free(datastore);
802 ast_mutex_destroy(&s->__lock);
807 static void destroy_session(struct mansession *s)
809 AST_LIST_LOCK(&sessions);
810 AST_LIST_REMOVE(&sessions, s, list);
811 ast_atomic_fetchadd_int(&num_sessions, -1);
813 AST_LIST_UNLOCK(&sessions);
817 * Generic function to return either the first or the last matching header
818 * from a list of variables, possibly skipping empty strings.
819 * At the moment there is only one use of this function in this file,
820 * so we make it static.
822 #define GET_HEADER_FIRST_MATCH 0
823 #define GET_HEADER_LAST_MATCH 1
824 #define GET_HEADER_SKIP_EMPTY 2
825 static const char *__astman_get_header(const struct message *m, char *var, int mode)
827 int x, l = strlen(var);
828 const char *result = "";
830 for (x = 0; x < m->hdrcount; x++) {
831 const char *h = m->headers[x];
832 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ') {
833 const char *value = h + l + 2;
834 /* found a potential candidate */
835 if (mode & GET_HEADER_SKIP_EMPTY && ast_strlen_zero(value))
836 continue; /* not interesting */
837 if (mode & GET_HEADER_LAST_MATCH)
838 result = value; /* record the last match so far */
848 * Return the first matching variable from an array.
849 * This is the legacy function and is implemented in therms of
850 * __astman_get_header().
852 const char *astman_get_header(const struct message *m, char *var)
854 return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
858 struct ast_variable *astman_get_variables(const struct message *m)
861 struct ast_variable *head = NULL, *cur;
863 AST_DECLARE_APP_ARGS(args,
864 AST_APP_ARG(vars)[32];
867 varlen = strlen("Variable: ");
869 for (x = 0; x < m->hdrcount; x++) {
870 char *parse, *var, *val;
872 if (strncasecmp("Variable: ", m->headers[x], varlen))
874 parse = ast_strdupa(m->headers[x] + varlen);
876 AST_STANDARD_APP_ARGS(args, parse);
879 for (y = 0; y < args.argc; y++) {
882 var = val = ast_strdupa(args.vars[y]);
884 if (!val || ast_strlen_zero(var))
886 cur = ast_variable_new(var, val, "");
896 * helper function to send a string to the socket.
897 * Return -1 on error (e.g. buffer full).
899 static int send_string(struct mansession *s, char *string)
901 int len = strlen(string); /* residual length */
903 struct timeval start = ast_tvnow();
909 n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
910 if (n == len /* ok */ || n < 0 /* error */)
912 len -= n; /* skip already written data */
916 n = -1; /* error marker */
917 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
918 if (elapsed > s->writetimeout)
920 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
924 return n < 0 ? -1 : 0;
928 * \brief thread local buffer for astman_append
930 * \note This can not be defined within the astman_append() function
931 * because it declares a couple of functions that get used to
932 * initialize the thread local storage key.
934 AST_THREADSTORAGE(astman_append_buf);
935 /*! \brief initial allocated size for the astman_append_buf */
936 #define ASTMAN_APPEND_BUF_INITSIZE 256
939 * utility functions for creating AMI replies
941 void astman_append(struct mansession *s, const char *fmt, ...)
946 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
950 ast_str_set_va(&buf, 0, fmt, ap);
954 send_string(s, buf->str);
956 ast_verbose("fd == -1 in astman_append, should not happen\n");
959 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
960 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
961 hold the session lock _or_ be running in an action callback (in which case s->busy will
962 be non-zero). In either of these cases, there is no need to lock-protect the session's
963 fd, since no other output will be sent (events will be queued), and no input will
964 be read until either the current action finishes or get_input() obtains the session
968 /*! \brief send a response with an optional message,
969 * and terminate it with an empty line.
970 * m is used only to grab the 'ActionID' field.
972 * Use the explicit constant MSG_MOREDATA to remove the empty line.
973 * XXX MSG_MOREDATA should go to a header file.
975 #define MSG_MOREDATA ((char *)astman_send_response)
976 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
978 const char *id = astman_get_header(m, "ActionID");
980 astman_append(s, "Response: %s\r\n", resp);
981 if (!ast_strlen_zero(id))
982 astman_append(s, "ActionID: %s\r\n", id);
984 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
985 if (msg == MSG_MOREDATA)
988 astman_append(s, "Message: %s\r\n\r\n", msg);
990 astman_append(s, "\r\n");
993 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
995 astman_send_response_full(s, m, resp, msg, NULL);
998 void astman_send_error(struct mansession *s, const struct message *m, char *error)
1000 astman_send_response_full(s, m, "Error", error, NULL);
1003 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
1005 astman_send_response_full(s, m, "Success", msg, NULL);
1008 static void astman_start_ack(struct mansession *s, const struct message *m)
1010 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
1013 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
1015 astman_send_response_full(s, m, "Success", msg, listflag);
1020 Rather than braindead on,off this now can also accept a specific int mask value
1021 or a ',' delim list of mask strings (the same as manager.conf) -anthm
1023 static int set_eventmask(struct mansession *s, const char *eventmask)
1025 int maskint = strings_to_mask(eventmask);
1027 ast_mutex_lock(&s->__lock);
1029 s->send_events = maskint;
1030 ast_mutex_unlock(&s->__lock);
1036 * Here we start with action_ handlers for AMI actions,
1037 * and the internal functions used by them.
1038 * Generally, the handlers are called action_foo()
1041 /* helper function for action_login() */
1042 static int authenticate(struct mansession *s, const struct message *m)
1044 const char *username = astman_get_header(m, "Username");
1045 const char *password = astman_get_header(m, "Secret");
1047 struct ast_manager_user *user = NULL;
1049 if (ast_strlen_zero(username)) /* missing username */
1052 /* locate user in locked state */
1053 AST_RWLIST_WRLOCK(&users);
1055 if (!(user = get_manager_by_name_locked(username))) {
1056 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1057 } else if (user->ha && !ast_apply_ha(user->ha, &(s->sin))) {
1058 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1059 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
1060 const char *key = astman_get_header(m, "Key");
1061 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) && user->secret) {
1064 char md5key[256] = "";
1065 struct MD5Context md5;
1066 unsigned char digest[16];
1069 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1070 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
1071 MD5Final(digest, &md5);
1072 for (x = 0; x < 16; x++)
1073 len += sprintf(md5key + len, "%2.2x", digest[x]);
1074 if (!strcmp(md5key, key))
1077 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
1078 S_OR(s->challenge, ""));
1080 } else if (password && user->secret && !strcmp(password, user->secret))
1084 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1085 AST_RWLIST_UNLOCK(&users);
1091 ast_copy_string(s->username, username, sizeof(s->username));
1092 s->readperm = user->readperm;
1093 s->writeperm = user->writeperm;
1094 s->writetimeout = user->writetimeout;
1095 s->sessionstart = time(NULL);
1096 set_eventmask(s, astman_get_header(m, "Events"));
1098 AST_RWLIST_UNLOCK(&users);
1102 /*! \brief Manager PING */
1103 static char mandescr_ping[] =
1104 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1105 " manager connection open.\n"
1106 "Variables: NONE\n";
1108 static int action_ping(struct mansession *s, const struct message *m)
1110 astman_append(s, "Response: Success\r\n"
1116 static char mandescr_getconfig[] =
1117 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1118 "file by category and contents or optionally by specified category only.\n"
1119 "Variables: (Names marked with * are required)\n"
1120 " *Filename: Configuration filename (e.g. foo.conf)\n"
1121 " Category: Category in configuration file\n";
1123 static int action_getconfig(struct mansession *s, const struct message *m)
1125 struct ast_config *cfg;
1126 const char *fn = astman_get_header(m, "Filename");
1127 const char *category = astman_get_header(m, "Category");
1130 char *cur_category = NULL;
1131 struct ast_variable *v;
1132 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1134 if (ast_strlen_zero(fn)) {
1135 astman_send_error(s, m, "Filename not specified");
1138 cfg = ast_config_load2(fn, "manager", config_flags);
1139 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
1140 astman_send_error(s, m, "Config file not found");
1144 astman_start_ack(s, m);
1145 while ((cur_category = ast_category_browse(cfg, cur_category))) {
1146 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
1148 astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
1149 for (v = ast_variable_browse(cfg, cur_category); v; v = v->next)
1150 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1154 if (!ast_strlen_zero(category) && catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1155 astman_append(s, "No categories found");
1156 ast_config_destroy(cfg);
1157 astman_append(s, "\r\n");
1162 static char mandescr_listcategories[] =
1163 "Description: A 'ListCategories' action will dump the categories in\n"
1166 " Filename: Configuration filename (e.g. foo.conf)\n";
1168 static int action_listcategories(struct mansession *s, const struct message *m)
1170 struct ast_config *cfg;
1171 const char *fn = astman_get_header(m, "Filename");
1172 char *category = NULL;
1173 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1176 if (ast_strlen_zero(fn)) {
1177 astman_send_error(s, m, "Filename not specified");
1180 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1181 astman_send_error(s, m, "Config file not found or file has invalid syntax");
1184 astman_start_ack(s, m);
1185 while ((category = ast_category_browse(cfg, category))) {
1186 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1189 if (catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1190 astman_append(s, "Error: no categories found");
1191 ast_config_destroy(cfg);
1192 astman_append(s, "\r\n");
1200 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1201 static void json_escape(char *out, const char *in)
1204 if (*in == '\\' || *in == '\"')
1211 static char mandescr_getconfigjson[] =
1212 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1213 "file by category and contents in JSON format. This only makes sense to be used\n"
1214 "using rawman over the HTTP interface.\n"
1216 " Filename: Configuration filename (e.g. foo.conf)\n";
1218 static int action_getconfigjson(struct mansession *s, const struct message *m)
1220 struct ast_config *cfg;
1221 const char *fn = astman_get_header(m, "Filename");
1222 char *category = NULL;
1223 struct ast_variable *v;
1226 unsigned int buf_len = 0;
1227 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1229 if (ast_strlen_zero(fn)) {
1230 astman_send_error(s, m, "Filename not specified");
1234 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1235 astman_send_error(s, m, "Config file not found");
1240 buf = alloca(buf_len);
1242 astman_start_ack(s, m);
1243 astman_append(s, "JSON: {");
1244 while ((category = ast_category_browse(cfg, category))) {
1246 if (buf_len < 2 * strlen(category) + 1) {
1248 buf = alloca(buf_len);
1250 json_escape(buf, category);
1251 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1254 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1256 astman_append(s, ",");
1257 if (buf_len < 2 * strlen(v->name) + 1) {
1259 buf = alloca(buf_len);
1261 json_escape(buf, v->name);
1262 astman_append(s, "\"%s", buf);
1263 if (buf_len < 2 * strlen(v->value) + 1) {
1265 buf = alloca(buf_len);
1267 json_escape(buf, v->value);
1268 astman_append(s, "%s\"", buf);
1272 astman_append(s, "]");
1274 astman_append(s, "}\r\n\r\n");
1276 ast_config_destroy(cfg);
1281 /* helper function for action_updateconfig */
1282 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1286 const char *action, *cat, *var, *value, *match, *line;
1287 struct ast_category *category;
1288 struct ast_variable *v;
1289 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
1290 enum error_type result = 0;
1292 for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
1293 unsigned int object = 0;
1295 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1296 action = astman_get_header(m, hdr);
1297 if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
1298 break; /* this could cause problems if actions come in misnumbered */
1300 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1301 cat = astman_get_header(m, hdr);
1302 if (ast_strlen_zero(cat)) { /* every action needs a category */
1303 result = UNSPECIFIED_CATEGORY;
1307 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1308 var = astman_get_header(m, hdr);
1310 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1311 value = astman_get_header(m, hdr);
1313 if (!ast_strlen_zero(value) && *value == '>') {
1318 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1319 match = astman_get_header(m, hdr);
1321 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
1322 line = astman_get_header(m, hdr);
1324 if (!strcasecmp(action, "newcat")) {
1325 if (ast_category_get(cfg,cat)) { /* check to make sure the cat doesn't */
1326 result = FAILURE_NEWCAT; /* already exist */
1329 if (!(category = ast_category_new(cat, dfn, -1))) {
1330 result = FAILURE_ALLOCATION;
1333 if (ast_strlen_zero(match)) {
1334 ast_category_append(cfg, category);
1336 ast_category_insert(cfg, category, match);
1337 } else if (!strcasecmp(action, "renamecat")) {
1338 if (ast_strlen_zero(value)) {
1339 result = UNSPECIFIED_ARGUMENT;
1342 if (!(category = ast_category_get(cfg, cat))) {
1343 result = UNKNOWN_CATEGORY;
1346 ast_category_rename(category, value);
1347 } else if (!strcasecmp(action, "delcat")) {
1348 if (ast_category_delete(cfg, cat)) {
1349 result = FAILURE_DELCAT;
1352 } else if (!strcasecmp(action, "emptycat")) {
1353 if (ast_category_empty(cfg, cat)) {
1354 result = FAILURE_EMPTYCAT;
1357 } else if (!strcasecmp(action, "update")) {
1358 if (ast_strlen_zero(var)) {
1359 result = UNSPECIFIED_ARGUMENT;
1362 if (!(category = ast_category_get(cfg,cat))) {
1363 result = UNKNOWN_CATEGORY;
1366 if (ast_variable_update(category, var, value, match, object)) {
1367 result = FAILURE_UPDATE;
1370 } else if (!strcasecmp(action, "delete")) {
1371 if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
1372 result = UNSPECIFIED_ARGUMENT;
1375 if (!(category = ast_category_get(cfg, cat))) {
1376 result = UNKNOWN_CATEGORY;
1379 if (ast_variable_delete(category, var, match, line)) {
1380 result = FAILURE_DELETE;
1383 } else if (!strcasecmp(action, "append")) {
1384 if (ast_strlen_zero(var)) {
1385 result = UNSPECIFIED_ARGUMENT;
1388 if (!(category = ast_category_get(cfg, cat))) {
1389 result = UNKNOWN_CATEGORY;
1392 if (!(v = ast_variable_new(var, value, dfn))) {
1393 result = FAILURE_ALLOCATION;
1396 if (object || (match && !strcasecmp(match, "object")))
1398 ast_variable_append(category, v);
1399 } else if (!strcasecmp(action, "insert")) {
1400 if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
1401 result = UNSPECIFIED_ARGUMENT;
1404 if (!(category = ast_category_get(cfg, cat))) {
1405 result = UNKNOWN_CATEGORY;
1408 if (!(v = ast_variable_new(var, value, dfn))) {
1409 result = FAILURE_ALLOCATION;
1412 ast_variable_insert(category, v, line);
1415 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
1416 result = UNKNOWN_ACTION;
1425 static char mandescr_updateconfig[] =
1426 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1427 "configuration elements in Asterisk configuration files.\n"
1428 "Variables (X's represent 6 digit number beginning with 000000):\n"
1429 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1430 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1431 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1432 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,EmptyCat,Update,Delete,Append,Insert)\n"
1433 " Cat-XXXXXX: Category to operate on\n"
1434 " Var-XXXXXX: Variable to work on\n"
1435 " Value-XXXXXX: Value to work on\n"
1436 " Match-XXXXXX: Extra match required to match line\n"
1437 " Line-XXXXXX: Line in category to operate on (used with delete and insert actions)\n";
1439 static int action_updateconfig(struct mansession *s, const struct message *m)
1441 struct ast_config *cfg;
1442 const char *sfn = astman_get_header(m, "SrcFilename");
1443 const char *dfn = astman_get_header(m, "DstFilename");
1445 const char *rld = astman_get_header(m, "Reload");
1446 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1447 enum error_type result;
1449 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1450 astman_send_error(s, m, "Filename not specified");
1453 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
1454 astman_send_error(s, m, "Config file not found");
1457 result = handle_updates(s, m, cfg, dfn);
1459 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1460 res = ast_config_text_file_save(dfn, cfg, "Manager");
1461 ast_config_destroy(cfg);
1463 astman_send_error(s, m, "Save of config failed");
1466 astman_send_ack(s, m, NULL);
1467 if (!ast_strlen_zero(rld)) {
1470 ast_module_reload(rld);
1473 ast_config_destroy(cfg);
1475 case UNKNOWN_ACTION:
1476 astman_send_error(s, m, "Unknown action command");
1478 case UNKNOWN_CATEGORY:
1479 astman_send_error(s, m, "Given category does not exist");
1481 case UNSPECIFIED_CATEGORY:
1482 astman_send_error(s, m, "Category not specified");
1484 case UNSPECIFIED_ARGUMENT:
1485 astman_send_error(s, m, "Problem with category, value, or line (if required)");
1487 case FAILURE_ALLOCATION:
1488 astman_send_error(s, m, "Memory allocation failure, this should not happen");
1490 case FAILURE_NEWCAT:
1491 astman_send_error(s, m, "Create category did not complete successfully");
1493 case FAILURE_DELCAT:
1494 astman_send_error(s, m, "Delete category did not complete successfully");
1496 case FAILURE_EMPTYCAT:
1497 astman_send_error(s, m, "Empty category did not complete successfully");
1499 case FAILURE_UPDATE:
1500 astman_send_error(s, m, "Update did not complete successfully");
1502 case FAILURE_DELETE:
1503 astman_send_error(s, m, "Delete did not complete successfully");
1505 case FAILURE_APPEND:
1506 astman_send_error(s, m, "Append did not complete successfully");
1513 static char mandescr_createconfig[] =
1514 "Description: A 'CreateConfig' action will create an empty file in the\n"
1515 "configuration directory. This action is intended to be used before an\n"
1516 "UpdateConfig action.\n"
1518 " Filename: The configuration filename to create (e.g. foo.conf)\n";
1520 static int action_createconfig(struct mansession *s, const struct message *m)
1523 const char *fn = astman_get_header(m, "Filename");
1524 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
1525 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
1526 ast_str_append(&filepath, 0, "%s", fn);
1528 if ((fd = open(filepath->str, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
1530 astman_send_ack(s, m, "New configuration file created successfully");
1532 astman_send_error(s, m, strerror(errno));
1537 /*! \brief Manager WAITEVENT */
1538 static char mandescr_waitevent[] =
1539 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1540 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1541 "session, events will be generated and queued.\n"
1543 " Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1545 static int action_waitevent(struct mansession *s, const struct message *m)
1547 const char *timeouts = astman_get_header(m, "Timeout");
1551 const char *id = astman_get_header(m, "ActionID");
1554 if (!ast_strlen_zero(id))
1555 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1559 if (!ast_strlen_zero(timeouts)) {
1560 sscanf(timeouts, "%i", &timeout);
1563 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1566 ast_mutex_lock(&s->__lock);
1567 if (s->waiting_thread != AST_PTHREADT_NULL)
1568 pthread_kill(s->waiting_thread, SIGURG);
1570 if (s->managerid) { /* AMI-over-HTTP session */
1572 * Make sure the timeout is within the expire time of the session,
1573 * as the client will likely abort the request if it does not see
1574 * data coming after some amount of time.
1576 time_t now = time(NULL);
1577 int max = s->sessiontimeout - now - 10;
1579 if (max < 0) /* We are already late. Strange but possible. */
1581 if (timeout < 0 || timeout > max)
1583 if (!s->send_events) /* make sure we record events */
1584 s->send_events = -1;
1586 ast_mutex_unlock(&s->__lock);
1588 /* XXX should this go inside the lock ? */
1589 s->waiting_thread = pthread_self(); /* let new events wake up this thread */
1590 ast_debug(1, "Starting waiting for an event!\n");
1592 for (x = 0; x < timeout || timeout < 0; x++) {
1593 ast_mutex_lock(&s->__lock);
1596 /* We can have multiple HTTP session point to the same mansession entry.
1597 * The way we deal with it is not very nice: newcomers kick out the previous
1598 * HTTP session. XXX this needs to be improved.
1600 if (s->waiting_thread != pthread_self())
1604 ast_mutex_unlock(&s->__lock);
1607 if (s->managerid == 0) { /* AMI session */
1608 if (ast_wait_for_input(s->fd, 1000))
1610 } else { /* HTTP session */
1614 ast_debug(1, "Finished waiting for an event!\n");
1615 ast_mutex_lock(&s->__lock);
1616 if (s->waiting_thread == pthread_self()) {
1617 struct eventqent *eqe;
1618 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1619 while ( (eqe = NEW_EVENT(s)) ) {
1621 if (((s->readperm & eqe->category) == eqe->category) &&
1622 ((s->send_events & eqe->category) == eqe->category)) {
1623 astman_append(s, "%s", eqe->eventdata);
1625 s->last_ev = unref_event(s->last_ev);
1628 "Event: WaitEventComplete\r\n"
1631 s->waiting_thread = AST_PTHREADT_NULL;
1633 ast_debug(1, "Abandoning event request!\n");
1635 ast_mutex_unlock(&s->__lock);
1639 static char mandescr_listcommands[] =
1640 "Description: Returns the action name and synopsis for every\n"
1641 " action that is available to the user\n"
1642 "Variables: NONE\n";
1644 /*! \note The actionlock is read-locked by the caller of this function */
1645 static int action_listcommands(struct mansession *s, const struct message *m)
1647 struct manager_action *cur;
1648 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1650 astman_start_ack(s, m);
1651 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1652 if (s->writeperm & cur->authority || cur->authority == 0)
1653 astman_append(s, "%s: %s (Priv: %s)\r\n",
1654 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1656 astman_append(s, "\r\n");
1661 static char mandescr_events[] =
1662 "Description: Enable/Disable sending of events to this manager\n"
1665 " EventMask: 'on' if all events should be sent,\n"
1666 " 'off' if no events should be sent,\n"
1667 " 'system,call,log' to select which flags events should have to be sent.\n";
1669 static int action_events(struct mansession *s, const struct message *m)
1671 const char *mask = astman_get_header(m, "EventMask");
1674 res = set_eventmask(s, mask);
1676 astman_append(s, "Response: Success\r\n"
1679 astman_append(s, "Response: Success\r\n"
1684 static char mandescr_logoff[] =
1685 "Description: Logoff this manager session\n"
1686 "Variables: NONE\n";
1688 static int action_logoff(struct mansession *s, const struct message *m)
1690 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1694 static int action_login(struct mansession *s, const struct message *m)
1696 if (authenticate(s, m)) {
1698 astman_send_error(s, m, "Authentication failed");
1701 s->authenticated = 1;
1702 if (manager_displayconnects(s))
1703 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1704 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1705 astman_send_ack(s, m, "Authentication accepted");
1709 static int action_challenge(struct mansession *s, const struct message *m)
1711 const char *authtype = astman_get_header(m, "AuthType");
1713 if (!strcasecmp(authtype, "MD5")) {
1714 if (ast_strlen_zero(s->challenge))
1715 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1716 ast_mutex_lock(&s->__lock);
1717 astman_start_ack(s, m);
1718 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1719 ast_mutex_unlock(&s->__lock);
1721 astman_send_error(s, m, "Must specify AuthType");
1726 static char mandescr_hangup[] =
1727 "Description: Hangup a channel\n"
1729 " Channel: The channel name to be hungup\n";
1731 static int action_hangup(struct mansession *s, const struct message *m)
1733 struct ast_channel *c = NULL;
1734 const char *name = astman_get_header(m, "Channel");
1735 if (ast_strlen_zero(name)) {
1736 astman_send_error(s, m, "No channel specified");
1739 c = ast_get_channel_by_name_locked(name);
1741 astman_send_error(s, m, "No such channel");
1744 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1745 ast_channel_unlock(c);
1746 astman_send_ack(s, m, "Channel Hungup");
1750 static char mandescr_setvar[] =
1751 "Description: Set a global or local channel variable.\n"
1752 "Variables: (Names marked with * are required)\n"
1753 " Channel: Channel to set variable for\n"
1754 " *Variable: Variable name\n"
1757 static int action_setvar(struct mansession *s, const struct message *m)
1759 struct ast_channel *c = NULL;
1760 const char *name = astman_get_header(m, "Channel");
1761 const char *varname = astman_get_header(m, "Variable");
1762 const char *varval = astman_get_header(m, "Value");
1764 if (ast_strlen_zero(varname)) {
1765 astman_send_error(s, m, "No variable specified");
1769 if (!ast_strlen_zero(name)) {
1770 c = ast_get_channel_by_name_locked(name);
1772 astman_send_error(s, m, "No such channel");
1777 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1780 ast_channel_unlock(c);
1782 astman_send_ack(s, m, "Variable Set");
1787 static char mandescr_getvar[] =
1788 "Description: Get the value of a global or local channel variable.\n"
1789 "Variables: (Names marked with * are required)\n"
1790 " Channel: Channel to read variable from\n"
1791 " *Variable: Variable name\n"
1792 " ActionID: Optional Action id for message matching.\n";
1794 static int action_getvar(struct mansession *s, const struct message *m)
1796 struct ast_channel *c = NULL;
1797 const char *name = astman_get_header(m, "Channel");
1798 const char *varname = astman_get_header(m, "Variable");
1800 char workspace[1024] = "";
1802 if (ast_strlen_zero(varname)) {
1803 astman_send_error(s, m, "No variable specified");
1807 if (!ast_strlen_zero(name)) {
1808 c = ast_get_channel_by_name_locked(name);
1810 astman_send_error(s, m, "No such channel");
1815 if (varname[strlen(varname) - 1] == ')') {
1817 c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
1819 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1820 ast_channel_free(c);
1822 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
1824 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1827 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1831 ast_channel_unlock(c);
1832 astman_start_ack(s, m);
1833 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1838 static char mandescr_status[] =
1839 "Description: Lists channel status along with requested channel vars.\n"
1840 "Variables: (Names marked with * are required)\n"
1841 " *Channel: Name of the channel to query for status\n"
1842 " Variables: Comma ',' separated list of variables to include\n"
1843 " ActionID: Optional ID for this transaction\n"
1844 "Will return the status information of each channel along with the\n"
1845 "value for the specified channel variables.\n";
1848 /*! \brief Manager "status" command to show channels */
1849 /* Needs documentation... */
1850 static int action_status(struct mansession *s, const struct message *m)
1852 const char *name = astman_get_header(m, "Channel");
1853 const char *cvariables = astman_get_header(m, "Variables");
1854 char *variables = ast_strdupa(S_OR(cvariables, ""));
1855 struct ast_channel *c;
1857 struct timeval now = ast_tvnow();
1858 long elapsed_seconds = 0;
1860 int all = ast_strlen_zero(name); /* set if we want all channels */
1861 const char *id = astman_get_header(m, "ActionID");
1863 AST_DECLARE_APP_ARGS(vars,
1864 AST_APP_ARG(name)[100];
1866 struct ast_str *str = ast_str_create(1000);
1868 if (!ast_strlen_zero(id))
1869 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1874 c = ast_channel_walk_locked(NULL);
1876 c = ast_get_channel_by_name_locked(name);
1878 astman_send_error(s, m, "No such channel");
1882 astman_send_ack(s, m, "Channel status will follow");
1884 if (!ast_strlen_zero(cvariables)) {
1885 AST_STANDARD_APP_ARGS(vars, variables);
1888 /* if we look by name, we break after the first iteration */
1890 if (!ast_strlen_zero(cvariables)) {
1893 for (i = 0; i < vars.argc; i++) {
1894 char valbuf[512], *ret = NULL;
1896 if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
1897 if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
1902 pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
1905 ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
1911 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1916 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1920 "Privilege: Call\r\n"
1922 "CallerIDNum: %s\r\n"
1923 "CallerIDName: %s\r\n"
1924 "Accountcode: %s\r\n"
1925 "ChannelState: %d\r\n"
1926 "ChannelStateDesc: %s\r\n"
1937 S_OR(c->cid.cid_num, ""),
1938 S_OR(c->cid.cid_name, ""),
1941 ast_state2str(c->_state), c->context,
1942 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, str->str, idText);
1946 "Privilege: Call\r\n"
1948 "CallerIDNum: %s\r\n"
1949 "CallerIDName: %s\r\n"
1958 S_OR(c->cid.cid_num, "<unknown>"),
1959 S_OR(c->cid.cid_name, "<unknown>"),
1961 ast_state2str(c->_state), bridge, c->uniqueid, str->str, idText);
1963 ast_channel_unlock(c);
1966 c = ast_channel_walk_locked(c);
1969 "Event: StatusComplete\r\n"
1972 "\r\n", idText, channels);
1977 static char mandescr_sendtext[] =
1978 "Description: Sends A Text Message while in a call.\n"
1979 "Variables: (Names marked with * are required)\n"
1980 " *Channel: Channel to send message to\n"
1981 " *Message: Message to send\n"
1982 " ActionID: Optional Action id for message matching.\n";
1984 static int action_sendtext(struct mansession *s, const struct message *m)
1986 struct ast_channel *c = NULL;
1987 const char *name = astman_get_header(m, "Channel");
1988 const char *textmsg = astman_get_header(m, "Message");
1991 if (ast_strlen_zero(name)) {
1992 astman_send_error(s, m, "No channel specified");
1996 if (ast_strlen_zero(textmsg)) {
1997 astman_send_error(s, m, "No Message specified");
2001 c = ast_get_channel_by_name_locked(name);
2003 astman_send_error(s, m, "No such channel");
2007 res = ast_sendtext(c, textmsg);
2008 ast_channel_unlock(c);
2011 astman_send_ack(s, m, "Success");
2013 astman_send_error(s, m, "Failure");
2018 static char mandescr_redirect[] =
2019 "Description: Redirect (transfer) a call.\n"
2020 "Variables: (Names marked with * are required)\n"
2021 " *Channel: Channel to redirect\n"
2022 " ExtraChannel: Second call leg to transfer (optional)\n"
2023 " *Exten: Extension to transfer to\n"
2024 " *Context: Context to transfer to\n"
2025 " *Priority: Priority to transfer to\n"
2026 " ActionID: Optional Action id for message matching.\n";
2028 /*! \brief action_redirect: The redirect manager command */
2029 static int action_redirect(struct mansession *s, const struct message *m)
2031 const char *name = astman_get_header(m, "Channel");
2032 const char *name2 = astman_get_header(m, "ExtraChannel");
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, *chan2 = NULL;
2040 if (ast_strlen_zero(name)) {
2041 astman_send_error(s, m, "Channel not specified");
2044 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2045 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2046 astman_send_error(s, m, "Invalid priority");
2050 /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
2051 chan = ast_get_channel_by_name_locked(name);
2054 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
2055 astman_send_error(s, m, buf);
2058 if (ast_check_hangup(chan)) {
2059 astman_send_error(s, m, "Redirect failed, channel not up.");
2060 ast_channel_unlock(chan);
2063 if (!ast_strlen_zero(name2))
2064 chan2 = ast_get_channel_by_name_locked(name2);
2065 if (chan2 && ast_check_hangup(chan2)) {
2066 astman_send_error(s, m, "Redirect failed, extra channel not up.");
2067 ast_channel_unlock(chan);
2068 ast_channel_unlock(chan2);
2071 res = ast_async_goto(chan, context, exten, pi);
2073 if (!ast_strlen_zero(name2)) {
2075 res = ast_async_goto(chan2, context, exten, pi);
2079 astman_send_ack(s, m, "Dual Redirect successful");
2081 astman_send_error(s, m, "Secondary redirect failed");
2083 astman_send_ack(s, m, "Redirect successful");
2085 astman_send_error(s, m, "Redirect failed");
2087 ast_channel_unlock(chan);
2089 ast_channel_unlock(chan2);
2093 static char mandescr_atxfer[] =
2094 "Description: Attended transfer.\n"
2095 "Variables: (Names marked with * are required)\n"
2096 " *Channel: Transferer's channel\n"
2097 " *Exten: Extension to transfer to\n"
2098 " *Context: Context to transfer to\n"
2099 " *Priority: Priority to transfer to\n"
2100 " ActionID: Optional Action id for message matching.\n";
2102 static int action_atxfer(struct mansession *s, const struct message *m)
2104 const char *name = astman_get_header(m, "Channel");
2105 const char *exten = astman_get_header(m, "Exten");
2106 const char *context = astman_get_header(m, "Context");
2107 struct ast_channel *chan = NULL;
2108 struct ast_call_feature *atxfer_feature = NULL;
2109 char *feature_code = NULL;
2111 if (ast_strlen_zero(name)) {
2112 astman_send_error(s, m, "No channel specified");
2115 if (ast_strlen_zero(exten)) {
2116 astman_send_error(s, m, "No extension specified");
2120 if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
2121 astman_send_error(s, m, "No attended transfer feature found");
2125 if (!(chan = ast_get_channel_by_name_locked(name))) {
2126 astman_send_error(s, m, "Channel specified does not exist");
2130 if (!ast_strlen_zero(context)) {
2131 pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
2134 for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
2135 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2136 ast_queue_frame(chan, &f);
2139 for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
2140 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2141 ast_queue_frame(chan, &f);
2144 astman_send_ack(s, m, "Atxfer successfully queued");
2145 ast_channel_unlock(chan);
2150 static int check_blacklist(const char *cmd)
2152 char *cmd_copy, *cur_cmd;
2153 char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
2156 cmd_copy = ast_strdupa(cmd);
2157 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
2158 cur_cmd = ast_strip(cur_cmd);
2159 if (ast_strlen_zero(cur_cmd)) {
2164 cmd_words[i] = cur_cmd;
2167 for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
2170 for (j = 0; command_blacklist[i].words[j]; j++) {
2171 if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
2185 static char mandescr_command[] =
2186 "Description: Run a CLI command.\n"
2187 "Variables: (Names marked with * are required)\n"
2188 " *Command: Asterisk CLI command to run\n"
2189 " ActionID: Optional Action id for message matching.\n";
2191 /*! \brief Manager command "command" - execute CLI command */
2192 static int action_command(struct mansession *s, const struct message *m)
2194 const char *cmd = astman_get_header(m, "Command");
2195 const char *id = astman_get_header(m, "ActionID");
2196 char *buf, *final_buf;
2197 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
2198 int fd = mkstemp(template);
2201 if (ast_strlen_zero(cmd)) {
2202 astman_send_error(s, m, "No command provided");
2206 if (check_blacklist(cmd)) {
2207 astman_send_error(s, m, "Command blacklisted");
2211 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
2212 if (!ast_strlen_zero(id))
2213 astman_append(s, "ActionID: %s\r\n", id);
2214 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
2215 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
2216 l = lseek(fd, 0, SEEK_END); /* how many chars available */
2218 /* This has a potential to overflow the stack. Hence, use the heap. */
2219 buf = ast_calloc(1, l + 1);
2220 final_buf = ast_calloc(1, l + 1);
2222 lseek(fd, 0, SEEK_SET);
2223 if (read(fd, buf, l) < 0) {
2224 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
2228 term_strip(final_buf, buf, l);
2229 final_buf[l] = '\0';
2231 astman_append(s, "%s", S_OR(final_buf, buf));
2236 astman_append(s, "--END COMMAND--\r\n\r\n");
2238 ast_free(final_buf);
2242 /*! \brief helper function for originate */
2243 struct fast_originate_helper {
2244 char tech[AST_MAX_EXTENSION];
2245 /*! data can contain a channel name, extension number, username, password, etc. */
2248 int format; /*!< Codecs used for a call */
2249 char app[AST_MAX_APP];
2250 char appdata[AST_MAX_EXTENSION];
2251 char cid_name[AST_MAX_EXTENSION];
2252 char cid_num[AST_MAX_EXTENSION];
2253 char context[AST_MAX_CONTEXT];
2254 char exten[AST_MAX_EXTENSION];
2255 char idtext[AST_MAX_EXTENSION];
2256 char account[AST_MAX_ACCOUNT_CODE];
2258 struct ast_variable *vars;
2261 static void *fast_originate(void *data)
2263 struct fast_originate_helper *in = data;
2266 struct ast_channel *chan = NULL;
2267 char requested_channel[AST_CHANNEL_NAME];
2269 if (!ast_strlen_zero(in->app)) {
2270 res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
2271 S_OR(in->cid_num, NULL),
2272 S_OR(in->cid_name, NULL),
2273 in->vars, in->account, &chan);
2275 res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
2276 S_OR(in->cid_num, NULL),
2277 S_OR(in->cid_name, NULL),
2278 in->vars, in->account, &chan);
2282 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
2283 /* Tell the manager what happened with the channel */
2284 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
2292 "CallerIDNum: %s\r\n"
2293 "CallerIDName: %s\r\n",
2294 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
2295 chan ? chan->uniqueid : "<null>",
2296 S_OR(in->cid_num, "<unknown>"),
2297 S_OR(in->cid_name, "<unknown>")
2300 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2302 ast_channel_unlock(chan);
2307 static char mandescr_originate[] =
2308 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
2309 " Application/Data\n"
2310 "Variables: (Names marked with * are required)\n"
2311 " *Channel: Channel name to call\n"
2312 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
2313 " Context: Context to use (requires 'Exten' and 'Priority')\n"
2314 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
2315 " Application: Application to use\n"
2316 " Data: Data to use (requires 'Application')\n"
2317 " Timeout: How long to wait for call to be answered (in ms)\n"
2318 " CallerID: Caller ID to be set on the outgoing channel\n"
2319 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
2320 " Account: Account code\n"
2321 " Async: Set to 'true' for fast origination\n";
2323 static int action_originate(struct mansession *s, const struct message *m)
2325 const char *name = astman_get_header(m, "Channel");
2326 const char *exten = astman_get_header(m, "Exten");
2327 const char *context = astman_get_header(m, "Context");
2328 const char *priority = astman_get_header(m, "Priority");
2329 const char *timeout = astman_get_header(m, "Timeout");
2330 const char *callerid = astman_get_header(m, "CallerID");
2331 const char *account = astman_get_header(m, "Account");
2332 const char *app = astman_get_header(m, "Application");
2333 const char *appdata = astman_get_header(m, "Data");
2334 const char *async = astman_get_header(m, "Async");
2335 const char *id = astman_get_header(m, "ActionID");
2336 const char *codecs = astman_get_header(m, "Codecs");
2337 struct ast_variable *vars = astman_get_variables(m);
2339 char *l = NULL, *n = NULL;
2346 int format = AST_FORMAT_SLINEAR;
2349 if (ast_strlen_zero(name)) {
2350 astman_send_error(s, m, "Channel not specified");
2353 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2354 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2355 astman_send_error(s, m, "Invalid priority");
2359 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2360 astman_send_error(s, m, "Invalid timeout");
2363 ast_copy_string(tmp, name, sizeof(tmp));
2365 data = strchr(tmp, '/');
2367 astman_send_error(s, m, "Invalid channel");
2371 ast_copy_string(tmp2, callerid, sizeof(tmp2));
2372 ast_callerid_parse(tmp2, &n, &l);
2374 if (ast_strlen_zero(n))
2378 ast_shrink_phone_number(l);
2379 if (ast_strlen_zero(l))
2382 if (!ast_strlen_zero(codecs)) {
2384 ast_parse_allow_disallow(NULL, &format, codecs, 1);
2386 if (ast_true(async)) {
2387 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2391 if (!ast_strlen_zero(id))
2392 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2393 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2394 ast_copy_string(fast->data, data, sizeof(fast->data));
2395 ast_copy_string(fast->app, app, sizeof(fast->app));
2396 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2398 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2400 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2402 ast_copy_string(fast->context, context, sizeof(fast->context));
2403 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2404 ast_copy_string(fast->account, account, sizeof(fast->account));
2405 fast->format = format;
2407 fast->priority = pi;
2408 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2415 } else if (!ast_strlen_zero(app)) {
2416 /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
2417 if (!(s->writeperm & EVENT_FLAG_SYSTEM)
2419 strcasestr(app, "system") == 0 || /* System(rm -rf /)
2420 TrySystem(rm -rf /) */
2421 strcasestr(app, "exec") || /* Exec(System(rm -rf /))
2422 TryExec(System(rm -rf /)) */
2423 strcasestr(app, "agi") || /* AGI(/bin/rm,-rf /)
2424 EAGI(/bin/rm,-rf /) */
2425 strstr(appdata, "SHELL") || /* NoOp(${SHELL(rm -rf /)}) */
2426 strstr(appdata, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
2428 astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
2431 res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2433 if (exten && context && pi)
2434 res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2436 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2441 astman_send_ack(s, m, "Originate successfully queued");
2443 astman_send_error(s, m, "Originate failed");
2447 /*! \brief Help text for manager command mailboxstatus
2449 static char mandescr_mailboxstatus[] =
2450 "Description: Checks a voicemail account for status.\n"
2451 "Variables: (Names marked with * are required)\n"
2452 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2453 " ActionID: Optional ActionID for message matching.\n"
2454 "Returns number of messages.\n"
2455 " Message: Mailbox Status\n"
2456 " Mailbox: <mailboxid>\n"
2457 " Waiting: <count>\n"
2460 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2462 const char *mailbox = astman_get_header(m, "Mailbox");
2465 if (ast_strlen_zero(mailbox)) {
2466 astman_send_error(s, m, "Mailbox not specified");
2469 ret = ast_app_has_voicemail(mailbox, NULL);
2470 astman_start_ack(s, m);
2471 astman_append(s, "Message: Mailbox Status\r\n"
2473 "Waiting: %d\r\n\r\n", mailbox, ret);
2477 static char mandescr_mailboxcount[] =
2478 "Description: Checks a voicemail account for new messages.\n"
2479 "Variables: (Names marked with * are required)\n"
2480 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2481 " ActionID: Optional ActionID for message matching.\n"
2482 "Returns number of urgent, new and old messages.\n"
2483 " Message: Mailbox Message Count\n"
2484 " Mailbox: <mailboxid>\n"
2485 " UrgentMessages: <count>\n"
2486 " NewMessages: <count>\n"
2487 " OldMessages: <count>\n"
2489 static int action_mailboxcount(struct mansession *s, const struct message *m)
2491 const char *mailbox = astman_get_header(m, "Mailbox");
2492 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
2494 if (ast_strlen_zero(mailbox)) {
2495 astman_send_error(s, m, "Mailbox not specified");
2498 ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
2499 astman_start_ack(s, m);
2500 astman_append(s, "Message: Mailbox Message Count\r\n"
2502 "UrgMessages: %d\r\n"
2503 "NewMessages: %d\r\n"
2504 "OldMessages: %d\r\n"
2506 mailbox, urgentmsgs, newmsgs, oldmsgs);
2510 static char mandescr_extensionstate[] =
2511 "Description: Report the extension state for given extension.\n"
2512 " If the extension has a hint, will use devicestate to check\n"
2513 " the status of the device connected to the extension.\n"
2514 "Variables: (Names marked with * are required)\n"
2515 " *Exten: Extension to check state on\n"
2516 " *Context: Context for extension\n"
2517 " ActionId: Optional ID for this transaction\n"
2518 "Will return an \"Extension Status\" message.\n"
2519 "The response will include the hint for the extension and the status.\n";
2521 static int action_extensionstate(struct mansession *s, const struct message *m)
2523 const char *exten = astman_get_header(m, "Exten");
2524 const char *context = astman_get_header(m, "Context");
2525 char hint[256] = "";
2527 if (ast_strlen_zero(exten)) {
2528 astman_send_error(s, m, "Extension not specified");
2531 if (ast_strlen_zero(context))
2532 context = "default";
2533 status = ast_extension_state(NULL, context, exten);
2534 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2535 astman_start_ack(s, m);
2536 astman_append(s, "Message: Extension Status\r\n"
2540 "Status: %d\r\n\r\n",
2541 exten, context, hint, status);
2545 static char mandescr_timeout[] =
2546 "Description: Hangup a channel after a certain time.\n"
2547 "Variables: (Names marked with * are required)\n"
2548 " *Channel: Channel name to hangup\n"
2549 " *Timeout: Maximum duration of the call (sec)\n"
2550 "Acknowledges set time with 'Timeout Set' message\n";
2552 static int action_timeout(struct mansession *s, const struct message *m)
2554 struct ast_channel *c;
2555 const char *name = astman_get_header(m, "Channel");
2556 double timeout = atof(astman_get_header(m, "Timeout"));
2557 struct timeval when = { timeout, 0 };
2559 if (ast_strlen_zero(name)) {
2560 astman_send_error(s, m, "No channel specified");
2563 if (!timeout || timeout < 0) {
2564 astman_send_error(s, m, "No timeout specified");
2567 c = ast_get_channel_by_name_locked(name);
2569 astman_send_error(s, m, "No such channel");
2573 when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
2574 ast_channel_setwhentohangup_tv(c, when);
2575 ast_channel_unlock(c);
2576 astman_send_ack(s, m, "Timeout Set");
2581 * Send any applicable events to the client listening on this socket.
2582 * Wait only for a finite time on each event, and drop all events whether
2583 * they are successfully sent or not.
2585 static int process_events(struct mansession *s)
2589 ast_mutex_lock(&s->__lock);
2591 struct eventqent *eqe;
2593 while ( (eqe = NEW_EVENT(s)) ) {
2595 if (!ret && s->authenticated &&
2596 (s->readperm & eqe->category) == eqe->category &&
2597 (s->send_events & eqe->category) == eqe->category) {
2598 if (send_string(s, eqe->eventdata) < 0)
2599 ret = -1; /* don't send more */
2601 s->last_ev = unref_event(s->last_ev);
2604 ast_mutex_unlock(&s->__lock);
2608 static char mandescr_userevent[] =
2609 "Description: Send an event to manager sessions.\n"
2610 "Variables: (Names marked with * are required)\n"
2611 " *UserEvent: EventStringToSend\n"
2612 " Header1: Content1\n"
2613 " HeaderN: ContentN\n";
2615 static int action_userevent(struct mansession *s, const struct message *m)
2617 const char *event = astman_get_header(m, "UserEvent");
2618 char body[2048] = "";
2620 for (x = 0; x < m->hdrcount; x++) {
2621 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2622 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2623 bodylen += strlen(m->headers[x]);
2624 ast_copy_string(body + bodylen, "\r\n", 3);
2629 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2633 static char mandescr_coresettings[] =
2634 "Description: Query for Core PBX settings.\n"
2635 "Variables: (Names marked with * are optional)\n"
2636 " *ActionID: ActionID of this transaction\n";
2638 /*! \brief Show PBX core settings information */
2639 static int action_coresettings(struct mansession *s, const struct message *m)
2641 const char *actionid = astman_get_header(m, "ActionID");
2644 if (!ast_strlen_zero(actionid))
2645 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2649 astman_append(s, "Response: Success\r\n"
2651 "AMIversion: %s\r\n"
2652 "AsteriskVersion: %s\r\n"
2653 "SystemName: %s\r\n"
2654 "CoreMaxCalls: %d\r\n"
2655 "CoreMaxLoadAvg: %f\r\n"
2656 "CoreRunUser: %s\r\n"
2657 "CoreRunGroup: %s\r\n"
2658 "CoreMaxFilehandles: %d\r\n"
2659 "CoreRealTimeEnabled: %s\r\n"
2660 "CoreCDRenabled: %s\r\n"
2661 "CoreHTTPenabled: %s\r\n"
2666 ast_config_AST_SYSTEM_NAME,
2669 ast_config_AST_RUN_USER,
2670 ast_config_AST_RUN_GROUP,
2672 ast_realtime_enabled() ? "Yes" : "No",
2673 check_cdr_enabled() ? "Yes" : "No",
2674 check_webmanager_enabled() ? "Yes" : "No"
2679 static char mandescr_corestatus[] =
2680 "Description: Query for Core PBX status.\n"
2681 "Variables: (Names marked with * are optional)\n"
2682 " *ActionID: ActionID of this transaction\n";
2684 /*! \brief Show PBX core status information */
2685 static int action_corestatus(struct mansession *s, const struct message *m)
2687 const char *actionid = astman_get_header(m, "ActionID");
2689 char startuptime[150];
2690 char reloadtime[150];
2693 if (!ast_strlen_zero(actionid))
2694 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2698 ast_localtime(&ast_startuptime, &tm, NULL);
2699 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2700 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2701 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2703 astman_append(s, "Response: Success\r\n"
2705 "CoreStartupTime: %s\r\n"
2706 "CoreReloadTime: %s\r\n"
2707 "CoreCurrentCalls: %d\r\n"
2712 ast_active_channels()
2717 static char mandescr_reload[] =
2718 "Description: Send a reload event.\n"
2719 "Variables: (Names marked with * are optional)\n"
2720 " *ActionID: ActionID of this transaction\n"
2721 " *Module: Name of the module to reload\n";
2723 /*! \brief Send a reload event */
2724 static int action_reload(struct mansession *s, const struct message *m)
2726 const char *module = astman_get_header(m, "Module");
2727 int res = ast_module_reload(S_OR(module, NULL));
2730 astman_send_ack(s, m, "Module Reloaded");
2732 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2736 static char mandescr_coreshowchannels[] =
2737 "Description: List currently defined channels and some information\n"
2740 " ActionID: Optional Action id for message matching.\n";
2742 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2743 * and some information about them. */
2744 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2746 const char *actionid = astman_get_header(m, "ActionID");
2747 char actionidtext[256];
2748 struct ast_channel *c = NULL;
2750 int duration, durh, durm, durs;
2752 if (!ast_strlen_zero(actionid))
2753 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2755 actionidtext[0] = '\0';
2757 astman_send_listack(s, m, "Channels will follow", "start");
2759 while ((c = ast_channel_walk_locked(c)) != NULL) {
2760 struct ast_channel *bc = ast_bridged_channel(c);
2761 char durbuf[10] = "";
2763 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2764 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2765 durh = duration / 3600;
2766 durm = (duration % 3600) / 60;
2767 durs = duration % 60;
2768 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2772 "Event: CoreShowChannel\r\n"
2778 "ChannelState: %d\r\n"
2779 "ChannelStateDesc: %s\r\n"
2780 "Application: %s\r\n"
2781 "ApplicationData: %s\r\n"
2782 "CallerIDnum: %s\r\n"
2784 "AccountCode: %s\r\n"
2785 "BridgedChannel: %s\r\n"
2786 "BridgedUniqueID: %s\r\n"
2787 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2788 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2789 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2790 ast_channel_unlock(c);
2795 "Event: CoreShowChannelsComplete\r\n"
2796 "EventList: Complete\r\n"
2799 "\r\n", numchans, actionidtext);
2804 static char mandescr_modulecheck[] =
2805 "Description: Checks if Asterisk module is loaded\n"
2807 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2808 " Module: <name> Asterisk module name (not including extension)\n"
2810 "Will return Success/Failure\n"
2811 "For success returns, the module revision number is included.\n";
2813 /* Manager function to check if module is loaded */
2814 static int manager_modulecheck(struct mansession *s, const struct message *m)
2817 const char *module = astman_get_header(m, "Module");
2818 const char *id = astman_get_header(m, "ActionID");
2820 #if !defined(LOW_MEMORY)
2821 const char *version;
2823 char filename[PATH_MAX];
2826 ast_copy_string(filename, module, sizeof(filename));
2827 if ((cut = strchr(filename, '.'))) {
2830 cut = filename + strlen(filename);
2832 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
2833 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2834 res = ast_module_check(filename);
2836 astman_send_error(s, m, "Module not loaded");
2839 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
2840 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2841 #if !defined(LOW_MEMORY)
2842 version = ast_file_version_find(filename);
2845 if (!ast_strlen_zero(id))
2846 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2849 astman_append(s, "Response: Success\r\n%s", idText);
2850 #if !defined(LOW_MEMORY)
2851 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2856 static char mandescr_moduleload[] =
2857 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2859 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2860 " Module: <name> Asterisk module name (including .so extension)\n"
2861 " or subsystem identifier:\n"
2862 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2863 " LoadType: load | unload | reload\n"
2864 " The operation to be done on module\n"
2865 " If no module is specified for a reload loadtype, all modules are reloaded";
2867 static int manager_moduleload(struct mansession *s, const struct message *m)
2870 const char *module = astman_get_header(m, "Module");
2871 const char *loadtype = astman_get_header(m, "LoadType");
2873 if (!loadtype || strlen(loadtype) == 0)
2874 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2875 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2876 astman_send_error(s, m, "Need module name");
2878 if (!strcasecmp(loadtype, "load")) {
2879 res = ast_load_resource(module);
2881 astman_send_error(s, m, "Could not load module.");
2883 astman_send_ack(s, m, "Module loaded.");
2884 } else if (!strcasecmp(loadtype, "unload")) {
2885 res = ast_unload_resource(module, AST_FORCE_SOFT);
2887 astman_send_error(s, m, "Could not unload module.");
2889 astman_send_ack(s, m, "Module unloaded.");
2890 } else if (!strcasecmp(loadtype, "reload")) {
2891 if (module != NULL) {
2892 res = ast_module_reload(module);
2894 astman_send_error(s, m, "No such module.");
2896 astman_send_error(s, m, "Module does not support reload action.");
2898 astman_send_ack(s, m, "Module reloaded.");
2900 ast_module_reload(NULL); /* Reload all modules */
2901 astman_send_ack(s, m, "All modules reloaded");
2904 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2909 * Done with the action handlers here, we start with the code in charge
2910 * of accepting connections and serving them.
2911 * accept_thread() forks a new thread for each connection, session_do(),
2912 * which in turn calls get_input() repeatedly until a full message has
2913 * been accumulated, and then invokes process_message() to pass it to
2914 * the appropriate handler.
2918 * Process an AMI message, performing desired action.
2919 * Return 0 on success, -1 on error that require the session to be destroyed.
2921 static int process_message(struct mansession *s, const struct message *m)
2923 char action[80] = "";
2925 struct manager_action *tmp;
2926 const char *user = astman_get_header(m, "Username");
2928 ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
2929 ast_debug(1, "Manager received command '%s'\n", action);
2931 if (ast_strlen_zero(action)) {
2932 ast_mutex_lock(&s->__lock);
2933 astman_send_error(s, m, "Missing action in request");
2934 ast_mutex_unlock(&s->__lock);
2938 if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2939 ast_mutex_lock(&s->__lock);
2940 astman_send_error(s, m, "Permission denied");
2941 ast_mutex_unlock(&s->__lock);
2945 if (!allowmultiplelogin && !s->authenticated && user &&
2946 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2947 if (check_manager_session_inuse(user)) {
2949 ast_mutex_lock(&s->__lock);
2950 astman_send_error(s, m, "Login Already In Use");
2951 ast_mutex_unlock(&s->__lock);
2956 AST_RWLIST_RDLOCK(&actions);
2957 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2958 if (strcasecmp(action, tmp->action))
2960 if (s->writeperm & tmp->authority || tmp->authority == 0)
2961 ret = tmp->func(s, m);
2963 astman_send_error(s, m, "Permission denied");
2966 AST_RWLIST_UNLOCK(&actions);
2970 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2971 ast_mutex_lock(&s->__lock);
2972 astman_send_error(s, m, buf);
2973 ast_mutex_unlock(&s->__lock);
2977 /* Once done with our message, deliver any pending events unless the
2978 requester doesn't want them as part of this response.
2980 if (ast_strlen_zero(astman_get_header(m, "SuppressEvents"))) {
2981 return process_events(s);
2988 * Read one full line (including crlf) from the manager socket.
2990 * \r\n is the only valid terminator for the line.
2991 * (Note that, later, '\0' will be considered as the end-of-line marker,
2992 * so everything between the '\0' and the '\r\n' will not be used).
2993 * Also note that we assume output to have at least "maxlen" space.
2996 static int get_input(struct mansession *s, char *output)
2999 int maxlen = sizeof(s->inbuf) - 1;
3000 char *src = s->inbuf;
3003 * Look for \r\n within the buffer. If found, copy to the output
3004 * buffer and return, trimming the \r\n (not used afterwards).
3006 for (x = 0; x < s->inlen; x++) {
3007 int cr; /* set if we have \r */
3008 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
3009 cr = 2; /* Found. Update length to include \r\n */
3010 else if (src[x] == '\n')
3011 cr = 1; /* also accept \n only */
3014 memmove(output, src, x); /*... but trim \r\n */
3015 output[x] = '\0'; /* terminate the string */
3016 x += cr; /* number of bytes used */
3017 s->inlen -= x; /* remaining size */
3018 memmove(src, src + x, s->inlen); /* remove used bytes */
3021 if (s->inlen >= maxlen) {
3022 /* no crlf found, and buffer full - sorry, too long for us */
3023 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
3028 /* XXX do we really need this locking ? */
3029 ast_mutex_lock(&s->__lock);
3030 if (s->pending_event) {
3031 s->pending_event = 0;
3032 ast_mutex_unlock(&s->__lock);
3035 s->waiting_thread = pthread_self();
3036 ast_mutex_unlock(&s->__lock);
3038 res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
3040 ast_mutex_lock(&s->__lock);
3041 s->waiting_thread = AST_PTHREADT_NULL;
3042 ast_mutex_unlock(&s->__lock);
3045 /* If we get a signal from some other thread (typically because
3046 * there are new events queued), return 0 to notify the caller.
3048 if (errno == EINTR || errno == EAGAIN)
3050 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
3053 ast_mutex_lock(&s->__lock);
3054 res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
3056 res = -1; /* error return */
3059 src[s->inlen] = '\0';
3062 ast_mutex_unlock(&s->__lock);
3066 static int do_message(struct mansession *s)
3068 struct message m = { 0 };
3069 char header_buf[sizeof(s->inbuf)] = { '\0' };
3073 /* Check if any events are pending and do them if needed */
3074 if (process_events(s))
3076 res = get_input(s, header_buf);
3079 } else if (res > 0) {
3080 if (ast_strlen_zero(header_buf))
3081 return process_message(s, &m) ? -1 : 0;
3082 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
3083 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
3090 /*! \brief The body of the individual manager session.
3091 * Call get_input() to read one line at a time
3092 * (or be woken up on new events), collect the lines in a
3093 * message until found an empty line, and execute the request.
3094 * In any case, deliver events asynchronously through process_events()
3095 * (called from here if no line is available, or at the end of
3096 * process_message(). )
3098 static void *session_do(void *data)
3100 struct ast_tcptls_session_instance *ser = data;
3101 struct mansession *s = ast_calloc(1, sizeof(*s));
3108 s->writetimeout = 100;
3109 s->waiting_thread = AST_PTHREADT_NULL;
3111 flags = fcntl(ser->fd, F_GETFL);
3112 if (!block_sockets) /* make sure socket is non-blocking */
3113 flags |= O_NONBLOCK;
3115 flags &= ~O_NONBLOCK;
3116 fcntl(ser->fd, F_SETFL, flags);
3118 ast_mutex_init(&s->__lock);
3119 s->send_events = -1;
3120 /* Hook to the tail of the event queue */
3121 s->last_ev = grab_last();
3123 /* these fields duplicate those in the 'ser' structure */
3126 s->sin = ser->remote_address;
3128 AST_LIST_HEAD_INIT_NOLOCK(&s->datastores);
3130 AST_LIST_LOCK(&sessions);
3131 AST_LIST_INSERT_HEAD(&sessions, s, list);
3132 ast_atomic_fetchadd_int(&num_sessions, 1);
3133 AST_LIST_UNLOCK(&sessions);
3135 astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
3137 if ((res = do_message(s)) < 0)
3140 /* session is over, explain why and terminate */
3141 if (s->authenticated) {
3142 if (manager_displayconnects(s))
3143 ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3144 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3146 if (displayconnects)
3147 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
3148 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
3151 /* It is possible under certain circumstances for this session thread
3152 to complete its work and exit *before* the thread that created it
3153 has finished executing the ast_pthread_create_background() function.
3154 If this occurs, some versions of glibc appear to act in a buggy
3155 fashion and attempt to write data into memory that it thinks belongs
3156 to the thread but is in fact not owned by the thread (or may have
3157 been freed completely).
3159 Causing this thread to yield to other threads at least one time
3160 appears to work around this bug.
3172 /*! \brief remove at most n_max stale session from the list. */
3173 static void purge_sessions(int n_max)
3175 struct mansession *s;
3176 time_t now = time(NULL);
3178 AST_LIST_LOCK(&sessions);
3179 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
3180 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
3181 AST_LIST_REMOVE_CURRENT(list);
3182 ast_atomic_fetchadd_int(&num_sessions, -1);
3183 if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
3184 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
3185 s->username, ast_inet_ntoa(s->sin.sin_addr));
3187 free_session(s); /* XXX outside ? */
3192 AST_LIST_TRAVERSE_SAFE_END;
3193 AST_LIST_UNLOCK(&sessions);
3197 * events are appended to a queue from where they
3198 * can be dispatched to clients.
3200 static int append_event(const char *str, int category)
3202 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
3203 static int seq; /* sequence number */
3208 /* need to init all fields, because ast_malloc() does not */
3210 tmp->category = category;
3211 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
3212 AST_LIST_NEXT(tmp, eq_next) = NULL;
3213 strcpy(tmp->eventdata, str);
3215 AST_LIST_LOCK(&all_events);
3216 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
3217 AST_LIST_UNLOCK(&all_events);
3222 /* XXX see if can be moved inside the function */
3223 AST_THREADSTORAGE(manager_event_buf);
3224 #define MANAGER_EVENT_BUF_INITSIZE 256
3226 /*! \brief manager_event: Send AMI event to client */
3227 int __manager_event(int category, const char *event,
3228 const char *file, int line, const char *func, const char *fmt, ...)
3230 struct mansession *s;
3231 struct manager_custom_hook *hook;
3232 struct ast_str *auth = ast_str_alloca(80);
3233 const char *cat_str;