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 #define DEFAULT_REALM "asterisk"
130 static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
132 static int block_sockets;
134 static int manager_debug; /*!< enable some debugging code in the manager */
137 * Descriptor for a manager session, either on the AMI socket or over HTTP.
140 * AMI session have managerid == 0; the entry is created upon a connect,
141 * and destroyed with the socket.
142 * HTTP sessions have managerid != 0, the value is used as a search key
143 * to lookup sessions (using the mansession_id cookie, or nonce key from
144 * Digest Authentication http header).
146 #define MAX_BLACKLIST_CMD_LEN 2
148 char *words[AST_MAX_CMD_LEN];
149 } command_blacklist[] = {
150 {{ "module", "load", NULL }},
151 {{ "module", "unload", NULL }},
152 {{ "restart", "gracefully", NULL }},
155 /* In order to understand what the heck is going on with the
156 * mansession_session and mansession structs, we need to have a bit of a history
159 * In the beginning, there was the mansession. The mansession contained data that was
160 * intrinsic to a manager session, such as the time that it started, the name of the logged-in
161 * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
162 * sessions, these were used to represent the TCP socket over which the AMI session was taking
163 * place. It makes perfect sense for these fields to be a part of the session-specific data since
164 * the session actually defines this information.
166 * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
167 * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
168 * but rather to the action that is being executed. Because a single session may execute many commands
169 * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
170 * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
171 * has had a chance to properly close its handles.
173 * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
174 * from being run at the same time in a single session. Some manager actions may block for a long time, thus
175 * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
176 * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
177 * part of the action instead.
179 * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
180 * contain the action-specific information, such as which file to write to. In order to maintain expectations
181 * of action handlers and not have to change the public API of the manager code, we would need to name this
182 * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
183 * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
184 * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
187 struct mansession_session {
188 pthread_t ms_t; /*!< Execution thread, basically useless */
189 /* XXX need to document which fields it is protecting */
190 struct sockaddr_in sin; /*!< address we are connecting from */
191 FILE *f; /*!< fdopen() on the underlying fd */
192 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
193 int inuse; /*!< number of HTTP sessions using this entry */
194 int needdestroy; /*!< Whether an HTTP session should be destroyed */
195 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
196 uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
197 time_t sessionstart; /*!< Session start time */
198 time_t sessiontimeout; /*!< Session timeout if HTTP */
199 char username[80]; /*!< Logged in username */
200 char challenge[10]; /*!< Authentication challenge */
201 int authenticated; /*!< Authentication status */
202 int readperm; /*!< Authorization for reading */
203 int writeperm; /*!< Authorization for writing */
204 char inbuf[1025]; /*!< Buffer */
205 /* we use the extra byte to add a '\0' and simplify parsing */
206 int inlen; /*!< number of buffered bytes */
207 int send_events; /*!< XXX what ? */
208 struct eventqent *last_ev; /*!< last event processed. */
209 int writetimeout; /*!< Timeout for ast_carefulwrite() */
210 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
211 time_t noncetime; /*!< Timer for nonce value expiration */
212 unsigned long oldnonce; /*!< Stale nonce value */
213 unsigned long nc; /*!< incremental nonce counter */
214 AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
215 AST_LIST_ENTRY(mansession_session) list;
218 /* In case you didn't read that giant block of text above the mansession_session struct, the
219 * 'mansession' struct is named this solely to keep the API the same in Asterisk. This structure really
220 * represents data that is different from Manager action to Manager action. The mansession_session pointer
221 * contained within points to session-specific data.
224 struct mansession_session *session;
229 static struct ao2_container *sessions = NULL;
231 #define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next))
233 /*! \brief user descriptor, as read from the config file.
235 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
236 * lines which are not supported here, and readperm/writeperm/writetimeout
239 struct ast_manager_user {
242 struct ast_ha *ha; /*!< ACL setting */
243 int readperm; /*! Authorization for reading */
244 int writeperm; /*! Authorization for writing */
245 int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
246 int displayconnects; /*!< XXX unused */
247 int keep; /*!< mark entries created on a reload */
248 char *a1_hash; /*!< precalculated A1 for Digest auth */
249 AST_RWLIST_ENTRY(ast_manager_user) list;
252 /*! \brief list of users found in the config file */
253 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
255 /*! \brief list of actions registered */
256 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
258 /*! \brief list of hooks registered */
259 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
261 static struct eventqent *unref_event(struct eventqent *e);
262 static void ref_event(struct eventqent *e);
264 /*! \brief Add a custom hook to be called when an event is fired */
265 void ast_manager_register_hook(struct manager_custom_hook *hook)
267 AST_RWLIST_WRLOCK(&manager_hooks);
268 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
269 AST_RWLIST_UNLOCK(&manager_hooks);
273 /*! \brief Delete a custom hook to be called when an event is fired */
274 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
276 AST_RWLIST_WRLOCK(&manager_hooks);
277 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
278 AST_RWLIST_UNLOCK(&manager_hooks);
283 * Event list management functions.
284 * We assume that the event list always has at least one element,
285 * and the delete code will not remove the last entry even if the
289 static time_t __deb(time_t start, const char *msg)
291 time_t now = time(NULL);
292 ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
293 if (start != 0 && now - start > 5)
294 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
298 static void LOCK_EVENTS(void)
300 time_t start = __deb(0, "about to lock events");
301 AST_LIST_LOCK(&all_events);
302 __deb(start, "done lock events");
305 static void UNLOCK_EVENTS(void)
307 __deb(0, "about to unlock events");
308 AST_LIST_UNLOCK(&all_events);
311 static void LOCK_SESS(void)
313 time_t start = __deb(0, "about to lock sessions");
314 AST_LIST_LOCK(&sessions);
315 __deb(start, "done lock sessions");
318 static void UNLOCK_SESS(void)
320 __deb(0, "about to unlock sessions");
321 AST_LIST_UNLOCK(&sessions);
325 int check_manager_enabled()
327 return manager_enabled;
330 int check_webmanager_enabled()
332 return (webmanager_enabled && manager_enabled);
336 * Grab a reference to the last event, update usecount as needed.
337 * Can handle a NULL pointer.
339 static struct eventqent *grab_last(void)
341 struct eventqent *ret;
343 AST_LIST_LOCK(&all_events);
344 ret = AST_LIST_LAST(&all_events);
345 /* the list is never empty now, but may become so when
346 * we optimize it in the future, so be prepared.
349 ast_atomic_fetchadd_int(&ret->usecount, 1);
351 AST_LIST_UNLOCK(&all_events);
356 * Purge unused events. Remove elements from the head
357 * as long as their usecount is 0 and there is a next element.
359 static void purge_events(void)
361 struct eventqent *ev;
363 AST_LIST_LOCK(&all_events);
364 while ( (ev = AST_LIST_FIRST(&all_events)) &&
365 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
366 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
369 AST_LIST_UNLOCK(&all_events);
373 * helper functions to convert back and forth between
374 * string and numeric representation of set of flags
376 static struct permalias {
380 { EVENT_FLAG_SYSTEM, "system" },
381 { EVENT_FLAG_CALL, "call" },
382 { EVENT_FLAG_LOG, "log" },
383 { EVENT_FLAG_VERBOSE, "verbose" },
384 { EVENT_FLAG_COMMAND, "command" },
385 { EVENT_FLAG_AGENT, "agent" },
386 { EVENT_FLAG_USER, "user" },
387 { EVENT_FLAG_CONFIG, "config" },
388 { EVENT_FLAG_DTMF, "dtmf" },
389 { EVENT_FLAG_REPORTING, "reporting" },
390 { EVENT_FLAG_CDR, "cdr" },
391 { EVENT_FLAG_DIALPLAN, "dialplan" },
392 { EVENT_FLAG_ORIGINATE, "originate" },
393 { EVENT_FLAG_AGI, "agi" },
398 /*! \brief Convert authority code to a list of options */
399 static char *authority_to_str(int authority, struct ast_str **res)
405 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
406 if (authority & perms[i].num) {
407 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
412 if (ast_str_strlen(*res) == 0) /* replace empty string with something sensible */
413 ast_str_append(res, 0, "<none>");
415 return ast_str_buffer(*res);
418 /*! Tells you if smallstr exists inside bigstr
419 which is delim by delim and uses no buf or stringsep
420 ast_instring("this|that|more","this",'|') == 1;
422 feel free to move this to app.c -anthm */
423 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
425 const char *val = bigstr, *next;
428 if ((next = strchr(val, delim))) {
429 if (!strncmp(val, smallstr, (next - val))) {
435 return !strcmp(smallstr, val);
437 } while (*(val = (next + 1)));
442 static int get_perm(const char *instr)
450 for (x = 0; x < ARRAY_LEN(perms); x++) {
451 if (ast_instring(instr, perms[x].label, ',')) {
460 * A number returns itself, false returns 0, true returns all flags,
461 * other strings return the flags that are set.
463 static int strings_to_mask(const char *string)
467 if (ast_strlen_zero(string)) {
471 for (p = string; *p; p++) {
472 if (*p < '0' || *p > '9') {
476 if (!p) { /* all digits */
479 if (ast_false(string)) {
482 if (ast_true(string)) { /* all permissions */
484 for (x = 0; x < ARRAY_LEN(perms); x++) {
489 return get_perm(string);
492 /*! \brief Unreference manager session object.
493 If no more references, then go ahead and delete it */
494 static struct mansession_session *unref_mansession(struct mansession_session *s)
496 int refcount = ao2_ref(s, -1);
498 ast_log(LOG_DEBUG, "Mansession: %p refcount now %d\n", s, refcount - 1);
503 static void session_destructor(void *obj)
505 struct mansession_session *session = obj;
506 struct eventqent *eqe = session->last_ev;
508 if (session->f != NULL) {
514 /*! \brief Allocate manager session structure and add it to the list of sessions */
515 static struct mansession_session *build_mansession(struct sockaddr_in sin)
517 struct mansession_session *newsession;
519 if (!(newsession = ao2_alloc(sizeof(*newsession), session_destructor))) {
522 memset(newsession, 0, sizeof(*newsession));
524 newsession->waiting_thread = AST_PTHREADT_NULL;
525 newsession->writetimeout = 100;
526 newsession->send_events = -1;
527 newsession->sin = sin;
529 ao2_link(sessions, newsession);
531 return unref_mansession(newsession);
534 static int mansession_cmp_fn(void *obj, void *arg, int flags)
536 struct mansession_session *s = obj;
538 return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
541 static void session_destroy(struct mansession_session *s)
543 ao2_unlink(sessions, s);
547 static int check_manager_session_inuse(const char *name)
549 struct mansession_session *session = ao2_find(sessions, (char*) name, OBJ_POINTER);
552 unref_mansession(session);
554 return session ? 1 : 0;
559 * lookup an entry in the list of registered users.
560 * must be called with the list lock held.
562 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
564 struct ast_manager_user *user = NULL;
566 AST_RWLIST_TRAVERSE(&users, user, list)
567 if (!strcasecmp(user->username, name)) {
573 /*! \brief Get displayconnects config option.
574 * \param session manager session to get parameter from.
575 * \return displayconnects config option value.
577 static int manager_displayconnects (struct mansession_session *session)
579 struct ast_manager_user *user = NULL;
582 AST_RWLIST_RDLOCK(&users);
583 if ((user = get_manager_by_name_locked (session->username))) {
584 ret = user->displayconnects;
586 AST_RWLIST_UNLOCK(&users);
591 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
593 struct manager_action *cur;
594 struct ast_str *authority;
599 e->command = "manager show command";
601 "Usage: manager show command <actionname>\n"
602 " Shows the detailed description for a specific Asterisk manager interface command.\n";
607 AST_RWLIST_RDLOCK(&actions);
608 AST_RWLIST_TRAVERSE(&actions, cur, list) {
609 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
610 ret = ast_strdup(cur->action);
611 break; /* make sure we exit even if ast_strdup() returns NULL */
614 AST_RWLIST_UNLOCK(&actions);
617 authority = ast_str_alloca(80);
619 return CLI_SHOWUSAGE;
622 AST_RWLIST_RDLOCK(&actions);
623 AST_RWLIST_TRAVERSE(&actions, cur, list) {
624 for (num = 3; num < a->argc; num++) {
625 if (!strcasecmp(cur->action, a->argv[num])) {
626 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
627 cur->action, cur->synopsis,
628 authority_to_str(cur->authority, &authority),
629 S_OR(cur->description, ""));
633 AST_RWLIST_UNLOCK(&actions);
638 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
642 e->command = "manager set debug [on|off]";
643 e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
650 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
651 } else if (a->argc == 4) {
652 if (!strcasecmp(a->argv[3], "on")) {
654 } else if (!strcasecmp(a->argv[3], "off")) {
657 return CLI_SHOWUSAGE;
663 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
665 struct ast_manager_user *user = NULL;
668 struct ast_str *rauthority = ast_str_alloca(128);
669 struct ast_str *wauthority = ast_str_alloca(128);
673 e->command = "manager show user";
675 " Usage: manager show user <user>\n"
676 " Display all information related to the manager user specified.\n";
684 AST_RWLIST_RDLOCK(&users);
685 AST_RWLIST_TRAVERSE(&users, user, list) {
686 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
687 ret = ast_strdup(user->username);
691 AST_RWLIST_UNLOCK(&users);
696 return CLI_SHOWUSAGE;
699 AST_RWLIST_RDLOCK(&users);
701 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
702 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
703 AST_RWLIST_UNLOCK(&users);
707 ast_cli(a->fd, "\n");
714 "displayconnects: %s\n",
715 (user->username ? user->username : "(N/A)"),
716 (user->secret ? "<Set>" : "(N/A)"),
717 (user->ha ? "yes" : "no"),
718 authority_to_str(user->readperm, &rauthority),
719 authority_to_str(user->writeperm, &wauthority),
720 (user->displayconnects ? "yes" : "no"));
722 AST_RWLIST_UNLOCK(&users);
728 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
730 struct ast_manager_user *user = NULL;
734 e->command = "manager show users";
736 "Usage: manager show users\n"
737 " Prints a listing of all managers that are currently configured on that\n"
744 return CLI_SHOWUSAGE;
747 AST_RWLIST_RDLOCK(&users);
749 /* If there are no users, print out something along those lines */
750 if (AST_RWLIST_EMPTY(&users)) {
751 ast_cli(a->fd, "There are no manager users.\n");
752 AST_RWLIST_UNLOCK(&users);
756 ast_cli(a->fd, "\nusername\n--------\n");
758 AST_RWLIST_TRAVERSE(&users, user, list) {
759 ast_cli(a->fd, "%s\n", user->username);
763 AST_RWLIST_UNLOCK(&users);
765 ast_cli(a->fd,"-------------------\n"
766 "%d manager users configured.\n", count_amu);
771 /*! \brief CLI command manager list commands */
772 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
774 struct manager_action *cur;
775 struct ast_str *authority;
776 #define HSMC_FORMAT " %-15.15s %-15.15s %-55.55s\n"
779 e->command = "manager show commands";
781 "Usage: manager show commands\n"
782 " Prints a listing of all the available Asterisk manager interface commands.\n";
787 authority = ast_str_alloca(80);
788 ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
789 ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
791 AST_RWLIST_RDLOCK(&actions);
792 AST_RWLIST_TRAVERSE(&actions, cur, list)
793 ast_cli(a->fd, HSMC_FORMAT, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
794 AST_RWLIST_UNLOCK(&actions);
799 /*! \brief CLI command manager list connected */
800 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
802 struct mansession_session *session;
803 time_t now = time(NULL);
804 #define HSMCONN_FORMAT1 " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
805 #define HSMCONN_FORMAT2 " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
807 struct ao2_iterator i;
811 e->command = "manager show connected";
813 "Usage: manager show connected\n"
814 " Prints a listing of the users that are currently connected to the\n"
815 "Asterisk manager interface.\n";
821 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
823 i = ao2_iterator_init(sessions, 0);
824 while ((session = ao2_iterator_next(&i))) {
826 ast_cli(a->fd, HSMCONN_FORMAT2, session->username, ast_inet_ntoa(session->sin.sin_addr), (int)(session->sessionstart), (int)(now - session->sessionstart), session->fd, session->inuse, session->readperm, session->writeperm);
829 unref_mansession(session);
832 ast_cli(a->fd, "%d users connected.\n", count);
837 /*! \brief CLI command manager list eventq */
838 /* Should change to "manager show connected" */
839 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
844 e->command = "manager show eventq";
846 "Usage: manager show eventq\n"
847 " Prints a listing of all events pending in the Asterisk manger\n"
853 AST_LIST_LOCK(&all_events);
854 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
855 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
856 ast_cli(a->fd, "Category: %d\n", s->category);
857 ast_cli(a->fd, "Event:\n%s", s->eventdata);
859 AST_LIST_UNLOCK(&all_events);
864 /*! \brief CLI command manager reload */
865 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
869 e->command = "manager reload";
871 "Usage: manager reload\n"
872 " Reloads the manager configuration.\n";
878 return CLI_SHOWUSAGE;
885 static struct ast_cli_entry cli_manager[] = {
886 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
887 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
888 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
889 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
890 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
891 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
892 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
893 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
896 static struct eventqent *unref_event(struct eventqent *e)
898 ast_atomic_fetchadd_int(&e->usecount, -1);
899 return AST_LIST_NEXT(e, eq_next);
902 static void ref_event(struct eventqent *e)
904 ast_atomic_fetchadd_int(&e->usecount, 1);
908 * destroy a session, leaving the usecount
910 static void free_session(struct mansession_session *session)
912 struct eventqent *eqe = session->last_ev;
913 struct ast_datastore *datastore;
915 /* Get rid of each of the data stores on the session */
916 while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
917 /* Free the data store */
918 ast_datastore_free(datastore);
921 if (session->f != NULL)
927 static void destroy_session(struct mansession_session *session)
929 ao2_unlink(sessions, session);
930 free_session(session);
934 * Generic function to return either the first or the last matching header
935 * from a list of variables, possibly skipping empty strings.
936 * At the moment there is only one use of this function in this file,
937 * so we make it static.
939 #define GET_HEADER_FIRST_MATCH 0
940 #define GET_HEADER_LAST_MATCH 1
941 #define GET_HEADER_SKIP_EMPTY 2
942 static const char *__astman_get_header(const struct message *m, char *var, int mode)
944 int x, l = strlen(var);
945 const char *result = "";
947 for (x = 0; x < m->hdrcount; x++) {
948 const char *h = m->headers[x];
949 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ') {
950 const char *value = h + l + 2;
951 /* found a potential candidate */
952 if (mode & GET_HEADER_SKIP_EMPTY && ast_strlen_zero(value))
953 continue; /* not interesting */
954 if (mode & GET_HEADER_LAST_MATCH)
955 result = value; /* record the last match so far */
965 * Return the first matching variable from an array.
966 * This is the legacy function and is implemented in therms of
967 * __astman_get_header().
969 const char *astman_get_header(const struct message *m, char *var)
971 return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
975 struct ast_variable *astman_get_variables(const struct message *m)
978 struct ast_variable *head = NULL, *cur;
980 AST_DECLARE_APP_ARGS(args,
981 AST_APP_ARG(vars)[32];
984 varlen = strlen("Variable: ");
986 for (x = 0; x < m->hdrcount; x++) {
987 char *parse, *var, *val;
989 if (strncasecmp("Variable: ", m->headers[x], varlen)) {
992 parse = ast_strdupa(m->headers[x] + varlen);
994 AST_STANDARD_APP_ARGS(args, parse);
998 for (y = 0; y < args.argc; y++) {
1002 var = val = ast_strdupa(args.vars[y]);
1004 if (!val || ast_strlen_zero(var)) {
1007 cur = ast_variable_new(var, val, "");
1017 * helper function to send a string to the socket.
1018 * Return -1 on error (e.g. buffer full).
1020 static int send_string(struct mansession *s, char *string)
1023 return ast_careful_fwrite(s->f, s->fd, string, strlen(string), s->session->writetimeout);
1025 return ast_careful_fwrite(s->session->f, s->session->fd, string, strlen(string), s->session->writetimeout);
1030 * \brief thread local buffer for astman_append
1032 * \note This can not be defined within the astman_append() function
1033 * because it declares a couple of functions that get used to
1034 * initialize the thread local storage key.
1036 AST_THREADSTORAGE(astman_append_buf);
1037 AST_THREADSTORAGE(userevent_buf);
1039 /*! \brief initial allocated size for the astman_append_buf */
1040 #define ASTMAN_APPEND_BUF_INITSIZE 256
1043 * utility functions for creating AMI replies
1045 void astman_append(struct mansession *s, const char *fmt, ...)
1048 struct ast_str *buf;
1050 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
1055 ast_str_set_va(&buf, 0, fmt, ap);
1058 if (s->f != NULL || s->session->f != NULL) {
1059 send_string(s, ast_str_buffer(buf));
1061 ast_verbose("fd == -1 in astman_append, should not happen\n");
1065 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
1066 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
1067 hold the session lock _or_ be running in an action callback (in which case s->session->busy will
1068 be non-zero). In either of these cases, there is no need to lock-protect the session's
1069 fd, since no other output will be sent (events will be queued), and no input will
1070 be read until either the current action finishes or get_input() obtains the session
1074 /*! \brief send a response with an optional message,
1075 * and terminate it with an empty line.
1076 * m is used only to grab the 'ActionID' field.
1078 * Use the explicit constant MSG_MOREDATA to remove the empty line.
1079 * XXX MSG_MOREDATA should go to a header file.
1081 #define MSG_MOREDATA ((char *)astman_send_response)
1082 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
1084 const char *id = astman_get_header(m, "ActionID");
1086 astman_append(s, "Response: %s\r\n", resp);
1087 if (!ast_strlen_zero(id)) {
1088 astman_append(s, "ActionID: %s\r\n", id);
1091 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
1093 if (msg == MSG_MOREDATA) {
1096 astman_append(s, "Message: %s\r\n\r\n", msg);
1098 astman_append(s, "\r\n");
1102 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
1104 astman_send_response_full(s, m, resp, msg, NULL);
1107 void astman_send_error(struct mansession *s, const struct message *m, char *error)
1109 astman_send_response_full(s, m, "Error", error, NULL);
1112 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
1114 astman_send_response_full(s, m, "Success", msg, NULL);
1117 static void astman_start_ack(struct mansession *s, const struct message *m)
1119 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
1122 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
1124 astman_send_response_full(s, m, "Success", msg, listflag);
1129 Rather than braindead on,off this now can also accept a specific int mask value
1130 or a ',' delim list of mask strings (the same as manager.conf) -anthm
1132 static int set_eventmask(struct mansession *s, const char *eventmask)
1134 int maskint = strings_to_mask(eventmask);
1138 s->session->send_events = maskint;
1146 * Here we start with action_ handlers for AMI actions,
1147 * and the internal functions used by them.
1148 * Generally, the handlers are called action_foo()
1151 /* helper function for action_login() */
1152 static int authenticate(struct mansession *s, const struct message *m)
1154 const char *username = astman_get_header(m, "Username");
1155 const char *password = astman_get_header(m, "Secret");
1157 struct ast_manager_user *user = NULL;
1159 if (ast_strlen_zero(username)) { /* missing username */
1163 /* locate user in locked state */
1164 AST_RWLIST_WRLOCK(&users);
1166 if (!(user = get_manager_by_name_locked(username))) {
1167 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
1168 } else if (user->ha && !ast_apply_ha(user->ha, &(s->session->sin))) {
1169 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
1170 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
1171 const char *key = astman_get_header(m, "Key");
1172 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
1175 char md5key[256] = "";
1176 struct MD5Context md5;
1177 unsigned char digest[16];
1180 MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
1181 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
1182 MD5Final(digest, &md5);
1183 for (x = 0; x < 16; x++)
1184 len += sprintf(md5key + len, "%2.2x", digest[x]);
1185 if (!strcmp(md5key, key)) {
1189 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
1190 S_OR(s->session->challenge, ""));
1192 } else if (password && user->secret && !strcmp(password, user->secret)) {
1197 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
1198 AST_RWLIST_UNLOCK(&users);
1204 ast_copy_string(s->session->username, username, sizeof(s->session->username));
1205 s->session->readperm = user->readperm;
1206 s->session->writeperm = user->writeperm;
1207 s->session->writetimeout = user->writetimeout;
1208 s->session->sessionstart = time(NULL);
1209 set_eventmask(s, astman_get_header(m, "Events"));
1211 AST_RWLIST_UNLOCK(&users);
1215 /*! \brief Manager PING */
1216 static char mandescr_ping[] =
1217 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1218 " manager connection open.\n"
1219 "Variables: NONE\n";
1221 static int action_ping(struct mansession *s, const struct message *m)
1223 const char *actionid = astman_get_header(m, "ActionID");
1225 astman_append(s, "Response: Success\r\n");
1226 if (!ast_strlen_zero(actionid)){
1227 astman_append(s, "ActionID: %s\r\n", actionid);
1229 astman_append(s, "Ping: Pong\r\n\r\n");
1233 static char mandescr_getconfig[] =
1234 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1235 "file by category and contents or optionally by specified category only.\n"
1236 "Variables: (Names marked with * are required)\n"
1237 " *Filename: Configuration filename (e.g. foo.conf)\n"
1238 " Category: Category in configuration file\n";
1240 static int action_getconfig(struct mansession *s, const struct message *m)
1242 struct ast_config *cfg;
1243 const char *fn = astman_get_header(m, "Filename");
1244 const char *category = astman_get_header(m, "Category");
1247 char *cur_category = NULL;
1248 struct ast_variable *v;
1249 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1251 if (ast_strlen_zero(fn)) {
1252 astman_send_error(s, m, "Filename not specified");
1255 cfg = ast_config_load2(fn, "manager", config_flags);
1256 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
1257 astman_send_error(s, m, "Config file not found");
1261 astman_start_ack(s, m);
1262 while ((cur_category = ast_category_browse(cfg, cur_category))) {
1263 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
1265 astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
1266 for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) {
1267 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1272 if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
1273 astman_append(s, "No categories found\r\n");
1275 ast_config_destroy(cfg);
1276 astman_append(s, "\r\n");
1281 static char mandescr_listcategories[] =
1282 "Description: A 'ListCategories' action will dump the categories in\n"
1285 " Filename: Configuration filename (e.g. foo.conf)\n";
1287 static int action_listcategories(struct mansession *s, const struct message *m)
1289 struct ast_config *cfg;
1290 const char *fn = astman_get_header(m, "Filename");
1291 char *category = NULL;
1292 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1295 if (ast_strlen_zero(fn)) {
1296 astman_send_error(s, m, "Filename not specified");
1299 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1300 astman_send_error(s, m, "Config file not found or file has invalid syntax");
1303 astman_start_ack(s, m);
1304 while ((category = ast_category_browse(cfg, category))) {
1305 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1308 if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
1309 astman_append(s, "Error: no categories found\r\n");
1311 ast_config_destroy(cfg);
1312 astman_append(s, "\r\n");
1320 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1321 static void json_escape(char *out, const char *in)
1324 if (*in == '\\' || *in == '\"') {
1332 static char mandescr_getconfigjson[] =
1333 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1334 "file by category and contents in JSON format. This only makes sense to be used\n"
1335 "using rawman over the HTTP interface.\n"
1337 " Filename: Configuration filename (e.g. foo.conf)\n";
1339 static int action_getconfigjson(struct mansession *s, const struct message *m)
1341 struct ast_config *cfg;
1342 const char *fn = astman_get_header(m, "Filename");
1343 char *category = NULL;
1344 struct ast_variable *v;
1347 unsigned int buf_len = 0;
1348 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1350 if (ast_strlen_zero(fn)) {
1351 astman_send_error(s, m, "Filename not specified");
1355 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1356 astman_send_error(s, m, "Config file not found");
1361 buf = alloca(buf_len);
1363 astman_start_ack(s, m);
1364 astman_append(s, "JSON: {");
1365 while ((category = ast_category_browse(cfg, category))) {
1367 if (buf_len < 2 * strlen(category) + 1) {
1369 buf = alloca(buf_len);
1371 json_escape(buf, category);
1372 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1376 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1378 astman_append(s, ",");
1380 if (buf_len < 2 * strlen(v->name) + 1) {
1382 buf = alloca(buf_len);
1384 json_escape(buf, v->name);
1385 astman_append(s, "\"%s", buf);
1386 if (buf_len < 2 * strlen(v->value) + 1) {
1388 buf = alloca(buf_len);
1390 json_escape(buf, v->value);
1391 astman_append(s, "%s\"", buf);
1396 astman_append(s, "]");
1398 astman_append(s, "}\r\n\r\n");
1400 ast_config_destroy(cfg);
1405 /* helper function for action_updateconfig */
1406 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1410 const char *action, *cat, *var, *value, *match, *line;
1411 struct ast_category *category;
1412 struct ast_variable *v;
1413 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
1414 enum error_type result = 0;
1416 for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
1417 unsigned int object = 0;
1419 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1420 action = astman_get_header(m, hdr);
1421 if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
1422 break; /* this could cause problems if actions come in misnumbered */
1424 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1425 cat = astman_get_header(m, hdr);
1426 if (ast_strlen_zero(cat)) { /* every action needs a category */
1427 result = UNSPECIFIED_CATEGORY;
1431 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1432 var = astman_get_header(m, hdr);
1434 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1435 value = astman_get_header(m, hdr);
1437 if (!ast_strlen_zero(value) && *value == '>') {
1442 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1443 match = astman_get_header(m, hdr);
1445 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
1446 line = astman_get_header(m, hdr);
1448 if (!strcasecmp(action, "newcat")) {
1449 if (ast_category_get(cfg,cat)) { /* check to make sure the cat doesn't */
1450 result = FAILURE_NEWCAT; /* already exist */
1453 if (!(category = ast_category_new(cat, dfn, -1))) {
1454 result = FAILURE_ALLOCATION;
1457 if (ast_strlen_zero(match)) {
1458 ast_category_append(cfg, category);
1460 ast_category_insert(cfg, category, match);
1462 } else if (!strcasecmp(action, "renamecat")) {
1463 if (ast_strlen_zero(value)) {
1464 result = UNSPECIFIED_ARGUMENT;
1467 if (!(category = ast_category_get(cfg, cat))) {
1468 result = UNKNOWN_CATEGORY;
1471 ast_category_rename(category, value);
1472 } else if (!strcasecmp(action, "delcat")) {
1473 if (ast_category_delete(cfg, cat)) {
1474 result = FAILURE_DELCAT;
1477 } else if (!strcasecmp(action, "emptycat")) {
1478 if (ast_category_empty(cfg, cat)) {
1479 result = FAILURE_EMPTYCAT;
1482 } else if (!strcasecmp(action, "update")) {
1483 if (ast_strlen_zero(var)) {
1484 result = UNSPECIFIED_ARGUMENT;
1487 if (!(category = ast_category_get(cfg,cat))) {
1488 result = UNKNOWN_CATEGORY;
1491 if (ast_variable_update(category, var, value, match, object)) {
1492 result = FAILURE_UPDATE;
1495 } else if (!strcasecmp(action, "delete")) {
1496 if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
1497 result = UNSPECIFIED_ARGUMENT;
1500 if (!(category = ast_category_get(cfg, cat))) {
1501 result = UNKNOWN_CATEGORY;
1504 if (ast_variable_delete(category, var, match, line)) {
1505 result = FAILURE_DELETE;
1508 } else if (!strcasecmp(action, "append")) {
1509 if (ast_strlen_zero(var)) {
1510 result = UNSPECIFIED_ARGUMENT;
1513 if (!(category = ast_category_get(cfg, cat))) {
1514 result = UNKNOWN_CATEGORY;
1517 if (!(v = ast_variable_new(var, value, dfn))) {
1518 result = FAILURE_ALLOCATION;
1521 if (object || (match && !strcasecmp(match, "object"))) {
1524 ast_variable_append(category, v);
1525 } else if (!strcasecmp(action, "insert")) {
1526 if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
1527 result = UNSPECIFIED_ARGUMENT;
1530 if (!(category = ast_category_get(cfg, cat))) {
1531 result = UNKNOWN_CATEGORY;
1534 if (!(v = ast_variable_new(var, value, dfn))) {
1535 result = FAILURE_ALLOCATION;
1538 ast_variable_insert(category, v, line);
1541 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
1542 result = UNKNOWN_ACTION;
1551 static char mandescr_updateconfig[] =
1552 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1553 "configuration elements in Asterisk configuration files.\n"
1554 "Variables (X's represent 6 digit number beginning with 000000):\n"
1555 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1556 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1557 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1558 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,EmptyCat,Update,Delete,Append,Insert)\n"
1559 " Cat-XXXXXX: Category to operate on\n"
1560 " Var-XXXXXX: Variable to work on\n"
1561 " Value-XXXXXX: Value to work on\n"
1562 " Match-XXXXXX: Extra match required to match line\n"
1563 " Line-XXXXXX: Line in category to operate on (used with delete and insert actions)\n";
1565 static int action_updateconfig(struct mansession *s, const struct message *m)
1567 struct ast_config *cfg;
1568 const char *sfn = astman_get_header(m, "SrcFilename");
1569 const char *dfn = astman_get_header(m, "DstFilename");
1571 const char *rld = astman_get_header(m, "Reload");
1572 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1573 enum error_type result;
1575 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1576 astman_send_error(s, m, "Filename not specified");
1579 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
1580 astman_send_error(s, m, "Config file not found");
1583 result = handle_updates(s, m, cfg, dfn);
1585 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1586 res = ast_config_text_file_save(dfn, cfg, "Manager");
1587 ast_config_destroy(cfg);
1589 astman_send_error(s, m, "Save of config failed");
1592 astman_send_ack(s, m, NULL);
1593 if (!ast_strlen_zero(rld)) {
1594 if (ast_true(rld)) {
1597 ast_module_reload(rld);
1600 ast_config_destroy(cfg);
1602 case UNKNOWN_ACTION:
1603 astman_send_error(s, m, "Unknown action command");
1605 case UNKNOWN_CATEGORY:
1606 astman_send_error(s, m, "Given category does not exist");
1608 case UNSPECIFIED_CATEGORY:
1609 astman_send_error(s, m, "Category not specified");
1611 case UNSPECIFIED_ARGUMENT:
1612 astman_send_error(s, m, "Problem with category, value, or line (if required)");
1614 case FAILURE_ALLOCATION:
1615 astman_send_error(s, m, "Memory allocation failure, this should not happen");
1617 case FAILURE_NEWCAT:
1618 astman_send_error(s, m, "Create category did not complete successfully");
1620 case FAILURE_DELCAT:
1621 astman_send_error(s, m, "Delete category did not complete successfully");
1623 case FAILURE_EMPTYCAT:
1624 astman_send_error(s, m, "Empty category did not complete successfully");
1626 case FAILURE_UPDATE:
1627 astman_send_error(s, m, "Update did not complete successfully");
1629 case FAILURE_DELETE:
1630 astman_send_error(s, m, "Delete did not complete successfully");
1632 case FAILURE_APPEND:
1633 astman_send_error(s, m, "Append did not complete successfully");
1640 static char mandescr_createconfig[] =
1641 "Description: A 'CreateConfig' action will create an empty file in the\n"
1642 "configuration directory. This action is intended to be used before an\n"
1643 "UpdateConfig action.\n"
1645 " Filename: The configuration filename to create (e.g. foo.conf)\n";
1647 static int action_createconfig(struct mansession *s, const struct message *m)
1650 const char *fn = astman_get_header(m, "Filename");
1651 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
1652 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
1653 ast_str_append(&filepath, 0, "%s", fn);
1655 if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
1657 astman_send_ack(s, m, "New configuration file created successfully");
1659 astman_send_error(s, m, strerror(errno));
1665 /*! \brief Manager WAITEVENT */
1666 static char mandescr_waitevent[] =
1667 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1668 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1669 "session, events will be generated and queued.\n"
1671 " Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1673 static int action_waitevent(struct mansession *s, const struct message *m)
1675 const char *timeouts = astman_get_header(m, "Timeout");
1679 const char *id = astman_get_header(m, "ActionID");
1682 if (!ast_strlen_zero(id)) {
1683 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1688 if (!ast_strlen_zero(timeouts)) {
1689 sscanf(timeouts, "%i", &timeout);
1693 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1697 if (s->session->waiting_thread != AST_PTHREADT_NULL) {
1698 pthread_kill(s->session->waiting_thread, SIGURG);
1701 if (s->session->managerid) { /* AMI-over-HTTP session */
1703 * Make sure the timeout is within the expire time of the session,
1704 * as the client will likely abort the request if it does not see
1705 * data coming after some amount of time.
1707 time_t now = time(NULL);
1708 int max = s->session->sessiontimeout - now - 10;
1710 if (max < 0) { /* We are already late. Strange but possible. */
1713 if (timeout < 0 || timeout > max) {
1716 if (!s->session->send_events) { /* make sure we record events */
1717 s->session->send_events = -1;
1722 /* XXX should this go inside the lock ? */
1723 s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */
1724 ast_debug(1, "Starting waiting for an event!\n");
1726 for (x = 0; x < timeout || timeout < 0; x++) {
1731 /* We can have multiple HTTP session point to the same mansession entry.
1732 * The way we deal with it is not very nice: newcomers kick out the previous
1733 * HTTP session. XXX this needs to be improved.
1735 if (s->session->waiting_thread != pthread_self()) {
1738 if (s->session->needdestroy) {
1745 if (s->session->managerid == 0) { /* AMI session */
1746 if (ast_wait_for_input(s->session->fd, 1000)) {
1749 } else { /* HTTP session */
1753 ast_debug(1, "Finished waiting for an event!\n");
1756 if (s->session->waiting_thread == pthread_self()) {
1757 struct eventqent *eqe;
1758 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1759 while ( (eqe = NEW_EVENT(s)) ) {
1761 if (((s->session->readperm & eqe->category) == eqe->category) &&
1762 ((s->session->send_events & eqe->category) == eqe->category)) {
1763 astman_append(s, "%s", eqe->eventdata);
1765 s->session->last_ev = unref_event(s->session->last_ev);
1768 "Event: WaitEventComplete\r\n"
1771 s->session->waiting_thread = AST_PTHREADT_NULL;
1773 ast_debug(1, "Abandoning event request!\n");
1779 static char mandescr_listcommands[] =
1780 "Description: Returns the action name and synopsis for every\n"
1781 " action that is available to the user\n"
1782 "Variables: NONE\n";
1784 /*! \note The actionlock is read-locked by the caller of this function */
1785 static int action_listcommands(struct mansession *s, const struct message *m)
1787 struct manager_action *cur;
1788 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1790 astman_start_ack(s, m);
1791 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1792 if (s->session->writeperm & cur->authority || cur->authority == 0) {
1793 astman_append(s, "%s: %s (Priv: %s)\r\n",
1794 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1797 astman_append(s, "\r\n");
1802 static char mandescr_events[] =
1803 "Description: Enable/Disable sending of events to this manager\n"
1806 " EventMask: 'on' if all events should be sent,\n"
1807 " 'off' if no events should be sent,\n"
1808 " 'system,call,log' to select which flags events should have to be sent.\n";
1810 static int action_events(struct mansession *s, const struct message *m)
1812 const char *mask = astman_get_header(m, "EventMask");
1815 res = set_eventmask(s, mask);
1817 astman_append(s, "Response: Success\r\n"
1820 astman_append(s, "Response: Success\r\n"
1825 static char mandescr_logoff[] =
1826 "Description: Logoff this manager session\n"
1827 "Variables: NONE\n";
1829 static int action_logoff(struct mansession *s, const struct message *m)
1831 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1835 static int action_login(struct mansession *s, const struct message *m)
1838 /* still authenticated - don't process again */
1839 if (s->session->authenticated) {
1840 astman_send_ack(s, m, "Already authenticated");
1844 if (authenticate(s, m)) {
1846 astman_send_error(s, m, "Authentication failed");
1849 s->session->authenticated = 1;
1850 if (manager_displayconnects(s->session)) {
1851 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
1853 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
1854 astman_send_ack(s, m, "Authentication accepted");
1858 static int action_challenge(struct mansession *s, const struct message *m)
1860 const char *authtype = astman_get_header(m, "AuthType");
1862 if (!strcasecmp(authtype, "MD5")) {
1863 if (ast_strlen_zero(s->session->challenge)) {
1864 snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
1867 astman_start_ack(s, m);
1868 astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
1871 astman_send_error(s, m, "Must specify AuthType");
1876 static char mandescr_hangup[] =
1877 "Description: Hangup a channel\n"
1879 " Channel: The channel name to be hungup\n"
1880 " Cause: numeric hangup cause\n";
1882 static int action_hangup(struct mansession *s, const struct message *m)
1884 struct ast_channel *c = NULL;
1885 int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
1886 const char *name = astman_get_header(m, "Channel");
1887 const char *cause = astman_get_header(m, "Cause");
1889 if (ast_strlen_zero(name)) {
1890 astman_send_error(s, m, "No channel specified");
1894 if (!ast_strlen_zero(cause)) {
1896 causecode = strtol(cause, &endptr, 10);
1897 if (causecode < 0 || causecode > 127 || *endptr != '\0') {
1898 ast_log(LOG_NOTICE, "Invalid 'Cause: %s' in manager action Hangup\n", cause);
1899 /* keep going, better to hangup without cause than to not hang up at all */
1900 causecode = 0; /* do not set channel's hangupcause */
1904 if (!(c = ast_channel_get_by_name(name))) {
1905 astman_send_error(s, m, "No such channel");
1909 ast_channel_lock(c);
1910 if (causecode > 0) {
1911 ast_debug(1, "Setting hangupcause of channel %s to %d (is %d now)\n",
1912 c->name, causecode, c->hangupcause);
1913 c->hangupcause = causecode;
1915 ast_softhangup_nolock(c, AST_SOFTHANGUP_EXPLICIT);
1916 ast_channel_unlock(c);
1918 c = ast_channel_unref(c);
1920 astman_send_ack(s, m, "Channel Hungup");
1925 static char mandescr_setvar[] =
1926 "Description: Set a global or local channel variable.\n"
1927 "Variables: (Names marked with * are required)\n"
1928 " Channel: Channel to set variable for\n"
1929 " *Variable: Variable name\n"
1932 static int action_setvar(struct mansession *s, const struct message *m)
1934 struct ast_channel *c = NULL;
1935 const char *name = astman_get_header(m, "Channel");
1936 const char *varname = astman_get_header(m, "Variable");
1937 const char *varval = astman_get_header(m, "Value");
1939 if (ast_strlen_zero(varname)) {
1940 astman_send_error(s, m, "No variable specified");
1944 if (!ast_strlen_zero(name)) {
1945 if (!(c = ast_channel_get_by_name(name))) {
1946 astman_send_error(s, m, "No such channel");
1951 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1954 c = ast_channel_unref(c);
1957 astman_send_ack(s, m, "Variable Set");
1962 static char mandescr_getvar[] =
1963 "Description: Get the value of a global or local channel variable.\n"
1964 "Variables: (Names marked with * are required)\n"
1965 " Channel: Channel to read variable from\n"
1966 " *Variable: Variable name\n"
1967 " ActionID: Optional Action id for message matching.\n";
1969 static int action_getvar(struct mansession *s, const struct message *m)
1971 struct ast_channel *c = NULL;
1972 const char *name = astman_get_header(m, "Channel");
1973 const char *varname = astman_get_header(m, "Variable");
1975 char workspace[1024] = "";
1977 if (ast_strlen_zero(varname)) {
1978 astman_send_error(s, m, "No variable specified");
1982 if (!ast_strlen_zero(name)) {
1983 if (!(c = ast_channel_get_by_name(name))) {
1984 astman_send_error(s, m, "No such channel");
1989 if (varname[strlen(varname) - 1] == ')') {
1991 c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
1993 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1994 c = ast_channel_release(c);
1996 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
1998 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
2002 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
2006 c = ast_channel_unref(c);
2009 astman_start_ack(s, m);
2010 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
2015 static char mandescr_status[] =
2016 "Description: Lists channel status along with requested channel vars.\n"
2017 "Variables: (Names marked with * are required)\n"
2018 " *Channel: Name of the channel to query for status\n"
2019 " Variables: Comma ',' separated list of variables to include\n"
2020 " ActionID: Optional ID for this transaction\n"
2021 "Will return the status information of each channel along with the\n"
2022 "value for the specified channel variables.\n";
2025 /*! \brief Manager "status" command to show channels */
2026 /* Needs documentation... */
2027 static int action_status(struct mansession *s, const struct message *m)
2029 const char *name = astman_get_header(m, "Channel");
2030 const char *cvariables = astman_get_header(m, "Variables");
2031 char *variables = ast_strdupa(S_OR(cvariables, ""));
2032 struct ast_channel *c;
2034 struct timeval now = ast_tvnow();
2035 long elapsed_seconds = 0;
2037 int all = ast_strlen_zero(name); /* set if we want all channels */
2038 const char *id = astman_get_header(m, "ActionID");
2040 AST_DECLARE_APP_ARGS(vars,
2041 AST_APP_ARG(name)[100];
2043 struct ast_str *str = ast_str_create(1000);
2044 struct ast_channel_iterator *iter = NULL;
2046 if (!ast_strlen_zero(id)) {
2047 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2053 if (!(iter = ast_channel_iterator_all_new(0))) {
2055 astman_send_error(s, m, "Memory Allocation Failure");
2058 c = ast_channel_iterator_next(iter);
2060 if (!(c = ast_channel_get_by_name(name))) {
2061 astman_send_error(s, m, "No such channel");
2067 astman_send_ack(s, m, "Channel status will follow");
2069 if (!ast_strlen_zero(cvariables)) {
2070 AST_STANDARD_APP_ARGS(vars, variables);
2073 /* if we look by name, we break after the first iteration */
2074 for (; c; c = ast_channel_iterator_next(iter)) {
2075 ast_channel_lock(c);
2077 if (!ast_strlen_zero(cvariables)) {
2080 for (i = 0; i < vars.argc; i++) {
2081 char valbuf[512], *ret = NULL;
2083 if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
2084 if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
2089 pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
2092 ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
2098 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
2104 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
2108 "Privilege: Call\r\n"
2110 "CallerIDNum: %s\r\n"
2111 "CallerIDName: %s\r\n"
2112 "Accountcode: %s\r\n"
2113 "ChannelState: %d\r\n"
2114 "ChannelStateDesc: %s\r\n"
2125 S_OR(c->cid.cid_num, ""),
2126 S_OR(c->cid.cid_name, ""),
2129 ast_state2str(c->_state), c->context,
2130 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, ast_str_buffer(str), idText);
2134 "Privilege: Call\r\n"
2136 "CallerIDNum: %s\r\n"
2137 "CallerIDName: %s\r\n"
2146 S_OR(c->cid.cid_num, "<unknown>"),
2147 S_OR(c->cid.cid_name, "<unknown>"),
2149 ast_state2str(c->_state), bridge, c->uniqueid,
2150 ast_str_buffer(str), idText);
2153 ast_channel_unlock(c);
2154 c = ast_channel_unref(c);
2162 "Event: StatusComplete\r\n"
2165 "\r\n", idText, channels);
2172 static char mandescr_sendtext[] =
2173 "Description: Sends A Text Message while in a call.\n"
2174 "Variables: (Names marked with * are required)\n"
2175 " *Channel: Channel to send message to\n"
2176 " *Message: Message to send\n"
2177 " ActionID: Optional Action id for message matching.\n";
2179 static int action_sendtext(struct mansession *s, const struct message *m)
2181 struct ast_channel *c = NULL;
2182 const char *name = astman_get_header(m, "Channel");
2183 const char *textmsg = astman_get_header(m, "Message");
2186 if (ast_strlen_zero(name)) {
2187 astman_send_error(s, m, "No channel specified");
2191 if (ast_strlen_zero(textmsg)) {
2192 astman_send_error(s, m, "No Message specified");
2196 if (!(c = ast_channel_get_by_name(name))) {
2197 astman_send_error(s, m, "No such channel");
2201 ast_channel_lock(c);
2202 res = ast_sendtext(c, textmsg);
2203 ast_channel_unlock(c);
2204 c = ast_channel_unref(c);
2207 astman_send_ack(s, m, "Success");
2209 astman_send_error(s, m, "Failure");
2215 static char mandescr_redirect[] =
2216 "Description: Redirect (transfer) a call.\n"
2217 "Variables: (Names marked with * are required)\n"
2218 " *Channel: Channel to redirect\n"
2219 " ExtraChannel: Second call leg to transfer (optional)\n"
2220 " *Exten: Extension to transfer to\n"
2221 " *Context: Context to transfer to\n"
2222 " *Priority: Priority to transfer to\n"
2223 " ActionID: Optional Action id for message matching.\n";
2225 /*! \brief action_redirect: The redirect manager command */
2226 static int action_redirect(struct mansession *s, const struct message *m)
2228 const char *name = astman_get_header(m, "Channel");
2229 const char *name2 = astman_get_header(m, "ExtraChannel");
2230 const char *exten = astman_get_header(m, "Exten");
2231 const char *context = astman_get_header(m, "Context");
2232 const char *priority = astman_get_header(m, "Priority");
2233 struct ast_channel *chan, *chan2 = NULL;
2237 if (ast_strlen_zero(name)) {
2238 astman_send_error(s, m, "Channel not specified");
2242 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2243 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2244 astman_send_error(s, m, "Invalid priority");
2249 if (!(chan = ast_channel_get_by_name(name))) {
2251 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
2252 astman_send_error(s, m, buf);
2256 if (ast_check_hangup_locked(chan)) {
2257 astman_send_error(s, m, "Redirect failed, channel not up.");
2258 chan = ast_channel_unref(chan);
2262 if (!ast_strlen_zero(name2)) {
2263 chan2 = ast_channel_get_by_name(name2);
2266 if (chan2 && ast_check_hangup_locked(chan2)) {
2267 astman_send_error(s, m, "Redirect failed, extra channel not up.");
2268 chan = ast_channel_unref(chan);
2269 chan2 = ast_channel_unref(chan2);
2274 ast_channel_lock(chan);
2275 ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
2276 ast_channel_unlock(chan);
2279 res = ast_async_goto(chan, context, exten, pi);
2281 if (!ast_strlen_zero(name2)) {
2284 ast_channel_lock(chan2);
2285 ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
2286 ast_channel_unlock(chan2);
2288 res = ast_async_goto(chan2, context, exten, pi);
2293 astman_send_ack(s, m, "Dual Redirect successful");
2295 astman_send_error(s, m, "Secondary redirect failed");
2298 astman_send_ack(s, m, "Redirect successful");
2301 astman_send_error(s, m, "Redirect failed");
2305 chan = ast_channel_unref(chan);
2309 chan2 = ast_channel_unref(chan2);
2315 static char mandescr_atxfer[] =
2316 "Description: Attended transfer.\n"
2317 "Variables: (Names marked with * are required)\n"
2318 " *Channel: Transferer's channel\n"
2319 " *Exten: Extension to transfer to\n"
2320 " *Context: Context to transfer to\n"
2321 " *Priority: Priority to transfer to\n"
2322 " ActionID: Optional Action id for message matching.\n";
2324 static int action_atxfer(struct mansession *s, const struct message *m)
2326 const char *name = astman_get_header(m, "Channel");
2327 const char *exten = astman_get_header(m, "Exten");
2328 const char *context = astman_get_header(m, "Context");
2329 struct ast_channel *chan = NULL;
2330 struct ast_call_feature *atxfer_feature = NULL;
2331 char *feature_code = NULL;
2333 if (ast_strlen_zero(name)) {
2334 astman_send_error(s, m, "No channel specified");
2337 if (ast_strlen_zero(exten)) {
2338 astman_send_error(s, m, "No extension specified");
2342 if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
2343 astman_send_error(s, m, "No attended transfer feature found");
2347 if (!(chan = ast_channel_get_by_name(name))) {
2348 astman_send_error(s, m, "Channel specified does not exist");
2352 if (!ast_strlen_zero(context)) {
2353 pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
2356 for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
2357 struct ast_frame f = { AST_FRAME_DTMF, *feature_code };
2358 ast_queue_frame(chan, &f);
2361 for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
2362 struct ast_frame f = { AST_FRAME_DTMF, *feature_code };
2363 ast_queue_frame(chan, &f);
2366 chan = ast_channel_unref(chan);
2368 astman_send_ack(s, m, "Atxfer successfully queued");
2373 static int check_blacklist(const char *cmd)
2375 char *cmd_copy, *cur_cmd;
2376 char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
2379 cmd_copy = ast_strdupa(cmd);
2380 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
2381 cur_cmd = ast_strip(cur_cmd);
2382 if (ast_strlen_zero(cur_cmd)) {
2387 cmd_words[i] = cur_cmd;
2390 for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
2393 for (j = 0; command_blacklist[i].words[j]; j++) {
2394 if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
2408 static char mandescr_command[] =
2409 "Description: Run a CLI command.\n"
2410 "Variables: (Names marked with * are required)\n"
2411 " *Command: Asterisk CLI command to run\n"
2412 " ActionID: Optional Action id for message matching.\n";
2414 /*! \brief Manager command "command" - execute CLI command */
2415 static int action_command(struct mansession *s, const struct message *m)
2417 const char *cmd = astman_get_header(m, "Command");
2418 const char *id = astman_get_header(m, "ActionID");
2419 char *buf, *final_buf;
2420 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
2421 int fd = mkstemp(template);
2424 if (ast_strlen_zero(cmd)) {
2425 astman_send_error(s, m, "No command provided");
2429 if (check_blacklist(cmd)) {
2430 astman_send_error(s, m, "Command blacklisted");
2434 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
2435 if (!ast_strlen_zero(id)) {
2436 astman_append(s, "ActionID: %s\r\n", id);
2438 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
2439 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
2440 l = lseek(fd, 0, SEEK_END); /* how many chars available */
2442 /* This has a potential to overflow the stack. Hence, use the heap. */
2443 buf = ast_calloc(1, l + 1);
2444 final_buf = ast_calloc(1, l + 1);
2446 lseek(fd, 0, SEEK_SET);
2447 if (read(fd, buf, l) < 0) {
2448 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
2452 term_strip(final_buf, buf, l);
2453 final_buf[l] = '\0';
2455 astman_append(s, "%s", S_OR(final_buf, buf));
2460 astman_append(s, "--END COMMAND--\r\n\r\n");
2462 ast_free(final_buf);
2467 /*! \brief helper function for originate */
2468 struct fast_originate_helper {
2469 char tech[AST_MAX_EXTENSION];
2470 /*! data can contain a channel name, extension number, username, password, etc. */
2473 int format; /*!< Codecs used for a call */
2474 char app[AST_MAX_APP];
2475 char appdata[AST_MAX_EXTENSION];
2476 char cid_name[AST_MAX_EXTENSION];
2477 char cid_num[AST_MAX_EXTENSION];
2478 char context[AST_MAX_CONTEXT];
2479 char exten[AST_MAX_EXTENSION];
2480 char idtext[AST_MAX_EXTENSION];
2481 char account[AST_MAX_ACCOUNT_CODE];
2483 struct ast_variable *vars;
2486 static void *fast_originate(void *data)
2488 struct fast_originate_helper *in = data;
2491 struct ast_channel *chan = NULL;
2492 char requested_channel[AST_CHANNEL_NAME];
2494 if (!ast_strlen_zero(in->app)) {
2495 res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
2496 S_OR(in->cid_num, NULL),
2497 S_OR(in->cid_name, NULL),
2498 in->vars, in->account, &chan);
2500 res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
2501 S_OR(in->cid_num, NULL),
2502 S_OR(in->cid_name, NULL),
2503 in->vars, in->account, &chan);
2507 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
2509 /* Tell the manager what happened with the channel */
2510 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
2518 "CallerIDNum: %s\r\n"
2519 "CallerIDName: %s\r\n",
2520 in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success",
2521 chan ? chan->name : requested_channel, in->context, in->exten, reason,
2522 chan ? chan->uniqueid : "<null>",
2523 S_OR(in->cid_num, "<unknown>"),
2524 S_OR(in->cid_name, "<unknown>")
2527 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2529 ast_channel_unlock(chan);
2535 static char mandescr_originate[] =
2536 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
2537 " Application/Data\n"
2538 "Variables: (Names marked with * are required)\n"
2539 " *Channel: Channel name to call\n"
2540 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
2541 " Context: Context to use (requires 'Exten' and 'Priority')\n"
2542 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
2543 " Application: Application to use\n"
2544 " Data: Data to use (requires 'Application')\n"
2545 " Timeout: How long to wait for call to be answered (in ms)\n"
2546 " CallerID: Caller ID to be set on the outgoing channel\n"
2547 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
2548 " Account: Account code\n"
2549 " Async: Set to 'true' for fast origination\n";
2551 static int action_originate(struct mansession *s, const struct message *m)
2553 const char *name = astman_get_header(m, "Channel");
2554 const char *exten = astman_get_header(m, "Exten");
2555 const char *context = astman_get_header(m, "Context");
2556 const char *priority = astman_get_header(m, "Priority");
2557 const char *timeout = astman_get_header(m, "Timeout");
2558 const char *callerid = astman_get_header(m, "CallerID");
2559 const char *account = astman_get_header(m, "Account");
2560 const char *app = astman_get_header(m, "Application");
2561 const char *appdata = astman_get_header(m, "Data");
2562 const char *async = astman_get_header(m, "Async");
2563 const char *id = astman_get_header(m, "ActionID");
2564 const char *codecs = astman_get_header(m, "Codecs");
2565 struct ast_variable *vars = astman_get_variables(m);
2567 char *l = NULL, *n = NULL;
2574 int format = AST_FORMAT_SLINEAR;
2577 if (ast_strlen_zero(name)) {
2578 astman_send_error(s, m, "Channel not specified");
2581 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2582 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2583 astman_send_error(s, m, "Invalid priority");
2587 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2588 astman_send_error(s, m, "Invalid timeout");
2591 ast_copy_string(tmp, name, sizeof(tmp));
2593 data = strchr(tmp, '/');
2595 astman_send_error(s, m, "Invalid channel");
2599 ast_copy_string(tmp2, callerid, sizeof(tmp2));
2600 ast_callerid_parse(tmp2, &n, &l);
2602 if (ast_strlen_zero(n)) {
2607 ast_shrink_phone_number(l);
2608 if (ast_strlen_zero(l)) {
2612 if (!ast_strlen_zero(codecs)) {
2614 ast_parse_allow_disallow(NULL, &format, codecs, 1);
2616 if (ast_true(async)) {
2617 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2621 if (!ast_strlen_zero(id))
2622 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id);
2623 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2624 ast_copy_string(fast->data, data, sizeof(fast->data));
2625 ast_copy_string(fast->app, app, sizeof(fast->app));
2626 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2628 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2631 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2634 ast_copy_string(fast->context, context, sizeof(fast->context));
2635 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2636 ast_copy_string(fast->account, account, sizeof(fast->account));
2637 fast->format = format;
2639 fast->priority = pi;
2640 if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2647 } else if (!ast_strlen_zero(app)) {
2648 /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
2649 if (!(s->session->writeperm & EVENT_FLAG_SYSTEM)
2651 strcasestr(app, "system") == 0 || /* System(rm -rf /)
2652 TrySystem(rm -rf /) */
2653 strcasestr(app, "exec") || /* Exec(System(rm -rf /))
2654 TryExec(System(rm -rf /)) */
2655 strcasestr(app, "agi") || /* AGI(/bin/rm,-rf /)
2656 EAGI(/bin/rm,-rf /) */
2657 strstr(appdata, "SHELL") || /* NoOp(${SHELL(rm -rf /)}) */
2658 strstr(appdata, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
2660 astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
2663 res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2665 if (exten && context && pi) {
2666 res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2668 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2673 astman_send_ack(s, m, "Originate successfully queued");
2675 astman_send_error(s, m, "Originate failed");
2680 /*! \brief Help text for manager command mailboxstatus
2682 static char mandescr_mailboxstatus[] =
2683 "Description: Checks a voicemail account for status.\n"
2684 "Variables: (Names marked with * are required)\n"
2685 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2686 " ActionID: Optional ActionID for message matching.\n"
2687 "Returns number of messages.\n"
2688 " Message: Mailbox Status\n"
2689 " Mailbox: <mailboxid>\n"
2690 " Waiting: <count>\n"
2693 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2695 const char *mailbox = astman_get_header(m, "Mailbox");
2698 if (ast_strlen_zero(mailbox)) {
2699 astman_send_error(s, m, "Mailbox not specified");
2702 ret = ast_app_has_voicemail(mailbox, NULL);
2703 astman_start_ack(s, m);
2704 astman_append(s, "Message: Mailbox Status\r\n"
2706 "Waiting: %d\r\n\r\n", mailbox, ret);
2710 static char mandescr_mailboxcount[] =
2711 "Description: Checks a voicemail account for new messages.\n"
2712 "Variables: (Names marked with * are required)\n"
2713 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2714 " ActionID: Optional ActionID for message matching.\n"
2715 "Returns number of urgent, new and old messages.\n"
2716 " Message: Mailbox Message Count\n"
2717 " Mailbox: <mailboxid>\n"
2718 " UrgentMessages: <count>\n"
2719 " NewMessages: <count>\n"
2720 " OldMessages: <count>\n"
2722 static int action_mailboxcount(struct mansession *s, const struct message *m)
2724 const char *mailbox = astman_get_header(m, "Mailbox");
2725 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
2727 if (ast_strlen_zero(mailbox)) {
2728 astman_send_error(s, m, "Mailbox not specified");
2731 ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
2732 astman_start_ack(s, m);
2733 astman_append(s, "Message: Mailbox Message Count\r\n"
2735 "UrgMessages: %d\r\n"
2736 "NewMessages: %d\r\n"
2737 "OldMessages: %d\r\n"
2739 mailbox, urgentmsgs, newmsgs, oldmsgs);
2743 static char mandescr_extensionstate[] =
2744 "Description: Report the extension state for given extension.\n"
2745 " If the extension has a hint, will use devicestate to check\n"
2746 " the status of the device connected to the extension.\n"
2747 "Variables: (Names marked with * are required)\n"
2748 " *Exten: Extension to check state on\n"
2749 " *Context: Context for extension\n"
2750 " ActionId: Optional ID for this transaction\n"
2751 "Will return an \"Extension Status\" message.\n"
2752 "The response will include the hint for the extension and the status.\n";
2754 static int action_extensionstate(struct mansession *s, const struct message *m)
2756 const char *exten = astman_get_header(m, "Exten");
2757 const char *context = astman_get_header(m, "Context");
2758 char hint[256] = "";
2760 if (ast_strlen_zero(exten)) {
2761 astman_send_error(s, m, "Extension not specified");
2764 if (ast_strlen_zero(context)) {
2765 context = "default";
2767 status = ast_extension_state(NULL, context, exten);
2768 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2769 astman_start_ack(s, m);
2770 astman_append(s, "Message: Extension Status\r\n"
2774 "Status: %d\r\n\r\n",
2775 exten, context, hint, status);
2779 static char mandescr_timeout[] =
2780 "Description: Hangup a channel after a certain time.\n"
2781 "Variables: (Names marked with * are required)\n"
2782 " *Channel: Channel name to hangup\n"
2783 " *Timeout: Maximum duration of the call (sec)\n"
2784 "Acknowledges set time with 'Timeout Set' message\n";
2786 static int action_timeout(struct mansession *s, const struct message *m)
2788 struct ast_channel *c;
2789 const char *name = astman_get_header(m, "Channel");
2790 double timeout = atof(astman_get_header(m, "Timeout"));
2791 struct timeval when = { timeout, 0 };
2793 if (ast_strlen_zero(name)) {
2794 astman_send_error(s, m, "No channel specified");
2798 if (!timeout || timeout < 0) {
2799 astman_send_error(s, m, "No timeout specified");
2803 if (!(c = ast_channel_get_by_name(name))) {
2804 astman_send_error(s, m, "No such channel");
2808 when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
2810 ast_channel_lock(c);
2811 ast_channel_setwhentohangup_tv(c, when);
2812 ast_channel_unlock(c);
2813 c = ast_channel_unref(c);
2815 astman_send_ack(s, m, "Timeout Set");
2821 * Send any applicable events to the client listening on this socket.
2822 * Wait only for a finite time on each event, and drop all events whether
2823 * they are successfully sent or not.
2825 static int process_events(struct mansession *s)
2829 ao2_lock(s->session);
2830 if (s->session->f != NULL) {
2831 struct eventqent *eqe;
2833 while ( (eqe = NEW_EVENT(s)) ) {
2835 if (!ret && s->session->authenticated &&
2836 (s->session->readperm & eqe->category) == eqe->category &&
2837 (s->session->send_events & eqe->category) == eqe->category) {
2838 if (send_string(s, eqe->eventdata) < 0)
2839 ret = -1; /* don't send more */
2841 s->session->last_ev = unref_event(s->session->last_ev);
2844 ao2_unlock(s->session);
2848 static char mandescr_userevent[] =
2849 "Description: Send an event to manager sessions.\n"
2850 "Variables: (Names marked with * are required)\n"
2851 " *UserEvent: EventStringToSend\n"
2852 " Header1: Content1\n"
2853 " HeaderN: ContentN\n";
2855 static int action_userevent(struct mansession *s, const struct message *m)
2857 const char *event = astman_get_header(m, "UserEvent");
2858 struct ast_str *body = ast_str_thread_get(&userevent_buf, 16);
2861 ast_str_reset(body);
2863 for (x = 0; x < m->hdrcount; x++) {
2864 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2865 ast_str_append(&body, 0, "%s\r\n", m->headers[x]);
2869 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, ast_str_buffer(body));
2873 static char mandescr_coresettings[] =
2874 "Description: Query for Core PBX settings.\n"
2875 "Variables: (Names marked with * are optional)\n"
2876 " *ActionID: ActionID of this transaction\n";
2878 /*! \brief Show PBX core settings information */
2879 static int action_coresettings(struct mansession *s, const struct message *m)
2881 const char *actionid = astman_get_header(m, "ActionID");
2884 if (!ast_strlen_zero(actionid)) {
2885 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2890 astman_append(s, "Response: Success\r\n"
2892 "AMIversion: %s\r\n"
2893 "AsteriskVersion: %s\r\n"
2894 "SystemName: %s\r\n"
2895 "CoreMaxCalls: %d\r\n"
2896 "CoreMaxLoadAvg: %f\r\n"
2897 "CoreRunUser: %s\r\n"
2898 "CoreRunGroup: %s\r\n"
2899 "CoreMaxFilehandles: %d\r\n"
2900 "CoreRealTimeEnabled: %s\r\n"
2901 "CoreCDRenabled: %s\r\n"
2902 "CoreHTTPenabled: %s\r\n"
2907 ast_config_AST_SYSTEM_NAME,
2910 ast_config_AST_RUN_USER,
2911 ast_config_AST_RUN_GROUP,
2913 ast_realtime_enabled() ? "Yes" : "No",
2914 check_cdr_enabled() ? "Yes" : "No",
2915 check_webmanager_enabled() ? "Yes" : "No"
2920 static char mandescr_corestatus[] =
2921 "Description: Query for Core PBX status.\n"
2922 "Variables: (Names marked with * are optional)\n"
2923 " *ActionID: ActionID of this transaction\n";
2925 /*! \brief Show PBX core status information */
2926 static int action_corestatus(struct mansession *s, const struct message *m)
2928 const char *actionid = astman_get_header(m, "ActionID");
2930 char startuptime[150];
2931 char reloadtime[150];
2934 if (!ast_strlen_zero(actionid)) {
2935 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2940 ast_localtime(&ast_startuptime, &tm, NULL);
2941 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2942 ast_localtime(&ast_lastreloadtime, &tm, NULL);
2943 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2945 astman_append(s, "Response: Success\r\n"
2947 "CoreStartupTime: %s\r\n"
2948 "CoreReloadTime: %s\r\n"
2949 "CoreCurrentCalls: %d\r\n"
2954 ast_active_channels()
2959 static char mandescr_reload[] =
2960 "Description: Send a reload event.\n"
2961 "Variables: (Names marked with * are optional)\n"
2962 " *ActionID: ActionID of this transaction\n"
2963 " *Module: Name of the module to reload\n";
2965 /*! \brief Send a reload event */
2966 static int action_reload(struct mansession *s, const struct message *m)
2968 const char *module = astman_get_header(m, "Module");
2969 int res = ast_module_reload(S_OR(module, NULL));
2972 astman_send_ack(s, m, "Module Reloaded");
2974 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2979 static char mandescr_coreshowchannels[] =
2980 "Description: List currently defined channels and some information\n"
2983 " ActionID: Optional Action id for message matching.\n";
2985 /*! \brief Manager command "CoreShowChannels" - List currently defined channels
2986 * and some information about them. */
2987 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2989 const char *actionid = astman_get_header(m, "ActionID");
2990 char actionidtext[256];
2991 struct ast_channel *c = NULL;
2993 int duration, durh, durm, durs;
2994 struct ast_channel_iterator *iter;
2996 if (!ast_strlen_zero(actionid)) {
2997 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2999 actionidtext[0] = '\0';
3002 if (!(iter = ast_channel_iterator_all_new(0))) {
3003 astman_send_error(s, m, "Memory Allocation Failure");
3007 astman_send_listack(s, m, "Channels will follow", "start");
3009 for (; (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
3010 struct ast_channel *bc;
3011 char durbuf[10] = "";
3013 ast_channel_lock(c);
3015 bc = ast_bridged_channel(c);
3016 if (c->cdr && !ast_tvzero(c->cdr->start)) {
3017 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
3018 durh = duration / 3600;
3019 durm = (duration % 3600) / 60;
3020 durs = duration % 60;
3021 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
3025 "Event: CoreShowChannel\r\n"
3031 "ChannelState: %d\r\n"
3032 "ChannelStateDesc: %s\r\n"
3033 "Application: %s\r\n"
3034 "ApplicationData: %s\r\n"
3035 "CallerIDnum: %s\r\n"
3037 "AccountCode: %s\r\n"
3038 "BridgedChannel: %s\r\n"
3039 "BridgedUniqueID: %s\r\n"
3040 "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
3041 c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
3042 S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
3044 ast_channel_unlock(c);
3050 "Event: CoreShowChannelsComplete\r\n"
3051 "EventList: Complete\r\n"
3054 "\r\n", numchans, actionidtext);
3056 ast_channel_iterator_destroy(iter);
3061 static char mandescr_modulecheck[] =
3062 "Description: Checks if Asterisk module is loaded\n"
3064 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
3065 " Module: <name> Asterisk module name (not including extension)\n"
3067 "Will return Success/Failure\n"
3068 "For success returns, the module revision number is included.\n";
3070 /* Manager function to check if module is loaded */
3071 static int manager_modulecheck(struct mansession *s, const struct message *m)
3074 const char *module = astman_get_header(m, "Module");
3075 const char *id = astman_get_header(m, "ActionID");
3077 #if !defined(LOW_MEMORY)
3078 const char *version;
3080 char filename[PATH_MAX];
3083 ast_copy_string(filename, module, sizeof(filename));
3084 if ((cut = strchr(filename, '.'))) {
3087 cut = filename + strlen(filename);
3089 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
3090 ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
3091 res = ast_module_check(filename);
3093 astman_send_error(s, m, "Module not loaded");
3096 snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
3097 ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
3098 #if !defined(LOW_MEMORY)
3099 version = ast_file_version_find(filename);
3102 if (!ast_strlen_zero(id)) {
3103 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
3107 astman_append(s, "Response: Success\r\n%s", idText);
3108 #if !defined(LOW_MEMORY)
3109 astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
3114 static char mandescr_moduleload[] =
3115 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
3117 " ActionID: <id> Action ID for this transaction. Will be returned.\n"
3118 " Module: <name> Asterisk module name (including .so extension)\n"
3119 " or subsystem identifier:\n"
3120 " cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
3121 " LoadType: load | unload | reload\n"
3122 " The operation to be done on module\n"
3123 " If no module is specified for a reload loadtype, all modules are reloaded";
3125 static int manager_moduleload(struct mansession *s, const struct message *m)
3128 const char *module = astman_get_header(m, "Module");
3129 const char *loadtype = astman_get_header(m, "LoadType");
3131 if (!loadtype || strlen(loadtype) == 0) {
3132 astman_send_error(s, m, "Incomplete ModuleLoad action.");
3134 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0) {
3135 astman_send_error(s, m, "Need module name");
3138 if (!strcasecmp(loadtype, "load")) {
3139 res = ast_load_resource(module);
3141 astman_send_error(s, m, "Could not load module.");
3143 astman_send_ack(s, m, "Module loaded.");
3145 } else if (!strcasecmp(loadtype, "unload")) {
3146 res = ast_unload_resource(module, AST_FORCE_SOFT);
3148 astman_send_error(s, m, "Could not unload module.");
3150 astman_send_ack(s, m, "Module unloaded.");
3152 } else if (!strcasecmp(loadtype, "reload")) {
3153 if (module != NULL) {
3154 res = ast_module_reload(module);
3156 astman_send_error(s, m, "No such module.");
3157 } else if (res == 1) {
3158 astman_send_error(s, m, "Module does not support reload action.");
3160 astman_send_ack(s, m, "Module reloaded.");
3163 ast_module_reload(NULL); /* Reload all modules */
3164 astman_send_ack(s, m, "All modules reloaded");
3167 astman_send_error(s, m, "Incomplete ModuleLoad action.");
3172 * Done with the action handlers here, we start with the code in charge
3173 * of accepting connections and serving them.
3174 * accept_thread() forks a new thread for each connection, session_do(),
3175 * which in turn calls get_input() repeatedly until a full message has
3176 * been accumulated, and then invokes process_message() to pass it to
3177 * the appropriate handler.
3181 * Process an AMI message, performing desired action.
3182 * Return 0 on success, -1 on error that require the session to be destroyed.
3184 static int process_message(struct mansession *s, const struct message *m)
3186 char action[80] = "";
3188 struct manager_action *tmp;
3189 const char *user = astman_get_header(m, "Username");
3190 int (*call_func)(struct mansession *s, const struct message *m) = NULL;
3192 ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
3194 if (ast_strlen_zero(action)) {
3196 astman_send_error(s, m, "Missing action in request");
3201 if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
3203 astman_send_error(s, m, "Permission denied");
3208 if (!allowmultiplelogin && !s->session->authenticated && user &&
3209 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
3210 if (check_manager_session_inuse(user)) {
3213 astman_send_error(s, m, "Login Already In Use");
3219 AST_RWLIST_RDLOCK(&actions);
3220 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
3221 if (strcasecmp(action, tmp->action)) {
3224 if (s->session->writeperm & tmp->authority || tmp->authority == 0) {
3225 call_func = tmp->func;
3227 astman_send_error(s, m, "Permission denied");
3232 AST_RWLIST_UNLOCK(&actions);
3234 if (tmp && call_func) {
3235 /* call AMI function after actions list are unlocked */
3236 ast_debug(1, "Running action '%s'\n", tmp->action);
3237 ret = call_func(s, m);
3240 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
3242 astman_send_error(s, m, buf);
3248 /* Once done with our message, deliver any pending events unless the
3249 requester doesn't want them as part of this response.
3251 if (ast_strlen_zero(astman_get_header(m, "SuppressEvents"))) {
3252 return process_events(s);
3259 * Read one full line (including crlf) from the manager socket.
3261 * \r\n is the only valid terminator for the line.
3262 * (Note that, later, '\0' will be considered as the end-of-line marker,
3263 * so everything between the '\0' and the '\r\n' will not be used).
3264 * Also note that we assume output to have at least "maxlen" space.
3267 static int get_input(struct mansession *s, char *output)
3270 int maxlen = sizeof(s->session->inbuf) - 1;
3271 char *src = s->session->inbuf;
3274 * Look for \r\n within the buffer. If found, copy to the output
3275 * buffer and return, trimming the \r\n (not used afterwards).
3277 for (x = 0; x < s->session->inlen; x++) {
3278 int cr; /* set if we have \r */
3279 if (src[x] == '\r' && x+1 < s->session->inlen && src[x + 1] == '\n') {
3280 cr = 2; /* Found. Update length to include \r\n */
3281 } else if (src[x] == '\n') {
3282 cr = 1; /* also accept \n only */
3286 memmove(output, src, x); /*... but trim \r\n */
3287 output[x] = '\0'; /* terminate the string */
3288 x += cr; /* number of bytes used */
3289 s->session->inlen -= x; /* remaining size */
3290 memmove(src, src + x, s->session->inlen); /* remove used bytes */
3293 if (s->session->inlen >= maxlen) {
3294 /* no crlf found, and buffer full - sorry, too long for us */
3295 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->session->sin.sin_addr), src);
3296 s->session->inlen = 0;
3300 /* XXX do we really need this locking ? */
3302 if (s->session->pending_event) {
3303 s->session->pending_event = 0;
3307 s->session->waiting_thread = pthread_self();
3310 res = ast_wait_for_input(s->session->fd, -1); /* return 0 on timeout ? */
3313 s->session->waiting_thread = AST_PTHREADT_NULL;
3317 /* If we get a signal from some other thread (typically because
3318 * there are new events queued), return 0 to notify the caller.
3320 if (errno == EINTR || errno == EAGAIN) {
3323 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
3328 res = fread(src + s->session->inlen, 1, maxlen - s->session->inlen, s->session->f);
3330 res = -1; /* error return */
3332 s->session->inlen += res;
3333 src[s->session->inlen] = '\0';
3340 static int do_message(struct mansession *s)
3342 struct message m = { 0 };
3343 char header_buf[sizeof(s->session->inbuf)] = { '\0' };
3347 /* Check if any events are pending and do them if needed */
3348 if (process_events(s)) {
3351 res = get_input(s, header_buf);
3354 } else if (res > 0) {
3355 if (ast_strlen_zero(header_buf)) {
3356 return process_message(s, &m) ? -1 : 0;
3357 } else if (m.hdrcount < (AST_MAX_MANHEADERS - 1)) {
3358 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
3366 /*! \brief The body of the individual manager session.
3367 * Call get_input() to read one line at a time
3368 * (or be woken up on new events), collect the lines in a
3369 * message until found an empty line, and execute the request.
3370 * In any case, deliver events asynchronously through process_events()
3371 * (called from here if no line is available, or at the end of
3372 * process_message(). )
3374 static void *session_do(void *data)
3376 struct ast_tcptls_session_instance *ser = data;
3377 struct mansession_session *session = build_mansession(ser->remote_address);
3378 struct mansession s = { NULL, };
3382 if (session == NULL) {
3387 flags = fcntl(ser->fd, F_GETFL);
3388 if (!block_sockets) { /* make sure socket is non-blocking */
3389 flags |= O_NONBLOCK;
3391 flags &= ~O_NONBLOCK;
3393 fcntl(ser->fd, F_SETFL, flags);
3395 /* Hook to the tail of the event queue */
3396 session->last_ev = grab_last();
3398 /* these fields duplicate those in the 'ser' structure */
3399 session->fd = s.fd = ser->fd;
3400 session->f = s.f = ser->f;
3401 session->sin = ser->remote_address;
3402 s.session = session;
3404 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
3406 ao2_unlock(sessions);
3407 astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
3409 if ((res = do_message(&s)) < 0) {
3413 /* session is over, explain why and terminate */
3414 if (session->authenticated) {
3415 if (manager_displayconnects(session)) {
3416 ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
3418 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
3420 if (displayconnects) {
3421 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
3423 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
3426 /* It is possible under certain circumstances for this session thread
3427 to complete its work and exit *before* the thread that created it
3428 has finished executing the ast_pthread_create_background() function.
3429 If this occurs, some versions of glibc appear to act in a buggy
3430 fashion and attempt to write data into memory that it thinks belongs
3431 to the thread but is in fact not owned by the thread (or may have
3432 been freed completely).
3434 Causing this thread to yield to other threads at least one time
3435 appears to work around this bug.
3439 destroy_session(session);
3447 /*! \brief remove at most n_max stale session from the list. */
3448 static void purge_sessions(int n_max)
3450 struct mansession_session *session;
3451 time_t now = time(NULL);
3452 struct ao2_iterator i;
3454 i = ao2_iterator_init(sessions, 0);
3455 while ((session = ao2_iterator_next(&i)) && n_max > 0) {
3456 unref_mansession(session);
3458 if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
3459 if (session->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(session)) {
3460 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
3461 session->username, ast_inet_ntoa(session->sin.sin_addr));
3463 ao2_unlock(session);
3464 session_destroy(session);
3467 ao2_unlock(session);
3473 * events are appended to a queue from where they
3474 * can be dispatched to clients.
3476 static int append_event(const char *str, int category)
3478 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
3479 static int seq; /* sequence number */
3485 /* need to init all fields, because ast_malloc() does not */
3487 tmp->category = category;
3488 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
3489 AST_LIST_NEXT(tmp, eq_next) = NULL;
3490 strcpy(tmp->eventdata, str);
3492 AST_LIST_LOCK(&all_events);
3493 AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
3494 AST_LIST_UNLOCK(&all_events);
3499 /* XXX see if can be moved inside the function */
3500 AST_THREADSTORAGE(manager_event_buf);
3501 #define MANAGER_EVENT_BUF_INITSIZE 256
3503 /*! \brief manager_event: Send AMI event to client */
3504 int __manager_event(int category, const char *event,
3505 const char *file, int line, const char *func, const char *fmt, ...)
3507 struct mansession_session *session;
3508 struct manager_custom_hook *hook;
3509 struct ast_str *auth = ast_str_alloca(80);
3510 const char *cat_str;
3513 struct ast_str *buf;
3514 struct ao2_iterator i;
3516 if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE))) {
3520 cat_str = authority_to_str(category, &auth);
3521 ast_str_set(&buf, 0,
3522 "Event: %s\r\nPrivilege: %s\r\n",
3525 if (timestampevents) {
3527 ast_str_append(&buf, 0,
3528 "Timestamp: %ld.%06lu\r\n",
3529 (long)now.tv_sec, (unsigned long) now.tv_usec);
3531 if (manager_debug) {
3533 ast_str_append(&buf, 0,
3534 "SequenceNumber: %d\r\n",
3535 ast_atomic_fetchadd_int(&seq, 1));
3536 ast_str_append(&buf, 0,
3537 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
3541 ast_str_append_va(&buf, 0, fmt, ap);
3544 ast_str_append(&buf, 0, "\r\n");
3546 append_event(ast_str_buffer(buf), category);
3548 /* Wake up any sleeping sessions */
3549 i = ao2_iterator_init(sessions, 0);
3550 while ((session = ao2_iterator_next(&i))) {
3552 if (session->waiting_thread != AST_PTHREADT_NULL) {
3553 pthread_kill(session->waiting_thread, SIGURG);
3555 /* We have an event to process, but the mansession is
3556 * not waiting for it. We still need to indicate that there
3557 * is an event waiting so that get_input processes the pending
3558 * event instead of polling.
3560 session->pending_event = 1;
3562 ao2_unlock(session);
3563 unref_mansession(session);
3566 AST_RWLIST_RDLOCK(&manager_hooks);
3567 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
3568 hook->helper(category, event, ast_str_buffer(buf));
3570 AST_RWLIST_UNLOCK(&manager_hooks);
3576 * support functions to register/unregister AMI action handlers,
3578 int ast_manager_unregister(char *action)
3580 struct manager_action *cur;
3581 struct timespec tv = { 5, };
3583 if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
3584 ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
3587 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
3588 if (!strcasecmp(action, cur->action)) {
3589 AST_RWLIST_REMOVE_CURRENT(list);
3591 ast_verb(2, "Manager unregistered action %s\n", action);
3595 AST_RWLIST_TRAVERSE_SAFE_END;
3596 AST_RWLIST_UNLOCK(&actions);
3601 static int manager_state_cb(char *context, char *exten, int state, void *data)
3603 /* Notify managers of change */
3605 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
3607 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
3611 static int ast_manager_register_struct(struct manager_action *act)
3613 struct manager_action *cur, *prev = NULL;
3614 struct timespec tv = { 5, };
3616 if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
3617 ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
3620 AST_RWLIST_TRAVERSE(&actions, cur, list) {
3621 int ret = strcasecmp(cur->action, act->action);
3623 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
3624 AST_RWLIST_UNLOCK(&actions);
3627 if (ret > 0) { /* Insert these alphabetically */
3634 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
3636 AST_RWLIST_INSERT_HEAD(&actions, act, list);
3639 ast_verb(2, "Manager registered action %s\n", act->action);
3641 AST_RWLIST_UNLOCK(&actions);
3646 /*! \brief register a new command with manager, including online help. This is
3647 the preferred way to register a manager command */
3648 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
3650 struct manager_action *cur = NULL;
3652 if (!(cur = ast_calloc(1, sizeof(*cur)))) {
3656 cur->action = action;
3657 cur->authority = auth;
3659 cur->synopsis = synopsis;
3660 cur->description = description;
3662 if (ast_manager_register_struct(cur)) {
3670 END Doxygen group */
3673 * The following are support functions for AMI-over-http.
3674 * The common entry point is generic_http_callback(),
3675 * which extracts HTTP header and URI fields and reformats
3676 * them into AMI messages, locates a proper session
3677 * (using the mansession_id Cookie or GET variable),
3678 * and calls process_message() as for regular AMI clients.
3679 * When done, the output (which goes to a temporary file)
3680 * is read back into a buffer and reformatted as desired,
3681 * then fed back to the client over the original socket.
3684 enum output_format {
3690 static char *contenttype[] = {
3691 [FORMAT_RAW] = "plain",
3692 [FORMAT_HTML] = "html",
3693 [FORMAT_XML] = "xml",
3697 * locate an http session in the list. The search key (ident) is
3698 * the value of the mansession_id cookie (0 is not valid and means
3699 * a session on the AMI socket).
3701 static struct mansession_session *find_session(uint32_t ident, int incinuse)
3703 struct mansession_session *session;
3704 struct ao2_iterator i;
3710 i = ao2_iterator_init(sessions, 0);
3711 while ((session = ao2_iterator_next(&i))) {
3713 if (session->managerid == ident && !session->needdestroy) {
3714 ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
3715 unref_mansession(session);
3718 ao2_unlock(session);
3719 unref_mansession(session);
3726 * locate an http session in the list.
3727 * The search keys (nonce) and (username) is value from received
3728 * "Authorization" http header.
3729 * As well as in find_session() function, the value of the nonce can't be zero.
3730 * (0 meansi, that the session used for AMI socket connection).
3731 * Flag (stale) is set, if client used valid, but old, nonce value.
3734 static struct mansession_session *find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
3736 struct mansession_session *session;
3737 struct ao2_iterator i;
3739 if (nonce == 0 || username == NULL || stale == NULL) {
3743 i = ao2_iterator_init(sessions, 0);
3744 while ((session = ao2_iterator_next(&i))) {
3746 if (!strcasecmp(session->username, username) && session->managerid == nonce) {
3748 unref_mansession(session);
3750 } else if (!strcasecmp(session->username, username) && session->oldnonce == nonce) {
3752 unref_mansession(session);
3755 ao2_unlock(session);
3756 unref_mansession(session);
3761 int astman_is_authed(uint32_t ident)
3764 struct mansession_session *session;
3766 if (!(session = find_session(ident, 0)))
3769 authed = (session->authenticated != 0);
3771 ao2_unlock(session);