2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief The Asterisk Management Interface - AMI
23 * \author Mark Spencer <markster@digium.com>
25 * \extref OpenSSL http://www.openssl.org - for AMI/SSL
27 * At the moment this file contains a number of functions, namely:
29 * - data structures storing AMI state
30 * - AMI-related API functions, used by internal asterisk components
31 * - handlers for AMI-related CLI functions
32 * - handlers for AMI functions (available through the AMI socket)
33 * - the code for the main AMI listener thread and individual session threads
34 * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
39 /*! \addtogroup Group_AMI AMI functions
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48 #include "asterisk/_private.h"
49 #include "asterisk/paths.h" /* use various ast_config_AST_* */
55 #include "asterisk/channel.h"
56 #include "asterisk/file.h"
57 #include "asterisk/manager.h"
58 #include "asterisk/module.h"
59 #include "asterisk/config.h"
60 #include "asterisk/callerid.h"
61 #include "asterisk/lock.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/app.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/md5.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/tcptls.h"
69 #include "asterisk/http.h"
70 #include "asterisk/ast_version.h"
71 #include "asterisk/threadstorage.h"
72 #include "asterisk/linkedlists.h"
73 #include "asterisk/version.h"
74 #include "asterisk/term.h"
75 #include "asterisk/astobj2.h"
76 #include "asterisk/features.h"
93 * Linked list of events.
94 * Global events are appended to the list by append_event().
95 * The usecount is the number of stored pointers to the element,
96 * excluding the list pointers. So an element that is only in
97 * the list has a usecount of 0, not 1.
99 * Clients have a pointer to the last event processed, and for each
100 * of these clients we track the usecount of the elements.
101 * If we have a pointer to an entry in the list, it is safe to navigate
102 * it forward because elements will not be deleted, but only appended.
103 * The worst that can happen is seeing the pointer still NULL.
105 * When the usecount of an element drops to 0, and the element is the
106 * first in the list, we can remove it. Removal is done within the
107 * main thread, which is woken up for the purpose.
109 * For simplicity of implementation, we make sure the list is never empty.
112 int usecount; /*!< # of clients who still need the event */
114 unsigned int seq; /*!< sequence number */
115 AST_LIST_ENTRY(eventqent) eq_next;
116 char eventdata[1]; /*!< really variable size, allocated by append_event() */
119 static AST_LIST_HEAD_STATIC(all_events, eventqent);
121 static int displayconnects = 1;
122 static int allowmultiplelogin = 1;
123 static int timestampevents;
124 static int httptimeout = 60;
125 static int manager_enabled = 0;
126 static int webmanager_enabled = 0;
128 static int block_sockets;
129 static int num_sessions;
131 static int manager_debug; /*!< enable some debugging code in the manager */
134 * Descriptor for a manager session, either on the AMI socket or over HTTP.
137 * AMI session have managerid == 0; the entry is created upon a connect,
138 * and destroyed with the socket.
139 * HTTP sessions have managerid != 0, the value is used as a search key
140 * to lookup sessions (using the mansession_id cookie).
142 static const char *command_blacklist[] = {
148 pthread_t ms_t; /*!< Execution thread, basically useless */
149 ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
150 /* XXX need to document which fields it is protecting */
151 struct sockaddr_in sin; /*!< address we are connecting from */
152 FILE *f; /*!< fdopen() on the underlying fd */
153 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
154 int inuse; /*!< number of HTTP sessions using this entry */
155 int needdestroy; /*!< Whether an HTTP session should be destroyed */
156 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
157 unsigned long managerid; /*!< Unique manager identifier, 0 for AMI sessions */
158 time_t sessionstart; /*!< Session start time */
159 time_t sessiontimeout; /*!< Session timeout if HTTP */
160 char username[80]; /*!< Logged in username */
161 char challenge[10]; /*!< Authentication challenge */
162 int authenticated; /*!< Authentication status */
163 int readperm; /*!< Authorization for reading */
164 int writeperm; /*!< Authorization for writing */
165 char inbuf[1025]; /*!< Buffer */
166 /* we use the extra byte to add a '\0' and simplify parsing */
167 int inlen; /*!< number of buffered bytes */
168 int send_events; /*!< XXX what ? */
169 struct eventqent *last_ev; /*!< last event processed. */
170 int writetimeout; /*!< Timeout for ast_carefulwrite() */
171 AST_LIST_ENTRY(mansession) list;
174 #define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
176 static AST_LIST_HEAD_STATIC(sessions, mansession);
178 /*! \brief user descriptor, as read from the config file.
180 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
181 * lines which are not supported here, and readperm/writeperm/writetimeout
184 struct ast_manager_user {
187 struct ast_ha *ha; /*!< ACL setting */
188 int readperm; /*! Authorization for reading */
189 int writeperm; /*! Authorization for writing */
190 int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
191 int displayconnects; /*!< XXX unused */
192 int keep; /*!< mark entries created on a reload */
193 AST_RWLIST_ENTRY(ast_manager_user) list;
196 /*! \brief list of users found in the config file */
197 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
199 /*! \brief list of actions registered */
200 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
202 /*! \brief list of hooks registered */
203 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
205 /*! \brief Add a custom hook to be called when an event is fired */
206 void ast_manager_register_hook(struct manager_custom_hook *hook)
208 AST_RWLIST_WRLOCK(&manager_hooks);
209 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
210 AST_RWLIST_UNLOCK(&manager_hooks);
214 /*! \brief Delete a custom hook to be called when an event is fired */
215 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
217 AST_RWLIST_WRLOCK(&manager_hooks);
218 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
219 AST_RWLIST_UNLOCK(&manager_hooks);
224 * Event list management functions.
225 * We assume that the event list always has at least one element,
226 * and the delete code will not remove the last entry even if the
230 static time_t __deb(time_t start, const char *msg)
232 time_t now = time(NULL);
233 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
234 if (start != 0 && now - start > 5)
235 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
239 static void LOCK_EVENTS(void)
241 time_t start = __deb(0, "about to lock events");
242 AST_LIST_LOCK(&all_events);
243 __deb(start, "done lock events");
246 static void UNLOCK_EVENTS(void)
248 __deb(0, "about to unlock events");
249 AST_LIST_UNLOCK(&all_events);
252 static void LOCK_SESS(void)
254 time_t start = __deb(0, "about to lock sessions");
255 AST_LIST_LOCK(&sessions);
256 __deb(start, "done lock sessions");
259 static void UNLOCK_SESS(void)
261 __deb(0, "about to unlock sessions");
262 AST_LIST_UNLOCK(&sessions);
266 int check_manager_enabled()
268 return manager_enabled;
271 int check_webmanager_enabled()
273 return (webmanager_enabled && manager_enabled);
277 * Grab a reference to the last event, update usecount as needed.
278 * Can handle a NULL pointer.
280 static struct eventqent *grab_last(void)
282 struct eventqent *ret;
284 AST_LIST_LOCK(&all_events);
285 ret = AST_LIST_LAST(&all_events);
286 /* the list is never empty now, but may become so when
287 * we optimize it in the future, so be prepared.
290 ast_atomic_fetchadd_int(&ret->usecount, 1);
291 AST_LIST_UNLOCK(&all_events);
296 * Purge unused events. Remove elements from the head
297 * as long as their usecount is 0 and there is a next element.
299 static void purge_events(void)
301 struct eventqent *ev;
303 AST_LIST_LOCK(&all_events);
304 while ( (ev = AST_LIST_FIRST(&all_events)) &&
305 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
306 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
309 AST_LIST_UNLOCK(&all_events);
313 * helper functions to convert back and forth between
314 * string and numeric representation of set of flags
316 static struct permalias {
320 { EVENT_FLAG_SYSTEM, "system" },
321 { EVENT_FLAG_CALL, "call" },
322 { EVENT_FLAG_LOG, "log" },
323 { EVENT_FLAG_VERBOSE, "verbose" },
324 { EVENT_FLAG_COMMAND, "command" },
325 { EVENT_FLAG_AGENT, "agent" },
326 { EVENT_FLAG_USER, "user" },
327 { EVENT_FLAG_CONFIG, "config" },
328 { EVENT_FLAG_DTMF, "dtmf" },
329 { EVENT_FLAG_REPORTING, "reporting" },
330 { EVENT_FLAG_CDR, "cdr" },
331 { EVENT_FLAG_DIALPLAN, "dialplan" },
332 { EVENT_FLAG_ORIGINATE, "originate" },
337 /*! \brief Convert authority code to a list of options */
338 static char *authority_to_str(int authority, struct ast_str **res)
344 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
345 if (authority & perms[i].num) {
346 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
351 if ((*res)->used == 0) /* replace empty string with something sensible */
352 ast_str_append(res, 0, "<none>");
357 /*! Tells you if smallstr exists inside bigstr
358 which is delim by delim and uses no buf or stringsep
359 ast_instring("this|that|more","this",'|') == 1;
361 feel free to move this to app.c -anthm */
362 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
364 const char *val = bigstr, *next;
367 if ((next = strchr(val, delim))) {
368 if (!strncmp(val, smallstr, (next - val)))
373 return !strcmp(smallstr, val);
374 } while (*(val = (next + 1)));
379 static int get_perm(const char *instr)
386 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
387 if (ast_instring(instr, perms[x].label, ','))
395 * A number returns itself, false returns 0, true returns all flags,
396 * other strings return the flags that are set.
398 static int strings_to_mask(const char *string)
402 if (ast_strlen_zero(string))
405 for (p = string; *p; p++)
406 if (*p < '0' || *p > '9')
408 if (!p) /* all digits */
410 if (ast_false(string))
412 if (ast_true(string)) { /* all permissions */
414 for (x = 0; x<sizeof(perms) / sizeof(perms[0]); x++)
418 return get_perm(string);
421 static int check_manager_session_inuse(const char *name)
423 struct mansession *session = NULL;
425 AST_LIST_LOCK(&sessions);
426 AST_LIST_TRAVERSE(&sessions, session, list) {
427 if (!strcasecmp(session->username, name))
430 AST_LIST_UNLOCK(&sessions);
432 return session ? 1 : 0;
437 * lookup an entry in the list of registered users.
438 * must be called with the list lock held.
440 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
442 struct ast_manager_user *user = NULL;
444 AST_RWLIST_TRAVERSE(&users, user, list)
445 if (!strcasecmp(user->username, name))
450 /*! \brief Get displayconnects config option.
451 * \param s manager session to get parameter from.
452 * \return displayconnects config option value.
454 static int manager_displayconnects (struct mansession *s)
456 struct ast_manager_user *user = NULL;
459 AST_RWLIST_RDLOCK(&users);
460 if ((user = get_manager_by_name_locked (s->username)))
461 ret = user->displayconnects;
462 AST_RWLIST_UNLOCK(&users);
467 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
469 struct manager_action *cur;
470 struct ast_str *authority;
475 e->command = "manager show command";
477 "Usage: manager show command <actionname>\n"
478 " Shows the detailed description for a specific Asterisk manager interface command.\n";
483 AST_RWLIST_RDLOCK(&actions);
484 AST_RWLIST_TRAVERSE(&actions, cur, list) {
485 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
486 ret = ast_strdup(cur->action);
487 break; /* make sure we exit even if ast_strdup() returns NULL */
490 AST_RWLIST_UNLOCK(&actions);
493 authority = ast_str_alloca(80);
495 return CLI_SHOWUSAGE;
497 AST_RWLIST_RDLOCK(&actions);
498 AST_RWLIST_TRAVERSE(&actions, cur, list) {
499 for (num = 3; num < a->argc; num++) {
500 if (!strcasecmp(cur->action, a->argv[num])) {
501 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
502 cur->action, cur->synopsis,
503 authority_to_str(cur->authority, &authority),
504 S_OR(cur->description, ""));
508 AST_RWLIST_UNLOCK(&actions);
513 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
517 e->command = "manager debug [on|off]";
518 e->usage = "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
524 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
525 else if (a->argc == 3) {
526 if (!strcasecmp(a->argv[2], "on"))
528 else if (!strcasecmp(a->argv[2], "off"))
531 return CLI_SHOWUSAGE;
536 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
538 struct ast_manager_user *user = NULL;
541 struct ast_str *rauthority = ast_str_alloca(80);
542 struct ast_str *wauthority = ast_str_alloca(80);
546 e->command = "manager show user";
548 " Usage: manager show user <user>\n"
549 " Display all information related to the manager user specified.\n";
556 AST_RWLIST_RDLOCK(&users);
557 AST_RWLIST_TRAVERSE(&users, user, list) {
558 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
559 ret = ast_strdup(user->username);
563 AST_RWLIST_UNLOCK(&users);
568 return CLI_SHOWUSAGE;
570 AST_RWLIST_RDLOCK(&users);
572 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
573 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
574 AST_RWLIST_UNLOCK(&users);
578 ast_cli(a->fd, "\n");
585 "displayconnects: %s\n",
586 (user->username ? user->username : "(N/A)"),
587 (user->secret ? "<Set>" : "(N/A)"),
588 (user->ha ? "yes" : "no"),
589 authority_to_str(user->readperm, &rauthority),
590 authority_to_str(user->writeperm, &wauthority),
591 (user->displayconnects ? "yes" : "no"));
593 AST_RWLIST_UNLOCK(&users);
599 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
601 struct ast_manager_user *user = NULL;
605 e->command = "manager show users";
607 "Usage: manager show users\n"
608 " Prints a listing of all managers that are currently configured on that\n"
615 return CLI_SHOWUSAGE;
617 AST_RWLIST_RDLOCK(&users);
619 /* If there are no users, print out something along those lines */
620 if (AST_RWLIST_EMPTY(&users)) {
621 ast_cli(a->fd, "There are no manager users.\n");
622 AST_RWLIST_UNLOCK(&users);
626 ast_cli(a->fd, "\nusername\n--------\n");
628 AST_RWLIST_TRAVERSE(&users, user, list) {
629 ast_cli(a->fd, "%s\n", user->username);
633 AST_RWLIST_UNLOCK(&users);
635 ast_cli(a->fd, "-------------------\n");
636 ast_cli(a->fd, "%d manager users configured.\n", count_amu);
642 /*! \brief CLI command manager list commands */
643 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
645 struct manager_action *cur;
646 struct ast_str *authority;
647 static const char *format = " %-15.15s %-15.15s %-55.55s\n";
650 e->command = "manager show commands";
652 "Usage: manager show commands\n"
653 " Prints a listing of all the available Asterisk manager interface commands.\n";
658 authority = ast_str_alloca(80);
659 ast_cli(a->fd, format, "Action", "Privilege", "Synopsis");
660 ast_cli(a->fd, format, "------", "---------", "--------");
662 AST_RWLIST_RDLOCK(&actions);
663 AST_RWLIST_TRAVERSE(&actions, cur, list)
664 ast_cli(a->fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
665 AST_RWLIST_UNLOCK(&actions);
670 /*! \brief CLI command manager list connected */
671 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
673 struct mansession *s;
674 time_t now = time(NULL);
675 static const char *format = " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n";
676 static const char *format2 = " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n";
680 e->command = "manager show connected";
682 "Usage: manager show connected\n"
683 " Prints a listing of the users that are currently connected to the\n"
684 "Asterisk manager interface.\n";
690 ast_cli(a->fd, format, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
692 AST_LIST_LOCK(&sessions);
693 AST_LIST_TRAVERSE(&sessions, s, list) {
694 ast_cli(a->fd, format2, s->username, ast_inet_ntoa(s->sin.sin_addr), (int)(s->sessionstart), (int)(now - s->sessionstart), s->fd, s->inuse, s->readperm, s->writeperm);
697 AST_LIST_UNLOCK(&sessions);
699 ast_cli(a->fd, "%d users connected.\n", count);
704 /*! \brief CLI command manager list eventq */
705 /* Should change to "manager show connected" */
706 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
711 e->command = "manager show eventq";
713 "Usage: manager show eventq\n"
714 " Prints a listing of all events pending in the Asterisk manger\n"
720 AST_LIST_LOCK(&all_events);
721 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
722 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
723 ast_cli(a->fd, "Category: %d\n", s->category);
724 ast_cli(a->fd, "Event:\n%s", s->eventdata);
726 AST_LIST_UNLOCK(&all_events);
731 /*! \brief CLI command manager reload */
732 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
736 e->command = "manager reload";
738 "Usage: manager reload\n"
739 " Reloads the manager configuration.\n";
745 return CLI_SHOWUSAGE;
751 static struct ast_cli_entry cli_manager[] = {
752 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
753 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
754 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
755 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
756 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
757 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
758 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
759 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
763 * Decrement the usecount for the event; if it goes to zero,
764 * (why check for e->next ?) wakeup the
765 * main thread, which is in charge of freeing the record.
766 * Returns the next record.
768 static struct eventqent *unref_event(struct eventqent *e)
770 ast_atomic_fetchadd_int(&e->usecount, -1);
771 return AST_LIST_NEXT(e, eq_next);
774 static void ref_event(struct eventqent *e)
776 ast_atomic_fetchadd_int(&e->usecount, 1);
780 * destroy a session, leaving the usecount
782 static void free_session(struct mansession *s)
784 struct eventqent *eqe = s->last_ev;
787 ast_mutex_destroy(&s->__lock);
792 static void destroy_session(struct mansession *s)
794 AST_LIST_LOCK(&sessions);
795 AST_LIST_REMOVE(&sessions, s, list);
796 ast_atomic_fetchadd_int(&num_sessions, -1);
798 AST_LIST_UNLOCK(&sessions);
801 const char *astman_get_header(const struct message *m, char *var)
803 int x, l = strlen(var);
805 for (x = 0; x < m->hdrcount; x++) {
806 const char *h = m->headers[x];
807 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
814 struct ast_variable *astman_get_variables(const struct message *m)
817 struct ast_variable *head = NULL, *cur;
819 AST_DECLARE_APP_ARGS(args,
820 AST_APP_ARG(vars)[32];
823 varlen = strlen("Variable: ");
825 for (x = 0; x < m->hdrcount; x++) {
826 char *parse, *var, *val;
828 if (strncasecmp("Variable: ", m->headers[x], varlen))
830 parse = ast_strdupa(m->headers[x] + varlen);
832 AST_STANDARD_APP_ARGS(args, parse);
835 for (y = 0; y < args.argc; y++) {
838 var = val = ast_strdupa(args.vars[y]);
840 if (!val || ast_strlen_zero(var))
842 cur = ast_variable_new(var, val, "");
852 * helper function to send a string to the socket.
853 * Return -1 on error (e.g. buffer full).
855 static int send_string(struct mansession *s, char *string)
857 int len = strlen(string); /* residual length */
859 struct timeval start = ast_tvnow();
865 n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
866 if (n == len /* ok */ || n < 0 /* error */)
868 len -= n; /* skip already written data */
872 n = -1; /* error marker */
873 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
874 if (elapsed > s->writetimeout)
876 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
880 return n < 0 ? -1 : 0;
884 * \brief thread local buffer for astman_append
886 * \note This can not be defined within the astman_append() function
887 * because it declares a couple of functions that get used to
888 * initialize the thread local storage key.
890 AST_THREADSTORAGE(astman_append_buf);
891 /*! \brief initial allocated size for the astman_append_buf */
892 #define ASTMAN_APPEND_BUF_INITSIZE 256
895 * utility functions for creating AMI replies
897 void astman_append(struct mansession *s, const char *fmt, ...)
902 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
906 ast_str_set_va(&buf, 0, fmt, ap);
910 send_string(s, buf->str);
912 ast_verbose("fd == -1 in astman_append, should not happen\n");
915 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
916 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
917 hold the session lock _or_ be running in an action callback (in which case s->busy will
918 be non-zero). In either of these cases, there is no need to lock-protect the session's
919 fd, since no other output will be sent (events will be queued), and no input will
920 be read until either the current action finishes or get_input() obtains the session
924 /*! \brief send a response with an optional message,
925 * and terminate it with an empty line.
926 * m is used only to grab the 'ActionID' field.
928 * Use the explicit constant MSG_MOREDATA to remove the empty line.
929 * XXX MSG_MOREDATA should go to a header file.
931 #define MSG_MOREDATA ((char *)astman_send_response)
932 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
934 const char *id = astman_get_header(m, "ActionID");
936 astman_append(s, "Response: %s\r\n", resp);
937 if (!ast_strlen_zero(id))
938 astman_append(s, "ActionID: %s\r\n", id);
940 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
941 if (msg == MSG_MOREDATA)
944 astman_append(s, "Message: %s\r\n\r\n", msg);
946 astman_append(s, "\r\n");
949 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
951 astman_send_response_full(s, m, resp, msg, NULL);
954 void astman_send_error(struct mansession *s, const struct message *m, char *error)
956 astman_send_response_full(s, m, "Error", error, NULL);
959 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
961 astman_send_response_full(s, m, "Success", msg, NULL);
964 static void astman_start_ack(struct mansession *s, const struct message *m)
966 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
969 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
971 astman_send_response_full(s, m, "Success", msg, listflag);
976 Rather than braindead on,off this now can also accept a specific int mask value
977 or a ',' delim list of mask strings (the same as manager.conf) -anthm
979 static int set_eventmask(struct mansession *s, const char *eventmask)
981 int maskint = strings_to_mask(eventmask);
983 ast_mutex_lock(&s->__lock);
985 s->send_events = maskint;
986 ast_mutex_unlock(&s->__lock);
992 * Here we start with action_ handlers for AMI actions,
993 * and the internal functions used by them.
994 * Generally, the handlers are called action_foo()
997 /* helper function for action_login() */
998 static int authenticate(struct mansession *s, const struct message *m)
1000 const char *username = astman_get_header(m, "Username");
1001 const char *password = astman_get_header(m, "Secret");
1003 struct ast_manager_user *user = NULL;
1005 if (ast_strlen_zero(username)) /* missing username */
1008 /* locate user in locked state */
1009 AST_RWLIST_WRLOCK(&users);
1011 if (!(user = get_manager_by_name_locked(username))) {
1012 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1013 } else if (user->ha && !ast_apply_ha(user->ha, &(s->sin))) {
1014 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1015 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
1016 const char *key = astman_get_header(m, "Key");
1017 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) && user->secret) {
1020 char md5key[256] = "";
1021 struct MD5Context md5;
1022 unsigned char digest[16];
1025 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1026 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
1027 MD5Final(digest, &md5);
1028 for (x = 0; x < 16; x++)
1029 len += sprintf(md5key + len, "%2.2x", digest[x]);
1030 if (!strcmp(md5key, key))
1033 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
1034 S_OR(s->challenge, ""));
1036 } else if (password && user->secret && !strcmp(password, user->secret))
1040 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1041 AST_RWLIST_UNLOCK(&users);
1047 ast_copy_string(s->username, username, sizeof(s->username));
1048 s->readperm = user->readperm;
1049 s->writeperm = user->writeperm;
1050 s->writetimeout = user->writetimeout;
1051 s->sessionstart = time(NULL);
1052 set_eventmask(s, astman_get_header(m, "Events"));
1054 AST_RWLIST_UNLOCK(&users);
1058 /*! \brief Manager PING */
1059 static char mandescr_ping[] =
1060 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1061 " manager connection open.\n"
1062 "Variables: NONE\n";
1064 static int action_ping(struct mansession *s, const struct message *m)
1066 astman_send_response(s, m, "Success", "Ping: Pong\r\n");
1070 static char mandescr_getconfig[] =
1071 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1072 "file by category and contents or optionally by specified category only.\n"
1073 "Variables: (Names marked with * are required)\n"
1074 " *Filename: Configuration filename (e.g. foo.conf)\n"
1075 " Category: Category in configuration file\n";
1077 static int action_getconfig(struct mansession *s, const struct message *m)
1079 struct ast_config *cfg;
1080 const char *fn = astman_get_header(m, "Filename");
1081 const char *category = astman_get_header(m, "Category");
1084 char *cur_category = NULL;
1085 struct ast_variable *v;
1086 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1088 if (ast_strlen_zero(fn)) {
1089 astman_send_error(s, m, "Filename not specified");
1092 if (!(cfg = ast_config_load(fn, config_flags))) {
1093 astman_send_error(s, m, "Config file not found");
1097 astman_start_ack(s, m);
1098 while ((cur_category = ast_category_browse(cfg, cur_category))) {
1099 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
1101 astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
1102 for (v = ast_variable_browse(cfg, cur_category); v; v = v->next)
1103 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1107 if (!ast_strlen_zero(category) && catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1108 astman_append(s, "No categories found");
1109 ast_config_destroy(cfg);
1110 astman_append(s, "\r\n");
1115 static char mandescr_listcategories[] =
1116 "Description: A 'ListCategories' action will dump the categories in\n"
1119 " Filename: Configuration filename (e.g. foo.conf)\n";
1121 static int action_listcategories(struct mansession *s, const struct message *m)
1123 struct ast_config *cfg;
1124 const char *fn = astman_get_header(m, "Filename");
1125 char *category = NULL;
1126 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1129 if (ast_strlen_zero(fn)) {
1130 astman_send_error(s, m, "Filename not specified");
1133 if (!(cfg = ast_config_load(fn, config_flags))) {
1134 astman_send_error(s, m, "Config file not found or file has invalid syntax");
1137 astman_start_ack(s, m);
1138 while ((category = ast_category_browse(cfg, category))) {
1139 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1142 if (catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1143 astman_append(s, "Error: no categories found");
1144 ast_config_destroy(cfg);
1145 astman_append(s, "\r\n");
1153 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1154 static void json_escape(char *out, const char *in)
1157 if (*in == '\\' || *in == '\"')
1164 static char mandescr_getconfigjson[] =
1165 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1166 "file by category and contents in JSON format. This only makes sense to be used\n"
1167 "using rawman over the HTTP interface.\n"
1169 " Filename: Configuration filename (e.g. foo.conf)\n";
1171 static int action_getconfigjson(struct mansession *s, const struct message *m)
1173 struct ast_config *cfg;
1174 const char *fn = astman_get_header(m, "Filename");
1175 char *category = NULL;
1176 struct ast_variable *v;
1179 unsigned int buf_len = 0;
1180 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1182 if (ast_strlen_zero(fn)) {
1183 astman_send_error(s, m, "Filename not specified");
1187 if (!(cfg = ast_config_load(fn, config_flags))) {
1188 astman_send_error(s, m, "Config file not found");
1193 buf = alloca(buf_len);
1195 astman_start_ack(s, m);
1196 astman_append(s, "JSON: {");
1197 while ((category = ast_category_browse(cfg, category))) {
1199 if (buf_len < 2 * strlen(category) + 1) {
1201 buf = alloca(buf_len);
1203 json_escape(buf, category);
1204 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1207 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1209 astman_append(s, ",");
1210 if (buf_len < 2 * strlen(v->name) + 1) {
1212 buf = alloca(buf_len);
1214 json_escape(buf, v->name);
1215 astman_append(s, "\"%s", buf);
1216 if (buf_len < 2 * strlen(v->value) + 1) {
1218 buf = alloca(buf_len);
1220 json_escape(buf, v->value);
1221 astman_append(s, "%s\"", buf);
1225 astman_append(s, "]");
1227 astman_append(s, "}\r\n\r\n");
1229 ast_config_destroy(cfg);
1234 /* helper function for action_updateconfig */
1235 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1239 const char *action, *cat, *var, *value, *match, *line;
1240 struct ast_category *category;
1241 struct ast_variable *v;
1243 for (x = 0; x < 100000; x++) {
1244 unsigned int object = 0;
1246 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1247 action = astman_get_header(m, hdr);
1248 if (ast_strlen_zero(action))
1250 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1251 cat = astman_get_header(m, hdr);
1252 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1253 var = astman_get_header(m, hdr);
1254 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1255 value = astman_get_header(m, hdr);
1256 if (!ast_strlen_zero(value) && *value == '>') {
1260 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1261 match = astman_get_header(m, hdr);
1262 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
1263 line = astman_get_header(m, hdr);
1264 if (!strcasecmp(action, "newcat")) {
1265 if (ast_strlen_zero(cat))
1266 return UNSPECIFIED_CATEGORY;
1267 if (!(category = ast_category_new(cat, dfn, -1)))
1268 return FAILURE_ALLOCATION;
1269 if (ast_strlen_zero(match)) {
1270 ast_category_append(cfg, category);
1272 ast_category_insert(cfg, category, match);
1273 } else if (!strcasecmp(action, "renamecat")) {
1274 if (ast_strlen_zero(cat) || ast_strlen_zero(value))
1275 return UNSPECIFIED_ARGUMENT;
1276 if (!(category = ast_category_get(cfg, cat)))
1277 return UNKNOWN_CATEGORY;
1278 ast_category_rename(category, value);
1279 } else if (!strcasecmp(action, "delcat")) {
1280 if (ast_strlen_zero(cat))
1281 return UNSPECIFIED_CATEGORY;
1282 if (ast_category_delete(cfg, cat))
1283 return FAILURE_DELCAT;
1284 } else if (!strcasecmp(action, "emptycat")) {
1285 if (ast_strlen_zero(cat))
1286 return UNSPECIFIED_CATEGORY;
1287 if (ast_category_empty(cfg, cat))
1288 return FAILURE_EMPTYCAT;
1289 } else if (!strcasecmp(action, "update")) {
1290 if (ast_strlen_zero(cat) || ast_strlen_zero(var))
1291 return UNSPECIFIED_ARGUMENT;
1292 if (!(category = ast_category_get(cfg,cat)))
1293 return UNKNOWN_CATEGORY;
1294 if (ast_variable_update(category, var, value, match, object))
1295 return FAILURE_UPDATE;
1296 } else if (!strcasecmp(action, "delete")) {
1297 if (ast_strlen_zero(cat) || (ast_strlen_zero(var) && ast_strlen_zero(line)))
1298 return UNSPECIFIED_ARGUMENT;
1299 if (!(category = ast_category_get(cfg, cat)))
1300 return UNKNOWN_CATEGORY;
1301 if (ast_variable_delete(category, var, match, line))
1302 return FAILURE_DELETE;
1303 } else if (!strcasecmp(action, "append")) {
1304 if (ast_strlen_zero(cat) || ast_strlen_zero(var))
1305 return UNSPECIFIED_ARGUMENT;
1306 if (!(category = ast_category_get(cfg, cat)))
1307 return UNKNOWN_CATEGORY;
1308 if (!(v = ast_variable_new(var, value, dfn)))
1309 return FAILURE_ALLOCATION;
1310 if (object || (match && !strcasecmp(match, "object")))
1312 ast_variable_append(category, v);
1313 } else if (!strcasecmp(action, "insert")) {
1314 if (ast_strlen_zero(cat) || ast_strlen_zero(var) || ast_strlen_zero(line))
1315 return UNSPECIFIED_ARGUMENT;
1316 if (!(category = ast_category_get(cfg, cat)))
1317 return UNKNOWN_CATEGORY;
1318 if (!(v = ast_variable_new(var, value, dfn)))
1319 return FAILURE_ALLOCATION;
1320 ast_variable_insert(category, v, line);
1323 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
1324 return UNKNOWN_ACTION;
1330 static char mandescr_updateconfig[] =
1331 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1332 "configuration elements in Asterisk configuration files.\n"
1333 "Variables (X's represent 6 digit number beginning with 000000):\n"
1334 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1335 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1336 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1337 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,EmptyCat,Update,Delete,Append,Insert)\n"
1338 " Cat-XXXXXX: Category to operate on\n"
1339 " Var-XXXXXX: Variable to work on\n"
1340 " Value-XXXXXX: Value to work on\n"
1341 " Match-XXXXXX: Extra match required to match line\n"
1342 " Line-XXXXXX: Line in category to operate on (used with delete and insert actions)\n";
1344 static int action_updateconfig(struct mansession *s, const struct message *m)
1346 struct ast_config *cfg;
1347 const char *sfn = astman_get_header(m, "SrcFilename");
1348 const char *dfn = astman_get_header(m, "DstFilename");
1350 const char *rld = astman_get_header(m, "Reload");
1351 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1352 enum error_type result;
1354 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1355 astman_send_error(s, m, "Filename not specified");
1358 if (!(cfg = ast_config_load(sfn, config_flags))) {
1359 astman_send_error(s, m, "Config file not found");
1362 result = handle_updates(s, m, cfg, dfn);
1364 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1365 res = config_text_file_save(dfn, cfg, "Manager");
1366 ast_config_destroy(cfg);
1368 astman_send_error(s, m, "Save of config failed");
1371 astman_send_ack(s, m, NULL);
1372 if (!ast_strlen_zero(rld)) {
1375 ast_module_reload(rld);
1378 ast_config_destroy(cfg);
1380 case UNKNOWN_ACTION:
1381 astman_send_error(s, m, "Unknown action command");
1383 case UNKNOWN_CATEGORY:
1384 astman_send_error(s, m, "Given category does not exist");
1386 case UNSPECIFIED_CATEGORY:
1387 astman_send_error(s, m, "Category not specified");
1389 case UNSPECIFIED_ARGUMENT:
1390 astman_send_error(s, m, "Problem with category, value, or line (if required)");
1392 case FAILURE_ALLOCATION:
1393 astman_send_error(s, m, "Memory allocation failure, this should not happen");
1395 case FAILURE_DELCAT:
1396 astman_send_error(s, m, "Delete category did not complete successfully");
1398 case FAILURE_EMPTYCAT:
1399 astman_send_error(s, m, "Empty category did not complete successfully");
1401 case FAILURE_UPDATE:
1402 astman_send_error(s, m, "Update did not complete successfully");
1404 case FAILURE_DELETE:
1405 astman_send_error(s, m, "Delete did not complete successfully");
1407 case FAILURE_APPEND:
1408 astman_send_error(s, m, "Append did not complete successfully");
1415 static char mandescr_createconfig[] =
1416 "Description: A 'CreateConfig' action will create an empty file in the\n"
1417 "configuration directory. This action is intended to be used before an\n"
1418 "UpdateConfig action.\n"
1420 " Filename: The configuration filename to create (e.g. foo.conf)\n";
1422 static int action_createconfig(struct mansession *s, const struct message *m)
1425 const char *fn = astman_get_header(m, "Filename");
1426 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
1427 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
1428 ast_str_append(&filepath, 0, "%s", fn);
1430 if ((fd = open(filepath->str, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
1432 astman_send_ack(s, m, "New configuration file created successfully");
1434 astman_send_error(s, m, strerror(errno));
1439 /*! \brief Manager WAITEVENT */
1440 static char mandescr_waitevent[] =
1441 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1442 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1443 "session, events will be generated and queued.\n"
1445 " Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1447 static int action_waitevent(struct mansession *s, const struct message *m)
1449 const char *timeouts = astman_get_header(m, "Timeout");
1453 const char *id = astman_get_header(m, "ActionID");
1456 if (!ast_strlen_zero(id))
1457 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1461 if (!ast_strlen_zero(timeouts)) {
1462 sscanf(timeouts, "%i", &timeout);
1465 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1468 ast_mutex_lock(&s->__lock);
1469 if (s->waiting_thread != AST_PTHREADT_NULL)
1470 pthread_kill(s->waiting_thread, SIGURG);
1472 if (s->managerid) { /* AMI-over-HTTP session */
1474 * Make sure the timeout is within the expire time of the session,
1475 * as the client will likely abort the request if it does not see
1476 * data coming after some amount of time.
1478 time_t now = time(NULL);
1479 int max = s->sessiontimeout - now - 10;
1481 if (max < 0) /* We are already late. Strange but possible. */
1483 if (timeout < 0 || timeout > max)
1485 if (!s->send_events) /* make sure we record events */
1486 s->send_events = -1;
1488 ast_mutex_unlock(&s->__lock);
1490 /* XXX should this go inside the lock ? */
1491 s->waiting_thread = pthread_self(); /* let new events wake up this thread */
1492 ast_debug(1, "Starting waiting for an event!\n");
1494 for (x = 0; x < timeout || timeout < 0; x++) {
1495 ast_mutex_lock(&s->__lock);
1498 /* We can have multiple HTTP session point to the same mansession entry.
1499 * The way we deal with it is not very nice: newcomers kick out the previous
1500 * HTTP session. XXX this needs to be improved.
1502 if (s->waiting_thread != pthread_self())
1506 ast_mutex_unlock(&s->__lock);
1509 if (s->managerid == 0) { /* AMI session */
1510 if (ast_wait_for_input(s->fd, 1000))
1512 } else { /* HTTP session */
1516 ast_debug(1, "Finished waiting for an event!\n");
1517 ast_mutex_lock(&s->__lock);
1518 if (s->waiting_thread == pthread_self()) {
1519 struct eventqent *eqe;
1520 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1521 while ( (eqe = NEW_EVENT(s)) ) {
1523 if (((s->readperm & eqe->category) == eqe->category) &&
1524 ((s->send_events & eqe->category) == eqe->category)) {
1525 astman_append(s, "%s", eqe->eventdata);
1527 s->last_ev = unref_event(s->last_ev);
1530 "Event: WaitEventComplete\r\n"
1533 s->waiting_thread = AST_PTHREADT_NULL;
1535 ast_debug(1, "Abandoning event request!\n");
1537 ast_mutex_unlock(&s->__lock);
1541 static char mandescr_listcommands[] =
1542 "Description: Returns the action name and synopsis for every\n"
1543 " action that is available to the user\n"
1544 "Variables: NONE\n";
1546 /*! \note The actionlock is read-locked by the caller of this function */
1547 static int action_listcommands(struct mansession *s, const struct message *m)
1549 struct manager_action *cur;
1550 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1552 astman_start_ack(s, m);
1553 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1554 if (s->writeperm & cur->authority || cur->authority == 0)
1555 astman_append(s, "%s: %s (Priv: %s)\r\n",
1556 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1558 astman_append(s, "\r\n");
1563 static char mandescr_events[] =
1564 "Description: Enable/Disable sending of events to this manager\n"
1567 " EventMask: 'on' if all events should be sent,\n"
1568 " 'off' if no events should be sent,\n"
1569 " 'system,call,log' to select which flags events should have to be sent.\n";
1571 static int action_events(struct mansession *s, const struct message *m)
1573 const char *mask = astman_get_header(m, "EventMask");
1576 res = set_eventmask(s, mask);
1578 astman_send_response(s, m, "Success", "Events: On\r\n");
1580 astman_send_response(s, m, "Success", "Events: Off\r\n");
1585 static char mandescr_logoff[] =
1586 "Description: Logoff this manager session\n"
1587 "Variables: NONE\n";
1589 static int action_logoff(struct mansession *s, const struct message *m)
1591 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1595 static int action_login(struct mansession *s, const struct message *m)
1597 if (authenticate(s, m)) {
1599 astman_send_error(s, m, "Authentication failed");
1602 s->authenticated = 1;
1603 if (manager_displayconnects(s))
1604 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1605 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1606 astman_send_ack(s, m, "Authentication accepted");
1610 static int action_challenge(struct mansession *s, const struct message *m)
1612 const char *authtype = astman_get_header(m, "AuthType");
1614 if (!strcasecmp(authtype, "MD5")) {
1615 if (ast_strlen_zero(s->challenge))
1616 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1617 ast_mutex_lock(&s->__lock);
1618 astman_start_ack(s, m);
1619 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1620 ast_mutex_unlock(&s->__lock);
1622 astman_send_error(s, m, "Must specify AuthType");
1627 static char mandescr_hangup[] =
1628 "Description: Hangup a channel\n"
1630 " Channel: The channel name to be hungup\n";
1632 static int action_hangup(struct mansession *s, const struct message *m)
1634 struct ast_channel *c = NULL;
1635 const char *name = astman_get_header(m, "Channel");
1636 if (ast_strlen_zero(name)) {
1637 astman_send_error(s, m, "No channel specified");
1640 c = ast_get_channel_by_name_locked(name);
1642 astman_send_error(s, m, "No such channel");
1645 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1646 ast_channel_unlock(c);
1647 astman_send_ack(s, m, "Channel Hungup");
1651 static char mandescr_setvar[] =
1652 "Description: Set a global or local channel variable.\n"
1653 "Variables: (Names marked with * are required)\n"
1654 " Channel: Channel to set variable for\n"
1655 " *Variable: Variable name\n"
1658 static int action_setvar(struct mansession *s, const struct message *m)
1660 struct ast_channel *c = NULL;
1661 const char *name = astman_get_header(m, "Channel");
1662 const char *varname = astman_get_header(m, "Variable");
1663 const char *varval = astman_get_header(m, "Value");
1665 if (ast_strlen_zero(varname)) {
1666 astman_send_error(s, m, "No variable specified");
1670 if (!ast_strlen_zero(name)) {
1671 c = ast_get_channel_by_name_locked(name);
1673 astman_send_error(s, m, "No such channel");
1678 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1681 ast_channel_unlock(c);
1683 astman_send_ack(s, m, "Variable Set");
1688 static char mandescr_getvar[] =
1689 "Description: Get the value of a global or local channel variable.\n"
1690 "Variables: (Names marked with * are required)\n"
1691 " Channel: Channel to read variable from\n"
1692 " *Variable: Variable name\n"
1693 " ActionID: Optional Action id for message matching.\n";
1695 static int action_getvar(struct mansession *s, const struct message *m)
1697 struct ast_channel *c = NULL;
1698 const char *name = astman_get_header(m, "Channel");
1699 const char *varname = astman_get_header(m, "Variable");
1701 char workspace[1024] = "";
1703 if (ast_strlen_zero(varname)) {
1704 astman_send_error(s, m, "No variable specified");
1708 if (!ast_strlen_zero(name)) {
1709 c = ast_get_channel_by_name_locked(name);
1711 astman_send_error(s, m, "No such channel");
1716 if (varname[strlen(varname) - 1] == ')') {
1717 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1720 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1724 ast_channel_unlock(c);
1725 astman_start_ack(s, m);
1726 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1732 /*! \brief Manager "status" command to show channels */
1733 /* Needs documentation... */
1734 static int action_status(struct mansession *s, const struct message *m)
1736 const char *name = astman_get_header(m, "Channel");
1737 struct ast_channel *c;
1739 struct timeval now = ast_tvnow();
1740 long elapsed_seconds = 0;
1742 int all = ast_strlen_zero(name); /* set if we want all channels */
1743 const char *id = astman_get_header(m, "ActionID");
1746 if (!ast_strlen_zero(id))
1747 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1752 c = ast_channel_walk_locked(NULL);
1754 c = ast_get_channel_by_name_locked(name);
1756 astman_send_error(s, m, "No such channel");
1760 astman_send_ack(s, m, "Channel status will follow");
1762 /* if we look by name, we break after the first iteration */
1766 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1771 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1775 "Privilege: Call\r\n"
1777 "CallerIDNum: %s\r\n"
1778 "CallerIDName: %s\r\n"
1779 "Accountcode: %s\r\n"
1780 "ChannelState: %d\r\n"
1781 "ChannelStateDesc: %s\r\n"
1791 S_OR(c->cid.cid_num, ""),
1792 S_OR(c->cid.cid_name, ""),
1795 ast_state2str(c->_state), c->context,
1796 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1800 "Privilege: Call\r\n"
1802 "CallerIDNum: %s\r\n"
1803 "CallerIDName: %s\r\n"
1811 S_OR(c->cid.cid_num, "<unknown>"),
1812 S_OR(c->cid.cid_name, "<unknown>"),
1814 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1816 ast_channel_unlock(c);
1819 c = ast_channel_walk_locked(c);
1822 "Event: StatusComplete\r\n"
1825 "\r\n", idText, channels);
1829 static char mandescr_sendtext[] =
1830 "Description: Sends A Text Message while in a call.\n"
1831 "Variables: (Names marked with * are required)\n"
1832 " *Channel: Channel to send message to\n"
1833 " *Message: Message to send\n"
1834 " ActionID: Optional Action id for message matching.\n";
1836 static int action_sendtext(struct mansession *s, const struct message *m)
1838 struct ast_channel *c = NULL;
1839 const char *name = astman_get_header(m, "Channel");
1840 const char *textmsg = astman_get_header(m, "Message");
1843 if (ast_strlen_zero(name)) {
1844 astman_send_error(s, m, "No channel specified");
1848 if (ast_strlen_zero(textmsg)) {
1849 astman_send_error(s, m, "No Message specified");
1853 c = ast_get_channel_by_name_locked(name);
1855 astman_send_error(s, m, "No such channel");
1859 res = ast_sendtext(c, textmsg);
1860 ast_channel_unlock(c);
1863 astman_send_ack(s, m, "Success");
1865 astman_send_error(s, m, "Failure");
1870 static char mandescr_redirect[] =
1871 "Description: Redirect (transfer) a call.\n"
1872 "Variables: (Names marked with * are required)\n"
1873 " *Channel: Channel to redirect\n"
1874 " ExtraChannel: Second call leg to transfer (optional)\n"
1875 " *Exten: Extension to transfer to\n"
1876 " *Context: Context to transfer to\n"
1877 " *Priority: Priority to transfer to\n"
1878 " ActionID: Optional Action id for message matching.\n";
1880 /*! \brief action_redirect: The redirect manager command */
1881 static int action_redirect(struct mansession *s, const struct message *m)
1883 const char *name = astman_get_header(m, "Channel");
1884 const char *name2 = astman_get_header(m, "ExtraChannel");
1885 const char *exten = astman_get_header(m, "Exten");
1886 const char *context = astman_get_header(m, "Context");
1887 const char *priority = astman_get_header(m, "Priority");
1888 struct ast_channel *chan, *chan2 = NULL;
1892 if (ast_strlen_zero(name)) {
1893 astman_send_error(s, m, "Channel not specified");
1896 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1897 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1898 astman_send_error(s, m, "Invalid priority\n");
1902 /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1903 chan = ast_get_channel_by_name_locked(name);
1906 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1907 astman_send_error(s, m, buf);
1910 if (ast_check_hangup(chan)) {
1911 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1912 ast_channel_unlock(chan);
1915 if (!ast_strlen_zero(name2))
1916 chan2 = ast_get_channel_by_name_locked(name2);
1917 if (chan2 && ast_check_hangup(chan2)) {
1918 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1919 ast_channel_unlock(chan);
1920 ast_channel_unlock(chan2);
1923 res = ast_async_goto(chan, context, exten, pi);
1925 if (!ast_strlen_zero(name2)) {
1927 res = ast_async_goto(chan2, context, exten, pi);
1931 astman_send_ack(s, m, "Dual Redirect successful");
1933 astman_send_error(s, m, "Secondary redirect failed");
1935 astman_send_ack(s, m, "Redirect successful");
1937 astman_send_error(s, m, "Redirect failed");
1939 ast_channel_unlock(chan);
1941 ast_channel_unlock(chan2);
1945 static char mandescr_atxfer[] =
1946 "Description: Attended transfer.\n"
1947 "Variables: (Names marked with * are required)\n"
1948 " *Channel: Transferer's channel\n"
1949 " *Exten: Extension to transfer to\n"
1950 " *Context: Context to transfer to\n"
1951 " *Priority: Priority to transfer to\n"
1952 " ActionID: Optional Action id for message matching.\n";
1954 static int action_atxfer(struct mansession *s, const struct message *m)
1956 const char *name = astman_get_header(m, "Channel");
1957 const char *exten = astman_get_header(m, "Exten");
1958 const char *context = astman_get_header(m, "Context");
1959 const char *priority = astman_get_header(m, "Priority");
1960 struct ast_channel *chan = NULL;
1961 struct ast_call_feature *atxfer_feature = NULL;
1962 char *feature_code = NULL;
1963 int priority_int = 0;
1965 if (ast_strlen_zero(name)) {
1966 astman_send_error(s, m, "No channel specified\n");
1969 if (ast_strlen_zero(exten)) {
1970 astman_send_error(s, m, "No extension specified\n");
1973 if (ast_strlen_zero(context)) {
1974 astman_send_error(s, m, "No context specified\n");
1977 if (ast_strlen_zero(priority)) {
1978 astman_send_error(s, m, "No priority specified\n");
1982 if (sscanf(priority, "%d", &priority_int) != 1 && (priority_int = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1983 astman_send_error(s, m, "Invalid Priority\n");
1987 if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
1988 astman_send_error(s, m, "No attended transfer feature found\n");
1992 if (!(chan = ast_get_channel_by_name_locked(name))) {
1993 astman_send_error(s, m, "Channel specified does not exist\n");
1997 for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
1998 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
1999 ast_queue_frame(chan, &f);
2002 for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
2003 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2004 ast_queue_frame(chan, &f);
2007 astman_send_ack(s, m, "Atxfer successfully queued\n");
2008 ast_channel_unlock(chan);
2013 static char mandescr_command[] =
2014 "Description: Run a CLI command.\n"
2015 "Variables: (Names marked with * are required)\n"
2016 " *Command: Asterisk CLI command to run\n"
2017 " ActionID: Optional Action id for message matching.\n";
2019 /*! \brief Manager command "command" - execute CLI command */
2020 static int action_command(struct mansession *s, const struct message *m)
2022 const char *cmd = astman_get_header(m, "Command");
2023 const char *id = astman_get_header(m, "ActionID");
2024 char *buf, *final_buf;
2025 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
2026 int fd = mkstemp(template), i = 0;
2029 for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
2030 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
2031 astman_send_error(s, m, "Command blacklisted");
2036 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
2037 if (!ast_strlen_zero(id))
2038 astman_append(s, "ActionID: %s\r\n", id);
2039 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
2040 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
2041 l = lseek(fd, 0, SEEK_END); /* how many chars available */
2043 /* This has a potential to overflow the stack. Hence, use the heap. */
2044 buf = ast_calloc(1, l + 1);
2045 final_buf = ast_calloc(1, l + 1);
2047 lseek(fd, 0, SEEK_SET);
2051 term_strip(final_buf, buf, l);
2052 final_buf[l] = '\0';
2054 astman_append(s, S_OR(final_buf, buf));
2059 astman_append(s, "--END COMMAND--\r\n\r\n");
2061 ast_free(final_buf);
2065 /* helper function for originate */
2066 struct fast_originate_helper {
2067 char tech[AST_MAX_EXTENSION];
2068 char data[AST_MAX_EXTENSION];
2070 char app[AST_MAX_APP];
2071 char appdata[AST_MAX_EXTENSION];
2072 char cid_name[AST_MAX_EXTENSION];
2073 char cid_num[AST_MAX_EXTENSION];
2074 char context[AST_MAX_CONTEXT];
2075 char exten[AST_MAX_EXTENSION];
2076 char idtext[AST_MAX_EXTENSION];
2077 char account[AST_MAX_ACCOUNT_CODE];
2079 struct ast_variable *vars;
2082 static void *fast_originate(void *data)
2084 struct fast_originate_helper *in = data;
2087 struct ast_channel *chan = NULL;
2088 char requested_channel[AST_CHANNEL_NAME];
2090 if (!ast_strlen_zero(in->app)) {
2091 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
2092 S_OR(in->cid_num, NULL),
2093 S_OR(in->cid_name, NULL),
2094 in->vars, in->account, &chan);
2096 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
2097 S_OR(in->cid_num, NULL),
2098 S_OR(in->cid_name, NULL),
2099 in->vars, in->account, &chan);
2103 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
2104 /* Tell the manager what happened with the channel */
2105 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
2113 "CallerIDNum: %s\r\n"
2114 "CallerIDName: %s\r\n",
2115 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
2116 chan ? chan->uniqueid : "<null>",
2117 S_OR(in->cid_num, "<unknown>"),
2118 S_OR(in->cid_name, "<unknown>")
2121 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2123 ast_channel_unlock(chan);
2128 static char mandescr_originate[] =
2129 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
2130 " Application/Data\n"
2131 "Variables: (Names marked with * are required)\n"
2132 " *Channel: Channel name to call\n"
2133 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
2134 " Context: Context to use (requires 'Exten' and 'Priority')\n"
2135 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
2136 " Application: Application to use\n"
2137 " Data: Data to use (requires 'Application')\n"
2138 " Timeout: How long to wait for call to be answered (in ms)\n"
2139 " CallerID: Caller ID to be set on the outgoing channel\n"
2140 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
2141 " Account: Account code\n"
2142 " Async: Set to 'true' for fast origination\n";
2144 static int action_originate(struct mansession *s, const struct message *m)
2146 const char *name = astman_get_header(m, "Channel");
2147 const char *exten = astman_get_header(m, "Exten");
2148 const char *context = astman_get_header(m, "Context");
2149 const char *priority = astman_get_header(m, "Priority");
2150 const char *timeout = astman_get_header(m, "Timeout");
2151 const char *callerid = astman_get_header(m, "CallerID");
2152 const char *account = astman_get_header(m, "Account");
2153 const char *app = astman_get_header(m, "Application");
2154 const char *appdata = astman_get_header(m, "Data");
2155 const char *async = astman_get_header(m, "Async");
2156 const char *id = astman_get_header(m, "ActionID");
2157 struct ast_variable *vars = astman_get_variables(m);
2159 char *l = NULL, *n = NULL;
2169 astman_send_error(s, m, "Channel not specified");
2172 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2173 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2174 astman_send_error(s, m, "Invalid priority\n");
2178 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2179 astman_send_error(s, m, "Invalid timeout\n");
2182 ast_copy_string(tmp, name, sizeof(tmp));
2184 data = strchr(tmp, '/');
2186 astman_send_error(s, m, "Invalid channel\n");
2190 ast_copy_string(tmp2, callerid, sizeof(tmp2));
2191 ast_callerid_parse(tmp2, &n, &l);
2193 if (ast_strlen_zero(n))
2197 ast_shrink_phone_number(l);
2198 if (ast_strlen_zero(l))
2201 if (ast_true(async)) {
2202 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2206 if (!ast_strlen_zero(id))
2207 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2208 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2209 ast_copy_string(fast->data, data, sizeof(fast->data));
2210 ast_copy_string(fast->app, app, sizeof(fast->app));
2211 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2213 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2215 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2217 ast_copy_string(fast->context, context, sizeof(fast->context));
2218 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2219 ast_copy_string(fast->account, account, sizeof(fast->account));
2221 fast->priority = pi;
2222 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2228 } else if (!ast_strlen_zero(app)) {
2229 /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
2230 if (!(s->writeperm & EVENT_FLAG_SYSTEM)
2232 strcasestr(app, "system") == 0 || /* System(rm -rf /)
2233 TrySystem(rm -rf /) */
2234 strcasestr(app, "exec") || /* Exec(System(rm -rf /))
2235 TryExec(System(rm -rf /)) */
2236 strcasestr(app, "agi") || /* AGI(/bin/rm,-rf /)
2237 EAGI(/bin/rm,-rf /) */
2238 strstr(appdata, "SHELL") || /* NoOp(${SHELL(rm -rf /)}) */
2239 strstr(appdata, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
2241 astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
2244 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2246 if (exten && context && pi)
2247 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2249 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2254 astman_send_ack(s, m, "Originate successfully queued");
2256 astman_send_error(s, m, "Originate failed");
2260 /*! \brief Help text for manager command mailboxstatus
2262 static char mandescr_mailboxstatus[] =
2263 "Description: Checks a voicemail account for status.\n"
2264 "Variables: (Names marked with * are required)\n"
2265 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2266 " ActionID: Optional ActionID for message matching.\n"
2267 "Returns number of messages.\n"
2268 " Message: Mailbox Status\n"
2269 " Mailbox: <mailboxid>\n"
2270 " Waiting: <count>\n"
2273 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2275 const char *mailbox = astman_get_header(m, "Mailbox");
2278 if (ast_strlen_zero(mailbox)) {
2279 astman_send_error(s, m, "Mailbox not specified");
2282 ret = ast_app_has_voicemail(mailbox, NULL);
2283 astman_start_ack(s, m);
2284 astman_append(s, "Message: Mailbox Status\r\n"
2286 "Waiting: %d\r\n\r\n", mailbox, ret);
2290 static char mandescr_mailboxcount[] =
2291 "Description: Checks a voicemail account for new messages.\n"
2292 "Variables: (Names marked with * are required)\n"
2293 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2294 " ActionID: Optional ActionID for message matching.\n"
2295 "Returns number of new and old messages.\n"
2296 " Message: Mailbox Message Count\n"
2297 " Mailbox: <mailboxid>\n"
2298 " NewMessages: <count>\n"
2299 " OldMessages: <count>\n"
2301 static int action_mailboxcount(struct mansession *s, const struct message *m)
2303 const char *mailbox = astman_get_header(m, "Mailbox");
2304 int newmsgs = 0, oldmsgs = 0;
2306 if (ast_strlen_zero(mailbox)) {
2307 astman_send_error(s, m, "Mailbox not specified");
2310 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2311 astman_start_ack(s, m);
2312 astman_append(s, "Message: Mailbox Message Count\r\n"
2314 "NewMessages: %d\r\n"
2315 "OldMessages: %d\r\n"
2317 mailbox, newmsgs, oldmsgs);
2321 static char mandescr_extensionstate[] =
2322 "Description: Report the extension state for given extension.\n"
2323 " If the extension has a hint, will use devicestate to check\n"
2324 " the status of the device connected to the extension.\n"
2325 "Variables: (Names marked with * are required)\n"
2326 " *Exten: Extension to check state on\n"
2327 " *Context: Context for extension\n"
2328 " ActionId: Optional ID for this transaction\n"
2329 "Will return an \"Extension Status\" message.\n"
2330 "The response will include the hint for the extension and the status.\n";
2332 static int action_extensionstate(struct mansession *s, const struct message *m)
2334 const char *exten = astman_get_header(m, "Exten");
2335 const char *context = astman_get_header(m, "Context");
2336 char hint[256] = "";
2338 if (ast_strlen_zero(exten)) {
2339 astman_send_error(s, m, "Extension not specified");
2342 if (ast_strlen_zero(context))
2343 context = "default";
2344 status = ast_extension_state(NULL, context, exten);
2345 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2346 astman_start_ack(s, m);
2347 astman_append(s, "Message: Extension Status\r\n"
2351 "Status: %d\r\n\r\n",
2352 exten, context, hint, status);
2356 static char mandescr_timeout[] =
2357 "Description: Hangup a channel after a certain time.\n"
2358 "Variables: (Names marked with * are required)\n"
2359 " *Channel: Channel name to hangup\n"
2360 " *Timeout: Maximum duration of the call (sec)\n"
2361 "Acknowledges set time with 'Timeout Set' message\n";
2363 static int action_timeout(struct mansession *s, const struct message *m)
2365 struct ast_channel *c;
2366 const char *name = astman_get_header(m, "Channel");
2367 int timeout = atoi(astman_get_header(m, "Timeout"));
2369 if (ast_strlen_zero(name)) {
2370 astman_send_error(s, m, "No channel specified");
2374 astman_send_error(s, m, "No timeout specified");
2377 c = ast_get_channel_by_name_locked(name);
2379 astman_send_error(s, m, "No such channel");
2382 ast_channel_setwhentohangup(c, timeout);
2383 ast_channel_unlock(c);
2384 astman_send_ack(s, m, "Timeout Set");
2389 * Send any applicable events to the client listening on this socket.
2390 * Wait only for a finite time on each event, and drop all events whether
2391 * they are successfully sent or not.
2393 static int process_events(struct mansession *s)
2397 ast_mutex_lock(&s->__lock);
2399 struct eventqent *eqe;
2401 while ( (eqe = NEW_EVENT(s)) ) {
2403 if (!ret && s->authenticated &&
2404 (s->readperm & eqe->category) == eqe->category &&
2405 (s->send_events & eqe->category) == eqe->category) {
2406 if (send_string(s, eqe->eventdata) < 0)
2407 ret = -1; /* don't send more */
2409 s->last_ev = unref_event(s->last_ev);
2412 ast_mutex_unlock(&s->__lock);
2416 static char mandescr_userevent[] =
2417 "Description: Send an event to manager sessions.\n"
2418 "Variables: (Names marked with * are required)\n"
2419 " *UserEvent: EventStringToSend\n"
2420 " Header1: Content1\n"
2421 " HeaderN: ContentN\n";
2423 static int action_userevent(struct mansession *s, const struct message *m)
2425 const char *event = astman_get_header(m, "UserEvent");
2426 char body[2048] = "";
2428 for (x = 0; x < m->hdrcount; x++) {
2429 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2430 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2431 bodylen += strlen(m->headers[x]);
2432 ast_copy_string(body + bodylen, "\r\n", 3);
2437 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2441 static char mandescr_coresettings[] =
2442 "Description: Query for Core PBX settings.\n"
2443 "Variables: (Names marked with * are optional)\n"
2444 " *ActionID: ActionID of this transaction\n";
2446 /*! \brief Show PBX core settings information */
2447 static int action_coresettings(struct mansession *s, const struct message *m)
2449 const char *actionid = astman_get_header(m, "ActionID");
2452 if (!ast_strlen_zero(actionid))
2453 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2457 astman_append(s, "Response: Success\r\n"
2459 "AMIversion: %s\r\n"
2460 "AsteriskVersion: %s\r\n"
2461 "SystemName: %s\r\n"
2462 "CoreMaxCalls: %d\r\n"
2463 "CoreMaxLoadAvg: %f\r\n"
2464 "CoreRunUser: %s\r\n"
2465 "CoreRunGroup: %s\r\n"
2466 "CoreMaxFilehandles: %d\r\n"
2467 "CoreRealTimeEnabled: %s\r\n"
2468 "CoreCDRenabled: %s\r\n"
2469 "CoreHTTPenabled: %s\r\n"
2474 ast_config_AST_SYSTEM_NAME,
2477 ast_config_AST_RUN_USER,
2478 ast_config_AST_RUN_GROUP,
2480 ast_realtime_enabled() ? "Yes" : "No",
2481 check_cdr_enabled() ? "Yes" : "No",
2482 check_webmanager_enabled() ? "Yes" : "No"
2487 static char mandescr_corestatus[] =
2488 "Description: Query for Core PBX status.\n"
2489 "Variables: (Names marked with * are optional)\n"
2490 " *ActionID: ActionID of this transaction\n";
2492 /*! \brief Show PBX core status information */
2493 static int action_corestatus(struct mansession *s, const struct message *m)
2495 const char *actionid = astman_get_header(m, "ActionID");
2497 char startuptime[150];
2498 char reloadtime[150];
2501 if (!ast_strlen_zero(actionid))
2502 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2506 ast_localtime(&ast_startuptime, &tm, NULL);
2507 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2508 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2509 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2511 astman_append(s, "Response: Success\r\n"
2513 "CoreStartupTime: %s\r\n"
2514 "CoreReloadTime: %s\r\n"
2515 "CoreCurrentCalls: %d\r\n"
2520 ast_active_channels()
2525 static char mandescr_reload[] =
2526 "Description: Send a reload event.\n"
2527 "Variables: (Names marked with * are optional)\n"
2528 " *ActionID: ActionID of this transaction\n"
2529 " *Module: Name of the module to reload\n";
2531 /*! \brief Send a reload event */
2532 static int action_reload(struct mansession *s, const struct message *m)
2534 const char *module = astman_get_header(m, "Module");
2535 int res = ast_module_reload(S_OR(module, NULL));
2538 astman_send_ack(s, m, "Module Reloaded");
2540 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2544 static char mandescr_coreshowchannels[] =
2545 "Description: List currently defined channels and some information\n"
2548 " ActionID: Optional Action id for message matching.\n";
2550 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2551 * and some information about them. */
2552 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2554 const char *actionid = astman_get_header(m, "ActionID");
2555 char actionidtext[256];
2556 struct ast_channel *c = NULL;
2558 int duration, durh, durm, durs;
2560 if (!ast_strlen_zero(actionid))
2561 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2563 actionidtext[0] = '\0';
2565 astman_send_listack(s, m, "Channels will follow", "start");
2567 while ((c = ast_channel_walk_locked(c)) != NULL) {
2568 struct ast_channel *bc = ast_bridged_channel(c);
2569 char durbuf[10] = "";
2571 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2572 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2573 durh = duration / 3600;
2574 durm = (duration % 3600) / 60;
2575 durs = duration % 60;
2576 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2585 "ChannelState: %d\r\n"
2586 "ChannelStateDesc: %s\r\n"
2587 "Application: %s\r\n"
2588 "ApplicationData: %s\r\n"
2589 "CallerIDnum: %s\r\n"
2591 "AccountCode: %s\r\n"
2592 "BridgedChannel: %s\r\n"
2593 "BridgedUniqueID: %s\r\n"
2594 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2595 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2596 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2597 ast_channel_unlock(c);
2602 "Event: CoreShowChannelsComplete\r\n"
2603 "EventList: Complete\r\n"
2606 "\r\n", numchans, actionidtext);
2611 static char mandescr_modulecheck[] =
2612 "Description: Checks if Asterisk module is loaded\n"
2614 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2615 " Module: <name> Asterisk module name (not including extension)\n"
2617 "Will return Success/Failure\n"
2618 "For success returns, the module revision number is included.\n";
2620 /* Manager function to check if module is loaded */
2621 static int manager_modulecheck(struct mansession *s, const struct message *m)
2624 const char *module = astman_get_header(m, "Module");
2625 const char *id = astman_get_header(m, "ActionID");
2627 const char *version;
2628 char filename[PATH_MAX];
2631 ast_copy_string(filename, module, sizeof(filename));
2632 if ((cut = strchr(filename, '.'))) {
2635 cut = filename + strlen(filename);
2637 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
2638 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2639 res = ast_module_check(filename);
2641 astman_send_error(s, m, "Module not loaded");
2644 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
2645 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2646 version = ast_file_version_find(filename);
2648 if (!ast_strlen_zero(id))
2649 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2652 astman_append(s, "Response: Success\r\n%s", idText);
2653 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2657 static char mandescr_moduleload[] =
2658 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2660 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
2661 " Module: <name> Asterisk module name (including .so extension)\n"
2662 " or subsystem identifier:\n"
2663 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2664 " LoadType: load | unload | reload\n"
2665 " The operation to be done on module\n"
2666 " If no module is specified for a reload loadtype, all modules are reloaded";
2668 static int manager_moduleload(struct mansession *s, const struct message *m)
2671 const char *module = astman_get_header(m, "Module");
2672 const char *loadtype = astman_get_header(m, "LoadType");
2674 if (!loadtype || strlen(loadtype) == 0)
2675 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2676 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2677 astman_send_error(s, m, "Need module name");
2679 if (!strcasecmp(loadtype, "load")) {
2680 res = ast_load_resource(module);
2682 astman_send_error(s, m, "Could not load module.");
2684 astman_send_ack(s, m, "Module loaded.");
2685 } else if (!strcasecmp(loadtype, "unload")) {
2686 res = ast_unload_resource(module, AST_FORCE_SOFT);
2688 astman_send_error(s, m, "Could not unload module.");
2690 astman_send_ack(s, m, "Module unloaded.");
2691 } else if (!strcasecmp(loadtype, "reload")) {
2692 if (module != NULL) {
2693 res = ast_module_reload(module);
2695 astman_send_error(s, m, "No such module.");
2697 astman_send_error(s, m, "Module does not support reload action.");
2699 astman_send_ack(s, m, "Module reloaded.");
2701 ast_module_reload(NULL); /* Reload all modules */
2702 astman_send_ack(s, m, "All modules reloaded");
2705 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2710 * Done with the action handlers here, we start with the code in charge
2711 * of accepting connections and serving them.
2712 * accept_thread() forks a new thread for each connection, session_do(),
2713 * which in turn calls get_input() repeatedly until a full message has
2714 * been accumulated, and then invokes process_message() to pass it to
2715 * the appropriate handler.
2719 * Process an AMI message, performing desired action.
2720 * Return 0 on success, -1 on error that require the session to be destroyed.
2722 static int process_message(struct mansession *s, const struct message *m)
2724 char action[80] = "";
2726 struct manager_action *tmp;
2727 const char *user = astman_get_header(m, "Username");
2729 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2730 ast_debug(1, "Manager received command '%s'\n", action);
2732 if (ast_strlen_zero(action)) {
2733 ast_mutex_lock(&s->__lock);
2734 astman_send_error(s, m, "Missing action in request");
2735 ast_mutex_unlock(&s->__lock);
2739 if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2740 ast_mutex_lock(&s->__lock);
2741 astman_send_error(s, m, "Permission denied");
2742 ast_mutex_unlock(&s->__lock);
2746 if (!allowmultiplelogin && !s->authenticated && user &&
2747 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2748 if (check_manager_session_inuse(user)) {
2750 ast_mutex_lock(&s->__lock);
2751 astman_send_error(s, m, "Login Already In Use");
2752 ast_mutex_unlock(&s->__lock);
2757 AST_RWLIST_RDLOCK(&actions);
2758 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2759 if (strcasecmp(action, tmp->action))
2761 if (s->writeperm & tmp->authority || tmp->authority == 0)
2762 ret = tmp->func(s, m);
2764 astman_send_error(s, m, "Permission denied");
2767 AST_RWLIST_UNLOCK(&actions);
2771 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2772 ast_mutex_lock(&s->__lock);
2773 astman_send_error(s, m, buf);
2774 ast_mutex_unlock(&s->__lock);
2778 /* Once done with our message, deliver any pending events */
2779 return process_events(s);
2783 * Read one full line (including crlf) from the manager socket.
2785 * \r\n is the only valid terminator for the line.
2786 * (Note that, later, '\0' will be considered as the end-of-line marker,
2787 * so everything between the '\0' and the '\r\n' will not be used).
2788 * Also note that we assume output to have at least "maxlen" space.
2791 static int get_input(struct mansession *s, char *output)
2794 int maxlen = sizeof(s->inbuf) - 1;
2795 char *src = s->inbuf;
2798 * Look for \r\n within the buffer. If found, copy to the output
2799 * buffer and return, trimming the \r\n (not used afterwards).
2801 for (x = 0; x < s->inlen; x++) {
2802 int cr; /* set if we have \r */
2803 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2804 cr = 2; /* Found. Update length to include \r\n */
2805 else if (src[x] == '\n')
2806 cr = 1; /* also accept \n only */
2809 memmove(output, src, x); /*... but trim \r\n */
2810 output[x] = '\0'; /* terminate the string */
2811 x += cr; /* number of bytes used */
2812 s->inlen -= x; /* remaining size */
2813 memmove(src, src + x, s->inlen); /* remove used bytes */
2816 if (s->inlen >= maxlen) {
2817 /* no crlf found, and buffer full - sorry, too long for us */
2818 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2823 /* XXX do we really need this locking ? */
2824 ast_mutex_lock(&s->__lock);
2825 s->waiting_thread = pthread_self();
2826 ast_mutex_unlock(&s->__lock);
2828 res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
2830 ast_mutex_lock(&s->__lock);
2831 s->waiting_thread = AST_PTHREADT_NULL;
2832 ast_mutex_unlock(&s->__lock);
2835 /* If we get a signal from some other thread (typically because
2836 * there are new events queued), return 0 to notify the caller.
2840 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2843 ast_mutex_lock(&s->__lock);
2844 res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2846 res = -1; /* error return */
2849 src[s->inlen] = '\0';
2852 ast_mutex_unlock(&s->__lock);
2856 static int do_message(struct mansession *s)
2858 struct message m = { 0 };
2859 char header_buf[sizeof(s->inbuf)] = { '\0' };
2863 /* Check if any events are pending and do them if needed */
2864 if (process_events(s))
2866 res = get_input(s, header_buf);
2869 } else if (res > 0) {
2870 if (ast_strlen_zero(header_buf))
2871 return process_message(s, &m) ? -1 : 0;
2872 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2873 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2880 /*! \brief The body of the individual manager session.
2881 * Call get_input() to read one line at a time
2882 * (or be woken up on new events), collect the lines in a
2883 * message until found an empty line, and execute the request.
2884 * In any case, deliver events asynchronously through process_events()
2885 * (called from here if no line is available, or at the end of
2886 * process_message(). )
2888 static void *session_do(void *data)
2890 struct ast_tcptls_session_instance *ser = data;
2891 struct mansession *s = ast_calloc(1, sizeof(*s));
2898 s->writetimeout = 100;
2899 s->waiting_thread = AST_PTHREADT_NULL;
2901 flags = fcntl(ser->fd, F_GETFL);
2902 if (!block_sockets) /* make sure socket is non-blocking */
2903 flags |= O_NONBLOCK;
2905 flags &= ~O_NONBLOCK;
2906 fcntl(ser->fd, F_SETFL, flags);
2908 ast_mutex_init(&s->__lock);
2909 s->send_events = -1;
2910 /* these fields duplicate those in the 'ser' structure */
2913 s->sin = ser->requestor;
2915 AST_LIST_LOCK(&sessions);
2916 AST_LIST_INSERT_HEAD(&sessions, s, list);
2917 ast_atomic_fetchadd_int(&num_sessions, 1);
2918 AST_LIST_UNLOCK(&sessions);
2919 /* Hook to the tail of the event queue */
2920 s->last_ev = grab_last();
2922 astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
2924 if ((res = do_message(s)) < 0)
2927 /* session is over, explain why and terminate */
2928 if (s->authenticated) {
2929 if (manager_displayconnects(s))
2930 ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2931 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2933 if (displayconnects)
2934 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2935 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2938 /* It is possible under certain circumstances for this session thread
2939 to complete its work and exit *before* the thread that created it
2940 has finished executing the ast_pthread_create_background() function.
2941 If this occurs, some versions of glibc appear to act in a buggy
2942 fashion and attempt to write data into memory that it thinks belongs
2943 to the thread but is in fact not owned by the thread (or may have
2944 been freed completely).
2946 Causing this thread to yield to other threads at least one time
2947 appears to work around this bug.
2954 ser = ast_tcptls_session_instance_destroy(ser);
2958 /*! \brief remove at most n_max stale session from the list. */
2959 static void purge_sessions(int n_max)
2961 struct mansession *s;
2962 time_t now = time(NULL);
2964 AST_LIST_LOCK(&sessions);
2965 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2966 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2967 AST_LIST_REMOVE_CURRENT(list);
2968 ast_atomic_fetchadd_int(&num_sessions, -1);
2969 if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
2970 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
2971 s->username, ast_inet_ntoa(s->sin.sin_addr));
2973 free_session(s); /* XXX outside ? */
2978 AST_LIST_TRAVERSE_SAFE_END;
2979 AST_LIST_UNLOCK(&sessions);
2983 * events are appended to a queue from where they
2984 * can be dispatched to clients.
2986 static int append_event(const char *str, int category)
2988 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2989 static int seq; /* sequence number */
2994 /* need to init all fields, because ast_malloc() does not */
2996 tmp->category = category;
2997 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2998 AST_LIST_NEXT(tmp, eq_next) = NULL;
2999 strcpy(tmp->eventdata, str);
3001 AST_LIST_LOCK(&all_events);
3002 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
3003 AST_LIST_UNLOCK(&all_events);
3008 /* XXX see if can be moved inside the function */
3009 AST_THREADSTORAGE(manager_event_buf);
3010 #define MANAGER_EVENT_BUF_INITSIZE 256
3012 /*! \brief manager_event: Send AMI event to client */
3013 int __manager_event(int category, const char *event,
3014 const char *file, int line, const char *func, const char *fmt, ...)
3016 struct mansession *s;
3017 struct manager_custom_hook *hook;
3018 struct ast_str *auth = ast_str_alloca(80);
3019 const char *cat_str;
3022 struct ast_str *buf;
3024 /* Abort if there aren't any manager sessions */
3028 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
3031 cat_str = authority_to_str(category, &auth);
3032 ast_str_set(&buf, 0,
3033 "Event: %s\r\nPrivilege: %s\r\n",
3036 if (timestampevents) {
3038 ast_str_append(&buf, 0,
3039 "Timestamp: %ld.%06lu\r\n",
3040 now.tv_sec, (unsigned long) now.tv_usec);
3042 if (manager_debug) {
3044 ast_str_append(&buf, 0,
3045 "SequenceNumber: %d\r\n",
3046 ast_atomic_fetchadd_int(&seq, 1));
3047 ast_str_append(&buf, 0,
3048 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
3052 ast_str_append_va(&buf, 0, fmt, ap);
3055 ast_str_append(&buf, 0, "\r\n");
3057 append_event(buf->str, category);
3059 /* Wake up any sleeping sessions */
3060 AST_LIST_LOCK(&sessions);
3061 AST_LIST_TRAVERSE(&sessions, s, list) {
3062 ast_mutex_lock(&s->__lock);
3063 if (s->waiting_thread != AST_PTHREADT_NULL)
3064 pthread_kill(s->waiting_thread, SIGURG);
3065 ast_mutex_unlock(&s->__lock);
3067 AST_LIST_UNLOCK(&sessions);
3069 AST_RWLIST_RDLOCK(&manager_hooks);
3070 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
3071 hook->helper(category, event, buf->str);
3073 AST_RWLIST_UNLOCK(&manager_hooks);
3079 * support functions to register/unregister AMI action handlers,
3081 int ast_manager_unregister(char *action)
3083 struct manager_action *cur;
3085 AST_RWLIST_WRLOCK(&actions);
3086 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
3087 if (!strcasecmp(action, cur->action)) {
3088 AST_RWLIST_REMOVE_CURRENT(list);
3090 ast_verb(2, "Manager unregistered action %s\n", action);
3094 AST_RWLIST_TRAVERSE_SAFE_END;
3095 AST_RWLIST_UNLOCK(&actions);
3100 static int manager_state_cb(char *context, char *exten, int state, void *data)
3102 /* Notify managers of change */
3104 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
3106 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
3110 static int ast_manager_register_struct(struct manager_action *act)
3112 struct manager_action *cur, *prev = NULL;
3114 AST_RWLIST_WRLOCK(&actions);
3115 AST_RWLIST_TRAVERSE(&actions, cur, list) {
3116 int ret = strcasecmp(cur->action, act->action);
3118 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
3119 AST_RWLIST_UNLOCK(&actions);
3122 if (ret > 0) { /* Insert these alphabetically */
3129 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
3131 AST_RWLIST_INSERT_HEAD(&actions, act, list);
3133 ast_verb(2, "Manager registered action %s\n", act->action);
3135 AST_RWLIST_UNLOCK(&actions);
3140 /*! \brief register a new command with manager, including online help. This is
3141 the preferred way to register a manager command */
3142 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
3144 struct manager_action *cur = NULL;
3146 if (!(cur = ast_calloc(1, sizeof(*cur))))
3149 cur->action = action;
3150 cur->authority = auth;
3152 cur->synopsis = synopsis;
3153 cur->description = description;
3155 ast_manager_register_struct(cur);
3160 END Doxygen group */
3163 * The following are support functions for AMI-over-http.
3164 * The common entry point is generic_http_callback(),
3165 * which extracts HTTP header and URI fields and reformats
3166 * them into AMI messages, locates a proper session
3167 * (using the mansession_id Cookie or GET variable),
3168 * and calls process_message() as for regular AMI clients.
3169 * When done, the output (which goes to a temporary file)
3170 * is read back into a buffer and reformatted as desired,
3171 * then fed back to the client over the original socket.
3174 enum output_format {
3180 static char *contenttype[] = {
3181 [FORMAT_RAW] = "plain",
3182 [FORMAT_HTML] = "html",
3183 [FORMAT_XML] = "xml",
3187 * locate an http session in the list. The search key (ident) is
3188 * the value of the mansession_id cookie (0 is not valid and means
3189 * a session on the AMI socket).
3191 static struct mansession *find_session(unsigned long ident)
3193 struct mansession *s;
3198 AST_LIST_LOCK(&sessions);
3199 AST_LIST_TRAVERSE(&sessions, s, list) {
3200 ast_mutex_lock(&s->__lock);
3201 if (s->managerid == ident && !s->needdestroy) {
3202 ast_atomic_fetchadd_int(&s->inuse, 1);
3205 ast_mutex_unlock(&s->__lock);
3207 AST_LIST_UNLOCK(&sessions);
3212 int astman_verify_session_readpermissions(unsigned long ident, int perm)
3215 struct mansession *s;
3217 AST_LIST_LOCK(&sessions);
3218 AST_LIST_TRAVERSE(&sessions, s, list) {
3219 ast_mutex_lock(&s->__lock);
3220 if ((s->managerid == ident) && (s->readperm & perm)) {
3222 ast_mutex_unlock(&s->__lock);
3225 ast_mutex_unlock(&s->__lock);
3227 AST_LIST_UNLOCK(&sessions);
3231 int astman_verify_session_writepermissions(unsigned long ident, int perm)
3234 struct mansession *s;
3236 AST_LIST_LOCK(&sessions);
3237 AST_LIST_TRAVERSE(&sessions, s, list) {
3238 ast_mutex_lock(&s->__lock);
3239 if ((s->managerid == ident) && (s->writeperm & perm)) {
3241 ast_mutex_unlock(&s->__lock);
3244 ast_mutex_unlock(&s->__lock);
3246 AST_LIST_UNLOCK(&sessions);
3251 * convert to xml with various conversion:
3252 * mode & 1 -> lowercase;
3253 * mode & 2 -> replace non-alphanumeric chars with underscore
3255 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
3257 /* store in a local buffer to avoid calling ast_str_append too often */
3260 int space = sizeof(buf);
3261 /* repeat until done and nothing to flush */
3262 for ( ; *src || dst != buf ; src++) {
3263 if (*src == '\0' || space < 10) { /* flush */