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 * Channel Management and more
30 /*! \addtogroup Group_AMI AMI functions
40 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <netinet/tcp.h>
45 #include <arpa/inet.h>
52 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
54 #include "asterisk/channel.h"
55 #include "asterisk/file.h"
56 #include "asterisk/manager.h"
57 #include "asterisk/config.h"
58 #include "asterisk/callerid.h"
59 #include "asterisk/lock.h"
60 #include "asterisk/logger.h"
61 #include "asterisk/options.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/http.h"
70 struct fast_originate_helper {
71 char tech[AST_MAX_MANHEADER_LEN];
72 char data[AST_MAX_MANHEADER_LEN];
74 char app[AST_MAX_APP];
75 char appdata[AST_MAX_MANHEADER_LEN];
76 char cid_name[AST_MAX_MANHEADER_LEN];
77 char cid_num[AST_MAX_MANHEADER_LEN];
78 char context[AST_MAX_CONTEXT];
79 char exten[AST_MAX_EXTENSION];
80 char idtext[AST_MAX_MANHEADER_LEN];
81 char account[AST_MAX_ACCOUNT_CODE];
83 struct ast_variable *vars;
90 struct eventqent *next;
94 static int enabled = 0;
95 static int portno = DEFAULT_MANAGER_PORT;
96 static int asock = -1;
97 static int displayconnects = 1;
98 static int timestampevents = 0;
99 static int httptimeout = 60;
102 AST_MUTEX_DEFINE_STATIC(sessionlock);
103 static int block_sockets = 0;
104 static int num_sessions = 0;
105 struct eventqent *master_eventq = NULL;
107 static struct permalias {
111 { EVENT_FLAG_SYSTEM, "system" },
112 { EVENT_FLAG_CALL, "call" },
113 { EVENT_FLAG_LOG, "log" },
114 { EVENT_FLAG_VERBOSE, "verbose" },
115 { EVENT_FLAG_COMMAND, "command" },
116 { EVENT_FLAG_AGENT, "agent" },
117 { EVENT_FLAG_USER, "user" },
122 static struct mansession {
123 /*! Execution thread */
125 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
127 /*! socket address */
128 struct sockaddr_in sin;
131 /*! Whether or not we're busy doing an action */
133 /*! Whether or not we're "dead" */
135 /*! Whether an HTTP manager is in use */
137 /*! Whether an HTTP session should be destroyed */
139 /*! Whether an HTTP session has someone waiting on events */
140 pthread_t waiting_thread;
141 /*! Unique manager identifer */
142 unsigned long managerid;
143 /*! Session timeout if HTTP */
144 time_t sessiontimeout;
145 /*! Output from manager interface */
147 /*! Logged in username */
149 /*! Authentication challenge */
151 /*! Authentication status */
153 /*! Authorization for reading */
155 /*! Authorization for writing */
158 char inbuf[AST_MAX_MANHEADER_LEN];
161 /* Queued events that we've not had the ability to send yet */
162 struct eventqent *eventq;
163 /* Timeout for ast_carefulwrite() */
165 struct mansession *next;
168 static struct manager_action *first_action = NULL;
169 AST_MUTEX_DEFINE_STATIC(actionlock);
171 /*! If you are calling ast_carefulwrite, it is assumed that you are calling
172 it on a file descriptor that _DOES_ have NONBLOCK set. This way,
173 there is only one system call made to do a write, unless we actually
174 have a need to wait. This way, we get better performance. */
175 int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
177 /* Try to write string, but wait no more than ms milliseconds
180 struct pollfd fds[1];
182 res = write(fd, s, len);
183 if ((res < 0) && (errno != EAGAIN)) {
193 fds[0].events = POLLOUT;
194 /* Wait until writable again */
195 res = poll(fds, 1, timeoutms);
203 /*! authority_to_str: Convert authority code to string with serveral options */
204 static char *authority_to_str(int authority, char *res, int reslen)
206 int running_total = 0, i;
207 memset(res, 0, reslen);
208 for (i=0; i<sizeof(perms) / sizeof(perms[0]) - 1; i++) {
209 if (authority & perms[i].num) {
211 strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
214 strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
215 running_total += strlen(perms[i].label);
218 if (ast_strlen_zero(res)) {
219 ast_copy_string(res, "<none>", reslen);
224 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
226 struct manager_action *cur;
230 ast_mutex_lock(&actionlock);
231 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
232 if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
233 ret = ast_strdup(cur->action);
234 break; /* make sure we exit even if ast_strdup() returns NULL */
237 ast_mutex_unlock(&actionlock);
241 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
243 while (*src && (*maxlen > 6)) {
246 strcpy(*dst, "<");
251 strcpy(*dst, ">");
256 strcpy(*dst, """);
261 strcpy(*dst, "'");
266 strcpy(*dst, "&");
271 *(*dst)++ = lower ? tolower(*src) : *src;
278 static char *xml_translate(char *in, struct ast_variable *vars)
280 struct ast_variable *v;
282 char *out, *tmp, *var, *val;
283 char *objtype = NULL;
294 if (!dest && !strcasecmp(v->name, "ajaxdest"))
296 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
304 for (x=0; in[x]; x++) {
307 else if (in[x] == '\n')
309 else if (strchr("&\"<>", in[x]))
312 len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&" */
319 while (*in && (*in >= 32))
322 if ((count > 3) && inobj) {
323 ast_build_string(&tmp, &len, " /></response>\n");
327 while (*in && (*in < 32)) {
332 val = strchr(var, ':');
339 ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
342 ast_build_string(&tmp, &len, " ");
343 xml_copy_escape(&tmp, &len, var, 1);
344 ast_build_string(&tmp, &len, "='");
345 xml_copy_escape(&tmp, &len, val, 0);
346 ast_build_string(&tmp, &len, "'");
351 ast_build_string(&tmp, &len, " /></response>\n");
355 static char *html_translate(char *in)
362 char *tmp, *var, *val, *out;
364 for (x=0; in[x]; x++) {
370 len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
377 while (*in && (*in >= 32))
380 if ((count % 4) == 0){
381 ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
384 while (*in && (*in < 32)) {
389 val = strchr(var, ':');
395 ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
402 void astman_append(struct mansession *s, const char *fmt, ...)
410 res = vasprintf(&stuff, fmt, ap);
413 ast_log(LOG_ERROR, "Memory allocation failure\n");
417 ast_carefulwrite(s->fd, stuff, strlen(stuff), s->writetimeout);
419 tmp = realloc(s->outputstr, (s->outputstr ? strlen(s->outputstr) : 0) + strlen(stuff) + 1);
424 strcat(s->outputstr, stuff);
430 static int handle_showmancmd(int fd, int argc, char *argv[])
432 struct manager_action *cur = first_action;
437 return RESULT_SHOWUSAGE;
438 ast_mutex_lock(&actionlock);
439 while (cur) { /* Walk the list of actions */
440 for (num = 3; num < argc; num++) {
441 if (!strcasecmp(cur->action, argv[num])) {
442 ast_cli(fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n", cur->action, cur->synopsis, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->description ? cur->description : "");
448 ast_mutex_unlock(&actionlock);
449 return RESULT_SUCCESS;
452 /*! \brief handle_showmancmds: CLI command */
453 /* Should change to "manager show commands" */
454 static int handle_showmancmds(int fd, int argc, char *argv[])
456 struct manager_action *cur = first_action;
458 char *format = " %-15.15s %-15.15s %-55.55s\n";
460 ast_mutex_lock(&actionlock);
461 ast_cli(fd, format, "Action", "Privilege", "Synopsis");
462 ast_cli(fd, format, "------", "---------", "--------");
463 while (cur) { /* Walk the list of actions */
464 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
468 ast_mutex_unlock(&actionlock);
469 return RESULT_SUCCESS;
472 /*! \brief handle_showmanconn: CLI command show manager connected */
473 /* Should change to "manager show connected" */
474 static int handle_showmanconn(int fd, int argc, char *argv[])
476 struct mansession *s;
477 char iabuf[INET_ADDRSTRLEN];
478 char *format = " %-15.15s %-15.15s\n";
479 ast_mutex_lock(&sessionlock);
481 ast_cli(fd, format, "Username", "IP Address");
483 ast_cli(fd, format,s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
487 ast_mutex_unlock(&sessionlock);
488 return RESULT_SUCCESS;
491 /*! \brief handle_showmanconn: CLI command show manager connected */
492 /* Should change to "manager show connected" */
493 static int handle_showmaneventq(int fd, int argc, char *argv[])
496 ast_mutex_lock(&sessionlock);
499 ast_cli(fd, "Usecount: %d\n",s->usecount);
500 ast_cli(fd, "Category: %d\n", s->category);
501 ast_cli(fd, "Event:\n%s", s->eventdata);
504 ast_mutex_unlock(&sessionlock);
505 return RESULT_SUCCESS;
508 static char showmancmd_help[] =
509 "Usage: show manager command <actionname>\n"
510 " Shows the detailed description for a specific Asterisk manager interface command.\n";
512 static char showmancmds_help[] =
513 "Usage: show manager commands\n"
514 " Prints a listing of all the available Asterisk manager interface commands.\n";
516 static char showmanconn_help[] =
517 "Usage: show manager connected\n"
518 " Prints a listing of the users that are currently connected to the\n"
519 "Asterisk manager interface.\n";
521 static char showmaneventq_help[] =
522 "Usage: show manager eventq\n"
523 " Prints a listing of all events pending in the Asterisk manger\n"
526 static struct ast_cli_entry show_mancmd_cli =
527 { { "show", "manager", "command", NULL },
528 handle_showmancmd, "Show a manager interface command", showmancmd_help, complete_show_mancmd };
530 static struct ast_cli_entry show_mancmds_cli =
531 { { "show", "manager", "commands", NULL },
532 handle_showmancmds, "List manager interface commands", showmancmds_help };
534 static struct ast_cli_entry show_manconn_cli =
535 { { "show", "manager", "connected", NULL },
536 handle_showmanconn, "Show connected manager interface users", showmanconn_help };
538 static struct ast_cli_entry show_maneventq_cli =
539 { { "show", "manager", "eventq", NULL },
540 handle_showmaneventq, "Show manager interface queued events", showmaneventq_help };
542 static void unuse_eventqent(struct eventqent *e)
544 /* XXX Need to atomically decrement the users. Change this to atomic_dec
545 one day when we have such a beast XXX */
547 ast_mutex_lock(&e->lock);
549 val = !e->usecount && e->next;
550 ast_mutex_unlock(&e->lock);
551 /* Wake up sleeping beauty */
553 pthread_kill(t, SIGURG);
556 static void free_session(struct mansession *s)
558 struct eventqent *eqe;
563 ast_mutex_destroy(&s->__lock);
566 s->eventq = s->eventq->next;
567 unuse_eventqent(eqe);
572 static void destroy_session(struct mansession *s)
574 struct mansession *cur, *prev = NULL;
575 ast_mutex_lock(&sessionlock);
585 prev->next = cur->next;
587 sessions = cur->next;
591 ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
592 ast_mutex_unlock(&sessionlock);
595 char *astman_get_header(struct message *m, char *var)
599 snprintf(cmp, sizeof(cmp), "%s: ", var);
600 for (x=0; x<m->hdrcount; x++)
601 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
602 return m->headers[x] + strlen(cmp);
606 struct ast_variable *astman_get_variables(struct message *m)
609 struct ast_variable *head = NULL, *cur;
613 AST_DECLARE_APP_ARGS(args,
614 AST_APP_ARG(vars)[32];
617 varlen = strlen("Variable: ");
619 for (x = 0; x < m->hdrcount; x++) {
620 if (strncasecmp("Variable: ", m->headers[x], varlen))
623 if (!(parse = ast_strdupa(m->headers[x] + varlen)))
626 AST_STANDARD_APP_ARGS(args, parse);
628 for (y = 0; y < args.argc; y++) {
631 var = val = ast_strdupa(args.vars[y]);
633 if (!val || ast_strlen_zero(var))
635 cur = ast_variable_new(var, val);
649 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
650 hold the session lock _or_ be running in an action callback (in which case s->busy will
651 be non-zero). In either of these cases, there is no need to lock-protect the session's
652 fd, since no other output will be sent (events will be queued), and no input will
653 be read until either the current action finishes or get_input() obtains the session
656 void astman_send_error(struct mansession *s, struct message *m, char *error)
658 char *id = astman_get_header(m,"ActionID");
660 astman_append(s, "Response: Error\r\n");
661 if (!ast_strlen_zero(id))
662 astman_append(s, "ActionID: %s\r\n", id);
663 astman_append(s, "Message: %s\r\n\r\n", error);
666 void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
668 char *id = astman_get_header(m,"ActionID");
670 astman_append(s, "Response: %s\r\n", resp);
671 if (!ast_strlen_zero(id))
672 astman_append(s, "ActionID: %s\r\n", id);
674 astman_append(s, "Message: %s\r\n\r\n", msg);
676 astman_append(s, "\r\n");
679 void astman_send_ack(struct mansession *s, struct message *m, char *msg)
681 astman_send_response(s, m, "Success", msg);
684 /*! Tells you if smallstr exists inside bigstr
685 which is delim by delim and uses no buf or stringsep
686 ast_instring("this|that|more","this",',') == 1;
688 feel free to move this to app.c -anthm */
689 static int ast_instring(char *bigstr, char *smallstr, char delim)
691 char *val = bigstr, *next;
694 if ((next = strchr(val, delim))) {
695 if (!strncmp(val, smallstr, (next - val)))
700 return !strcmp(smallstr, val);
702 } while (*(val = (next + 1)));
707 static int get_perm(char *instr)
714 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
715 if (ast_instring(instr, perms[x].label, ','))
721 static int ast_is_number(char *string)
728 for (x=0; x < strlen(string); x++) {
729 if (!(string[x] >= 48 && string[x] <= 57)) {
735 return ret ? atoi(string) : 0;
738 static int ast_strings_to_mask(char *string)
742 x = ast_is_number(string);
746 } else if (ast_strlen_zero(string)) {
748 } else if (ast_false(string)) {
750 } else if (ast_true(string)) {
752 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
756 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
757 if (ast_instring(string, perms[x].label, ','))
766 Rather than braindead on,off this now can also accept a specific int mask value
767 or a ',' delim list of mask strings (the same as manager.conf) -anthm
770 static int set_eventmask(struct mansession *s, char *eventmask)
772 int maskint = ast_strings_to_mask(eventmask);
774 ast_mutex_lock(&s->__lock);
776 s->send_events = maskint;
777 ast_mutex_unlock(&s->__lock);
782 static int authenticate(struct mansession *s, struct message *m)
784 struct ast_config *cfg;
785 char iabuf[INET_ADDRSTRLEN];
787 char *user = astman_get_header(m, "Username");
788 char *pass = astman_get_header(m, "Secret");
789 char *authtype = astman_get_header(m, "AuthType");
790 char *key = astman_get_header(m, "Key");
791 char *events = astman_get_header(m, "Events");
793 cfg = ast_config_load("manager.conf");
796 cat = ast_category_browse(cfg, NULL);
798 if (strcasecmp(cat, "general")) {
800 if (!strcasecmp(cat, user)) {
801 struct ast_variable *v;
802 struct ast_ha *ha = NULL;
803 char *password = NULL;
804 v = ast_variable_browse(cfg, cat);
806 if (!strcasecmp(v->name, "secret")) {
808 } else if (!strcasecmp(v->name, "permit") ||
809 !strcasecmp(v->name, "deny")) {
810 ha = ast_append_ha(v->name, v->value, ha);
811 } else if (!strcasecmp(v->name, "writetimeout")) {
812 int val = atoi(v->value);
815 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
817 s->writetimeout = val;
822 if (ha && !ast_apply_ha(ha, &(s->sin))) {
823 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
825 ast_config_destroy(cfg);
829 if (!strcasecmp(authtype, "MD5")) {
830 if (!ast_strlen_zero(key) && s->challenge) {
833 char md5key[256] = "";
834 struct MD5Context md5;
835 unsigned char digest[16];
837 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
838 MD5Update(&md5, (unsigned char *) password, strlen(password));
839 MD5Final(digest, &md5);
841 len += sprintf(md5key + len, "%2.2x", digest[x]);
842 if (!strcmp(md5key, key))
845 ast_config_destroy(cfg);
849 } else if (password && !strcmp(password, pass)) {
852 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
853 ast_config_destroy(cfg);
858 cat = ast_category_browse(cfg, cat);
861 ast_copy_string(s->username, cat, sizeof(s->username));
862 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
863 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
864 ast_config_destroy(cfg);
866 set_eventmask(s, events);
869 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
870 ast_config_destroy(cfg);
874 /*! \brief PING: Manager PING */
875 static char mandescr_ping[] =
876 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
877 " manager connection open.\n"
880 static int action_ping(struct mansession *s, struct message *m)
882 astman_send_response(s, m, "Pong", NULL);
886 /*! \brief WAITEVENT: Manager WAITEVENT */
887 static char mandescr_waitevent[] =
888 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
889 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
890 "session, events will be generated and queued.\n"
892 " Timeout: Maximum time to wait for events\n";
894 static int action_waitevent(struct mansession *s, struct message *m)
896 char *timeouts = astman_get_header(m, "Timeout");
897 int timeout = -1, max;
901 struct eventqent *eqe;
902 char *id = astman_get_header(m,"ActionID");
903 char idText[256] = "";
905 if (!ast_strlen_zero(id))
906 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
908 if (!ast_strlen_zero(timeouts)) {
909 sscanf(timeouts, "%i", &timeout);
912 ast_mutex_lock(&s->__lock);
913 if (s->waiting_thread != AST_PTHREADT_NULL) {
914 pthread_kill(s->waiting_thread, SIGURG);
916 if (s->sessiontimeout) {
918 max = s->sessiontimeout - now - 10;
921 if ((timeout < 0) || (timeout > max))
925 /* Once waitevent is called, always queue events from now on */
929 ast_mutex_unlock(&s->__lock);
930 s->waiting_thread = pthread_self();
932 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
933 for (x=0; ((x < timeout) || (timeout < 0)); x++) {
934 ast_mutex_lock(&s->__lock);
935 if (s->eventq && s->eventq->next)
937 if (s->waiting_thread != pthread_self())
941 ast_mutex_unlock(&s->__lock);
945 if (ast_wait_for_input(s->fd, 1000))
952 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
953 ast_mutex_lock(&s->__lock);
954 if (s->waiting_thread == pthread_self()) {
955 astman_send_response(s, m, "Success", "Waiting for Event...");
956 /* Only show events if we're the most recent waiter */
957 while(s->eventq->next) {
958 eqe = s->eventq->next;
959 if (((s->readperm & eqe->category) == eqe->category) &&
960 ((s->send_events & eqe->category) == eqe->category)) {
961 astman_append(s, "%s", eqe->eventdata);
963 unuse_eventqent(s->eventq);
967 "Event: WaitEventComplete\r\n"
970 s->waiting_thread = AST_PTHREADT_NULL;
972 ast_log(LOG_DEBUG, "Abandoning event request!\n");
974 ast_mutex_unlock(&s->__lock);
978 static char mandescr_listcommands[] =
979 "Description: Returns the action name and synopsis for every\n"
980 " action that is available to the user\n"
983 static int action_listcommands(struct mansession *s, struct message *m)
985 struct manager_action *cur = first_action;
986 char idText[256] = "";
988 char *id = astman_get_header(m,"ActionID");
990 if (!ast_strlen_zero(id))
991 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
992 astman_append(s, "Response: Success\r\n%s", idText);
993 ast_mutex_lock(&actionlock);
994 while (cur) { /* Walk the list of actions */
995 if ((s->writeperm & cur->authority) == cur->authority)
996 astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
999 ast_mutex_unlock(&actionlock);
1000 astman_append(s, "\r\n");
1005 static char mandescr_events[] =
1006 "Description: Enable/Disable sending of events to this manager\n"
1009 " EventMask: 'on' if all events should be sent,\n"
1010 " 'off' if no events should be sent,\n"
1011 " 'system,call,log' to select which flags events should have to be sent.\n";
1013 static int action_events(struct mansession *s, struct message *m)
1015 char *mask = astman_get_header(m, "EventMask");
1018 res = set_eventmask(s, mask);
1020 astman_send_response(s, m, "Events On", NULL);
1022 astman_send_response(s, m, "Events Off", NULL);
1027 static char mandescr_logoff[] =
1028 "Description: Logoff this manager session\n"
1029 "Variables: NONE\n";
1031 static int action_logoff(struct mansession *s, struct message *m)
1033 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1037 static char mandescr_hangup[] =
1038 "Description: Hangup a channel\n"
1040 " Channel: The channel name to be hungup\n";
1042 static int action_hangup(struct mansession *s, struct message *m)
1044 struct ast_channel *c = NULL;
1045 char *name = astman_get_header(m, "Channel");
1046 if (ast_strlen_zero(name)) {
1047 astman_send_error(s, m, "No channel specified");
1050 c = ast_get_channel_by_name_locked(name);
1052 astman_send_error(s, m, "No such channel");
1055 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1056 ast_mutex_unlock(&c->lock);
1057 astman_send_ack(s, m, "Channel Hungup");
1061 static char mandescr_setvar[] =
1062 "Description: Set a global or local channel variable.\n"
1063 "Variables: (Names marked with * are required)\n"
1064 " Channel: Channel to set variable for\n"
1065 " *Variable: Variable name\n"
1068 static int action_setvar(struct mansession *s, struct message *m)
1070 struct ast_channel *c = NULL;
1071 char *name = astman_get_header(m, "Channel");
1072 char *varname = astman_get_header(m, "Variable");
1073 char *varval = astman_get_header(m, "Value");
1075 if (ast_strlen_zero(varname)) {
1076 astman_send_error(s, m, "No variable specified");
1080 if (ast_strlen_zero(varval)) {
1081 astman_send_error(s, m, "No value specified");
1085 if (!ast_strlen_zero(name)) {
1086 c = ast_get_channel_by_name_locked(name);
1088 astman_send_error(s, m, "No such channel");
1093 pbx_builtin_setvar_helper(c, varname, varval);
1096 ast_mutex_unlock(&c->lock);
1098 astman_send_ack(s, m, "Variable Set");
1103 static char mandescr_getvar[] =
1104 "Description: Get the value of a global or local channel variable.\n"
1105 "Variables: (Names marked with * are required)\n"
1106 " Channel: Channel to read variable from\n"
1107 " *Variable: Variable name\n"
1108 " ActionID: Optional Action id for message matching.\n";
1110 static int action_getvar(struct mansession *s, struct message *m)
1112 struct ast_channel *c = NULL;
1113 char *name = astman_get_header(m, "Channel");
1114 char *varname = astman_get_header(m, "Variable");
1115 char *id = astman_get_header(m,"ActionID");
1117 char workspace[1024];
1119 if (ast_strlen_zero(varname)) {
1120 astman_send_error(s, m, "No variable specified");
1124 if (!ast_strlen_zero(name)) {
1125 c = ast_get_channel_by_name_locked(name);
1127 astman_send_error(s, m, "No such channel");
1132 if (varname[strlen(varname) - 1] == ')') {
1133 ast_func_read(c, varname, workspace, sizeof(workspace));
1135 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1139 ast_mutex_unlock(&c->lock);
1140 astman_append(s, "Response: Success\r\n"
1141 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1142 if (!ast_strlen_zero(id))
1143 astman_append(s, "ActionID: %s\r\n",id);
1144 astman_append(s, "\r\n");
1150 /*! \brief action_status: Manager "status" command to show channels */
1151 /* Needs documentation... */
1152 static int action_status(struct mansession *s, struct message *m)
1154 char *id = astman_get_header(m,"ActionID");
1155 char *name = astman_get_header(m,"Channel");
1156 char idText[256] = "";
1157 struct ast_channel *c;
1159 struct timeval now = ast_tvnow();
1160 long elapsed_seconds = 0;
1161 int all = ast_strlen_zero(name); /* set if we want all channels */
1163 astman_send_ack(s, m, "Channel status will follow");
1164 if (!ast_strlen_zero(id))
1165 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1167 c = ast_channel_walk_locked(NULL);
1169 c = ast_get_channel_by_name_locked(name);
1171 astman_send_error(s, m, "No such channel");
1175 /* if we look by name, we break after the first iteration */
1178 snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1183 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1187 "Privilege: Call\r\n"
1189 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1190 "CallerIDNum: %s\r\n"
1191 "CallerIDName: %s\r\n"
1203 c->cid.cid_num ? c->cid.cid_num : "<unknown>",
1204 c->cid.cid_num ? c->cid.cid_num : "<unknown>",
1205 c->cid.cid_name ? c->cid.cid_name : "<unknown>",
1207 ast_state2str(c->_state), c->context,
1208 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1212 "Privilege: Call\r\n"
1214 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1215 "CallerIDNum: %s\r\n"
1216 "CallerIDName: %s\r\n"
1224 c->cid.cid_num ? c->cid.cid_num : "<unknown>",
1225 c->cid.cid_num ? c->cid.cid_num : "<unknown>",
1226 c->cid.cid_name ? c->cid.cid_name : "<unknown>",
1228 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1230 ast_mutex_unlock(&c->lock);
1233 c = ast_channel_walk_locked(c);
1236 "Event: StatusComplete\r\n"
1242 static char mandescr_redirect[] =
1243 "Description: Redirect (transfer) a call.\n"
1244 "Variables: (Names marked with * are required)\n"
1245 " *Channel: Channel to redirect\n"
1246 " ExtraChannel: Second call leg to transfer (optional)\n"
1247 " *Exten: Extension to transfer to\n"
1248 " *Context: Context to transfer to\n"
1249 " *Priority: Priority to transfer to\n"
1250 " ActionID: Optional Action id for message matching.\n";
1252 /*! \brief action_redirect: The redirect manager command */
1253 static int action_redirect(struct mansession *s, struct message *m)
1255 char *name = astman_get_header(m, "Channel");
1256 char *name2 = astman_get_header(m, "ExtraChannel");
1257 char *exten = astman_get_header(m, "Exten");
1258 char *context = astman_get_header(m, "Context");
1259 char *priority = astman_get_header(m, "Priority");
1260 struct ast_channel *chan, *chan2 = NULL;
1264 if (ast_strlen_zero(name)) {
1265 astman_send_error(s, m, "Channel not specified");
1268 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1269 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1270 astman_send_error(s, m, "Invalid priority\n");
1274 /* XXX watch out, possible deadlock!!! */
1275 chan = ast_get_channel_by_name_locked(name);
1278 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1279 astman_send_error(s, m, buf);
1282 if (!ast_strlen_zero(name2))
1283 chan2 = ast_get_channel_by_name_locked(name2);
1284 res = ast_async_goto(chan, context, exten, pi);
1286 if (!ast_strlen_zero(name2)) {
1288 res = ast_async_goto(chan2, context, exten, pi);
1292 astman_send_ack(s, m, "Dual Redirect successful");
1294 astman_send_error(s, m, "Secondary redirect failed");
1296 astman_send_ack(s, m, "Redirect successful");
1298 astman_send_error(s, m, "Redirect failed");
1300 ast_mutex_unlock(&chan->lock);
1302 ast_mutex_unlock(&chan2->lock);
1306 static char mandescr_command[] =
1307 "Description: Run a CLI command.\n"
1308 "Variables: (Names marked with * are required)\n"
1309 " *Command: Asterisk CLI command to run\n"
1310 " ActionID: Optional Action id for message matching.\n";
1312 /*! \brief action_command: Manager command "command" - execute CLI command */
1313 static int action_command(struct mansession *s, struct message *m)
1315 char *cmd = astman_get_header(m, "Command");
1316 char *id = astman_get_header(m, "ActionID");
1317 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1318 if (!ast_strlen_zero(id))
1319 astman_append(s, "ActionID: %s\r\n", id);
1320 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1321 ast_cli_command(s->fd, cmd);
1322 astman_append(s, "--END COMMAND--\r\n\r\n");
1326 static void *fast_originate(void *data)
1328 struct fast_originate_helper *in = data;
1331 struct ast_channel *chan = NULL;
1333 if (!ast_strlen_zero(in->app)) {
1334 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1335 S_OR(in->cid_num, NULL),
1336 S_OR(in->cid_name, NULL),
1337 in->vars, in->account, &chan);
1339 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1340 S_OR(in->cid_num, NULL),
1341 S_OR(in->cid_name, NULL),
1342 in->vars, in->account, &chan);
1345 /* Tell the manager what happened with the channel */
1346 manager_event(EVENT_FLAG_CALL,
1347 res ? "OriginateSuccess" : "OriginateFailure",
1349 "Channel: %s/%s\r\n"
1354 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1355 "CallerIDNum: %s\r\n"
1356 "CallerIDName: %s\r\n",
1357 in->idtext, in->tech, in->data, in->context, in->exten, reason,
1358 chan ? chan->uniqueid : "<null>",
1359 in->cid_num ? in->cid_num : "<unknown>",
1360 in->cid_num ? in->cid_num : "<unknown>",
1361 in->cid_name ? in->cid_name : "<unknown>"
1364 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1366 ast_mutex_unlock(&chan->lock);
1371 static char mandescr_originate[] =
1372 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1373 " Application/Data\n"
1374 "Variables: (Names marked with * are required)\n"
1375 " *Channel: Channel name to call\n"
1376 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1377 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1378 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1379 " Application: Application to use\n"
1380 " Data: Data to use (requires 'Application')\n"
1381 " Timeout: How long to wait for call to be answered (in ms)\n"
1382 " CallerID: Caller ID to be set on the outgoing channel\n"
1383 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1384 " Account: Account code\n"
1385 " Async: Set to 'true' for fast origination\n";
1387 static int action_originate(struct mansession *s, struct message *m)
1389 char *name = astman_get_header(m, "Channel");
1390 char *exten = astman_get_header(m, "Exten");
1391 char *context = astman_get_header(m, "Context");
1392 char *priority = astman_get_header(m, "Priority");
1393 char *timeout = astman_get_header(m, "Timeout");
1394 char *callerid = astman_get_header(m, "CallerID");
1395 char *account = astman_get_header(m, "Account");
1396 char *app = astman_get_header(m, "Application");
1397 char *appdata = astman_get_header(m, "Data");
1398 char *async = astman_get_header(m, "Async");
1399 char *id = astman_get_header(m, "ActionID");
1400 struct ast_variable *vars = astman_get_variables(m);
1402 char *l = NULL, *n = NULL;
1411 pthread_attr_t attr;
1413 astman_send_error(s, m, "Channel not specified");
1416 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1417 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1418 astman_send_error(s, m, "Invalid priority\n");
1422 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1423 astman_send_error(s, m, "Invalid timeout\n");
1426 ast_copy_string(tmp, name, sizeof(tmp));
1428 data = strchr(tmp, '/');
1430 astman_send_error(s, m, "Invalid channel\n");
1434 ast_copy_string(tmp2, callerid, sizeof(tmp2));
1435 ast_callerid_parse(tmp2, &n, &l);
1437 if (ast_strlen_zero(n))
1441 ast_shrink_phone_number(l);
1442 if (ast_strlen_zero(l))
1445 if (ast_true(async)) {
1446 struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper));
1450 memset(fast, 0, sizeof(struct fast_originate_helper));
1451 if (!ast_strlen_zero(id))
1452 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1453 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1454 ast_copy_string(fast->data, data, sizeof(fast->data));
1455 ast_copy_string(fast->app, app, sizeof(fast->app));
1456 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1458 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1460 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1462 ast_copy_string(fast->context, context, sizeof(fast->context));
1463 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1464 ast_copy_string(fast->account, account, sizeof(fast->account));
1466 fast->priority = pi;
1467 pthread_attr_init(&attr);
1468 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1469 if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1475 } else if (!ast_strlen_zero(app)) {
1476 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1478 if (exten && context && pi)
1479 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1481 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1486 astman_send_ack(s, m, "Originate successfully queued");
1488 astman_send_error(s, m, "Originate failed");
1492 /*! \brief Help text for manager command mailboxstatus
1494 static char mandescr_mailboxstatus[] =
1495 "Description: Checks a voicemail account for status.\n"
1496 "Variables: (Names marked with * are required)\n"
1497 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1498 " ActionID: Optional ActionID for message matching.\n"
1499 "Returns number of messages.\n"
1500 " Message: Mailbox Status\n"
1501 " Mailbox: <mailboxid>\n"
1502 " Waiting: <count>\n"
1505 static int action_mailboxstatus(struct mansession *s, struct message *m)
1507 char *mailbox = astman_get_header(m, "Mailbox");
1508 char *id = astman_get_header(m,"ActionID");
1509 char idText[256] = "";
1511 if (ast_strlen_zero(mailbox)) {
1512 astman_send_error(s, m, "Mailbox not specified");
1515 if (!ast_strlen_zero(id))
1516 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1517 ret = ast_app_has_voicemail(mailbox, NULL);
1518 astman_append(s, "Response: Success\r\n"
1520 "Message: Mailbox Status\r\n"
1522 "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1526 static char mandescr_mailboxcount[] =
1527 "Description: Checks a voicemail account for new messages.\n"
1528 "Variables: (Names marked with * are required)\n"
1529 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1530 " ActionID: Optional ActionID for message matching.\n"
1531 "Returns number of new and old messages.\n"
1532 " Message: Mailbox Message Count\n"
1533 " Mailbox: <mailboxid>\n"
1534 " NewMessages: <count>\n"
1535 " OldMessages: <count>\n"
1537 static int action_mailboxcount(struct mansession *s, struct message *m)
1539 char *mailbox = astman_get_header(m, "Mailbox");
1540 char *id = astman_get_header(m,"ActionID");
1541 char idText[256] = "";
1542 int newmsgs = 0, oldmsgs = 0;
1543 if (ast_strlen_zero(mailbox)) {
1544 astman_send_error(s, m, "Mailbox not specified");
1547 ast_app_messagecount(mailbox, &newmsgs, &oldmsgs);
1548 if (!ast_strlen_zero(id)) {
1549 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1551 astman_append(s, "Response: Success\r\n"
1553 "Message: Mailbox Message Count\r\n"
1555 "NewMessages: %d\r\n"
1556 "OldMessages: %d\r\n"
1558 idText,mailbox, newmsgs, oldmsgs);
1562 static char mandescr_extensionstate[] =
1563 "Description: Report the extension state for given extension.\n"
1564 " If the extension has a hint, will use devicestate to check\n"
1565 " the status of the device connected to the extension.\n"
1566 "Variables: (Names marked with * are required)\n"
1567 " *Exten: Extension to check state on\n"
1568 " *Context: Context for extension\n"
1569 " ActionId: Optional ID for this transaction\n"
1570 "Will return an \"Extension Status\" message.\n"
1571 "The response will include the hint for the extension and the status.\n";
1573 static int action_extensionstate(struct mansession *s, struct message *m)
1575 char *exten = astman_get_header(m, "Exten");
1576 char *context = astman_get_header(m, "Context");
1577 char *id = astman_get_header(m,"ActionID");
1578 char idText[256] = "";
1579 char hint[256] = "";
1581 if (ast_strlen_zero(exten)) {
1582 astman_send_error(s, m, "Extension not specified");
1585 if (ast_strlen_zero(context))
1586 context = "default";
1587 status = ast_extension_state(NULL, context, exten);
1588 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1589 if (!ast_strlen_zero(id)) {
1590 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1592 astman_append(s, "Response: Success\r\n"
1594 "Message: Extension Status\r\n"
1598 "Status: %d\r\n\r\n",
1599 idText,exten, context, hint, status);
1603 static char mandescr_timeout[] =
1604 "Description: Hangup a channel after a certain time.\n"
1605 "Variables: (Names marked with * are required)\n"
1606 " *Channel: Channel name to hangup\n"
1607 " *Timeout: Maximum duration of the call (sec)\n"
1608 "Acknowledges set time with 'Timeout Set' message\n";
1610 static int action_timeout(struct mansession *s, struct message *m)
1612 struct ast_channel *c = NULL;
1613 char *name = astman_get_header(m, "Channel");
1614 int timeout = atoi(astman_get_header(m, "Timeout"));
1615 if (ast_strlen_zero(name)) {
1616 astman_send_error(s, m, "No channel specified");
1620 astman_send_error(s, m, "No timeout specified");
1623 c = ast_get_channel_by_name_locked(name);
1625 astman_send_error(s, m, "No such channel");
1628 ast_channel_setwhentohangup(c, timeout);
1629 ast_mutex_unlock(&c->lock);
1630 astman_send_ack(s, m, "Timeout Set");
1634 static int process_events(struct mansession *s)
1636 struct eventqent *eqe;
1638 ast_mutex_lock(&s->__lock);
1642 s->eventq = master_eventq;
1643 while(s->eventq->next) {
1644 eqe = s->eventq->next;
1645 if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
1646 ((s->send_events & eqe->category) == eqe->category)) {
1647 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
1650 unuse_eventqent(s->eventq);
1654 ast_mutex_unlock(&s->__lock);
1658 static int process_message(struct mansession *s, struct message *m)
1660 char action[80] = "";
1661 struct manager_action *tmp = first_action;
1662 char *id = astman_get_header(m,"ActionID");
1663 char idText[256] = "";
1664 char iabuf[INET_ADDRSTRLEN];
1667 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1668 ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
1670 if (ast_strlen_zero(action)) {
1671 astman_send_error(s, m, "Missing action in request");
1674 if (!ast_strlen_zero(id)) {
1675 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1677 if (!s->authenticated) {
1678 if (!strcasecmp(action, "Challenge")) {
1680 authtype = astman_get_header(m, "AuthType");
1681 if (!strcasecmp(authtype, "MD5")) {
1682 if (ast_strlen_zero(s->challenge))
1683 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1684 ast_mutex_lock(&s->__lock);
1685 astman_append(s, "Response: Success\r\n"
1687 "Challenge: %s\r\n\r\n",
1688 idText, s->challenge);
1689 ast_mutex_unlock(&s->__lock);
1692 astman_send_error(s, m, "Must specify AuthType");
1695 } else if (!strcasecmp(action, "Login")) {
1696 if (authenticate(s, m)) {
1698 astman_send_error(s, m, "Authentication failed");
1701 s->authenticated = 1;
1702 if (option_verbose > 1) {
1703 if (displayconnects) {
1704 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1707 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1708 astman_send_ack(s, m, "Authentication accepted");
1710 } else if (!strcasecmp(action, "Logoff")) {
1711 astman_send_ack(s, m, "See ya");
1714 astman_send_error(s, m, "Authentication Required");
1716 ast_mutex_lock(&s->__lock);
1718 ast_mutex_unlock(&s->__lock);
1720 if (!strcasecmp(action, tmp->action)) {
1721 if ((s->writeperm & tmp->authority) == tmp->authority) {
1722 if (tmp->func(s, m))
1725 astman_send_error(s, m, "Permission denied");
1732 astman_send_error(s, m, "Invalid/unknown command");
1736 return process_events(s);
1739 static int get_input(struct mansession *s, char *output)
1741 /* output must have at least sizeof(s->inbuf) space */
1744 struct pollfd fds[1];
1745 char iabuf[INET_ADDRSTRLEN];
1746 for (x = 1; x < s->inlen; x++) {
1747 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
1748 /* Copy output data up to and including \r\n */
1749 memcpy(output, s->inbuf, x + 1);
1750 /* Add trailing \0 */
1752 /* Move remaining data back to the front */
1753 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
1754 s->inlen -= (x + 1);
1758 if (s->inlen >= sizeof(s->inbuf) - 1) {
1759 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf);
1763 fds[0].events = POLLIN;
1765 ast_mutex_lock(&s->__lock);
1766 s->waiting_thread = pthread_self();
1767 ast_mutex_unlock(&s->__lock);
1769 res = poll(fds, 1, -1);
1771 ast_mutex_lock(&s->__lock);
1772 s->waiting_thread = AST_PTHREADT_NULL;
1773 ast_mutex_unlock(&s->__lock);
1775 if (errno == EINTR) {
1780 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
1782 } else if (res > 0) {
1783 ast_mutex_lock(&s->__lock);
1784 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
1785 ast_mutex_unlock(&s->__lock);
1792 s->inbuf[s->inlen] = '\0';
1796 static void *session_do(void *data)
1798 struct mansession *s = data;
1800 char iabuf[INET_ADDRSTRLEN];
1803 ast_mutex_lock(&s->__lock);
1804 astman_append(s, "Asterisk Call Manager/1.0\r\n");
1805 ast_mutex_unlock(&s->__lock);
1806 memset(&m, 0, sizeof(m));
1808 res = get_input(s, m.headers[m.hdrcount]);
1810 /* Strip trailing \r\n */
1811 if (strlen(m.headers[m.hdrcount]) < 2)
1813 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
1814 if (ast_strlen_zero(m.headers[m.hdrcount])) {
1815 if (process_message(s, &m))
1817 memset(&m, 0, sizeof(m));
1818 } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
1820 } else if (res < 0) {
1822 } else if (s->eventq->next) {
1823 if (process_events(s))
1827 if (s->authenticated) {
1828 if (option_verbose > 1) {
1829 if (displayconnects)
1830 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1832 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1834 if (option_verbose > 1) {
1835 if (displayconnects)
1836 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1838 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1844 static void *accept_thread(void *ignore)
1847 struct sockaddr_in sin;
1849 struct eventqent *eqe;
1850 struct mansession *s, *prev = NULL, *next;
1854 pthread_attr_t attr;
1856 struct pollfd pfds[1];
1857 char iabuf[INET_ADDRSTRLEN];
1859 pthread_attr_init(&attr);
1860 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1864 ast_mutex_lock(&sessionlock);
1869 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
1875 if (s->authenticated && (option_verbose > 1) && displayconnects) {
1876 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
1877 s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1884 /* Purge master event queue of old, unused events, but make sure we
1885 always keep at least one in the queue */
1886 eqe = master_eventq;
1887 while (master_eventq->next && !master_eventq->usecount) {
1888 eqe = master_eventq;
1889 master_eventq = master_eventq->next;
1892 ast_mutex_unlock(&sessionlock);
1894 sinlen = sizeof(sin);
1896 pfds[0].events = POLLIN;
1897 /* Wait for something to happen, but timeout every few seconds so
1898 we can ditch any old manager sessions */
1899 if (poll(pfds, 1, 5000) < 1)
1901 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
1903 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
1906 p = getprotobyname("tcp");
1908 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
1909 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
1912 s = malloc(sizeof(struct mansession));
1914 ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
1917 memset(s, 0, sizeof(struct mansession));
1918 memcpy(&s->sin, &sin, sizeof(sin));
1919 s->writetimeout = 100;
1920 s->waiting_thread = AST_PTHREADT_NULL;
1922 if (!block_sockets) {
1923 /* For safety, make sure socket is non-blocking */
1924 flags = fcntl(as, F_GETFL);
1925 fcntl(as, F_SETFL, flags | O_NONBLOCK);
1927 ast_mutex_init(&s->__lock);
1929 s->send_events = -1;
1930 ast_mutex_lock(&sessionlock);
1934 /* Find the last place in the master event queue and hook ourselves
1936 s->eventq = master_eventq;
1937 while(s->eventq->next)
1938 s->eventq = s->eventq->next;
1939 ast_mutex_lock(&s->eventq->lock);
1940 s->eventq->usecount++;
1941 ast_mutex_unlock(&s->eventq->lock);
1942 ast_mutex_unlock(&sessionlock);
1943 if (ast_pthread_create(&s->t, &attr, session_do, s))
1946 pthread_attr_destroy(&attr);
1950 static int append_event(const char *str, int category)
1952 struct eventqent *tmp, *prev = NULL;
1953 tmp = malloc(sizeof(struct eventqent) + strlen(str));
1955 ast_mutex_init(&tmp->lock);
1957 tmp->category = category;
1958 strcpy(tmp->eventdata, str);
1959 if (master_eventq) {
1960 prev = master_eventq;
1965 master_eventq = tmp;
1967 tmp->usecount = num_sessions;
1973 /*! \brief manager_event: Send AMI event to client */
1974 int manager_event(int category, const char *event, const char *fmt, ...)
1976 struct mansession *s;
1978 char tmp[4096] = "";
1979 char *tmp_next = tmp;
1980 size_t tmp_left = sizeof(tmp) - 2;
1984 /* Abort if there aren't any manager sessions */
1988 ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
1989 event, authority_to_str(category, auth, sizeof(auth)));
1990 if (timestampevents) {
1992 ast_build_string(&tmp_next, &tmp_left, "Timestamp: %ld.%06lu\r\n",
1993 now.tv_sec, (unsigned long) now.tv_usec);
1996 ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
2002 ast_mutex_lock(&sessionlock);
2003 /* Append even to master list and wake up any sleeping sessions */
2004 append_event(tmp, category);
2005 for (s = sessions; s; s = s->next) {
2006 ast_mutex_lock(&s->__lock);
2007 if (s->waiting_thread != AST_PTHREADT_NULL)
2008 pthread_kill(s->waiting_thread, SIGURG);
2009 ast_mutex_unlock(&s->__lock);
2011 ast_mutex_unlock(&sessionlock);
2016 int ast_manager_unregister( char *action )
2018 struct manager_action *cur = first_action, *prev = first_action;
2020 ast_mutex_lock(&actionlock);
2022 if (!strcasecmp(action, cur->action)) {
2023 prev->next = cur->next;
2025 if (option_verbose > 1)
2026 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2027 ast_mutex_unlock(&actionlock);
2033 ast_mutex_unlock(&actionlock);
2037 static int manager_state_cb(char *context, char *exten, int state, void *data)
2039 /* Notify managers of change */
2040 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2044 static int ast_manager_register_struct(struct manager_action *act)
2046 struct manager_action *cur = first_action, *prev = NULL;
2049 ast_mutex_lock(&actionlock);
2050 while (cur) { /* Walk the list of actions */
2051 ret = strcasecmp(cur->action, act->action);
2053 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2054 ast_mutex_unlock(&actionlock);
2056 } else if (ret > 0) {
2057 /* Insert these alphabetically */
2059 act->next = prev->next;
2062 act->next = first_action;
2079 if (option_verbose > 1)
2080 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2081 ast_mutex_unlock(&actionlock);
2085 /*! \brief register a new command with manager, including online help. This is
2086 the preferred way to register a manager command */
2087 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2089 struct manager_action *cur;
2091 cur = malloc(sizeof(struct manager_action));
2093 ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
2096 cur->action = action;
2097 cur->authority = auth;
2099 cur->synopsis = synopsis;
2100 cur->description = description;
2103 ast_manager_register_struct(cur);
2108 END Doxygen group */
2110 static struct mansession *find_session(unsigned long ident)
2112 struct mansession *s;
2113 ast_mutex_lock(&sessionlock);
2116 ast_mutex_lock(&s->__lock);
2117 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2121 ast_mutex_unlock(&s->__lock);
2124 ast_mutex_unlock(&sessionlock);
2129 static void vars2msg(struct message *m, struct ast_variable *vars)
2132 for (x = 0; vars && (x < AST_MAX_MANHEADERS); x++, vars = vars->next) {
2135 m->hdrcount = x + 1;
2136 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2140 #define FORMAT_RAW 0
2141 #define FORMAT_HTML 1
2142 #define FORMAT_XML 2
2144 static char *contenttype[] = { "plain", "html", "xml" };
2146 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2148 struct mansession *s = NULL;
2149 unsigned long ident = 0;
2150 char workspace[256];
2152 char iabuf[INET_ADDRSTRLEN];
2153 size_t len = sizeof(workspace);
2155 char *c = workspace;
2156 char *retval = NULL;
2158 struct ast_variable *v;
2162 if (!strcasecmp(v->name, "mansession_id")) {
2163 sscanf(v->value, "%lx", &ident);
2168 s = find_session(ident);
2171 /* Create new session */
2172 s = calloc(1, sizeof(struct mansession));
2173 memcpy(&s->sin, requestor, sizeof(s->sin));
2175 s->waiting_thread = AST_PTHREADT_NULL;
2177 ast_mutex_init(&s->__lock);
2178 ast_mutex_lock(&s->__lock);
2179 ast_mutex_lock(&sessionlock);
2181 s->managerid = rand() | (unsigned long)s;
2185 /* Hook into the last spot in the event queue */
2186 s->eventq = master_eventq;
2187 while (s->eventq->next)
2188 s->eventq = s->eventq->next;
2189 ast_mutex_lock(&s->eventq->lock);
2190 s->eventq->usecount++;
2191 ast_mutex_unlock(&s->eventq->lock);
2192 ast_mutex_unlock(&sessionlock);
2195 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2196 time(&s->sessiontimeout);
2197 if (!s->authenticated && (httptimeout > 5))
2198 s->sessiontimeout += 5;
2200 s->sessiontimeout += httptimeout;
2201 ast_mutex_unlock(&s->__lock);
2203 memset(&m, 0, sizeof(m));
2206 ast_build_string(&c, &len, "Content-type: text/%s\n", contenttype[format]);
2207 sprintf(tmp, "%08lx", s->managerid);
2208 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2209 if (format == FORMAT_HTML)
2210 ast_build_string(&c, &len, "<title>Asterisk™ Manager Test Interface</title>");
2211 vars2msg(&m, params);
2212 if (format == FORMAT_XML) {
2213 ast_build_string(&c, &len, "<ajax-response>\n");
2214 } else if (format == FORMAT_HTML) {
2215 ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2216 ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1> Manager Tester</h1></td></tr>\r\n");
2218 if (process_message(s, &m)) {
2219 if (s->authenticated) {
2220 if (option_verbose > 1) {
2221 if (displayconnects)
2222 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2224 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2226 if (option_verbose > 1) {
2227 if (displayconnects)
2228 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2230 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2236 if (format == FORMAT_XML)
2237 tmp = xml_translate(s->outputstr, params);
2238 else if (format == FORMAT_HTML)
2239 tmp = html_translate(s->outputstr);
2243 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2245 strcpy(retval, workspace);
2246 strcpy(retval + strlen(retval), tmp);
2247 c = retval + strlen(retval);
2252 if (tmp != s->outputstr)
2254 s->outputstr = NULL;
2256 /* Still okay because c would safely be pointing to workspace even
2257 if retval failed to allocate above */
2258 if (format == FORMAT_XML) {
2259 ast_build_string(&c, &len, "</ajax-response>\n");
2260 } else if (format == FORMAT_HTML)
2261 ast_build_string(&c, &len, "</table></body>\r\n");
2264 *title = strdup("Server Error");
2266 ast_mutex_lock(&s->__lock);
2267 if (s->needdestroy) {
2268 if (s->inuse == 1) {
2269 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2272 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2273 if (s->waiting_thread != AST_PTHREADT_NULL)
2274 pthread_kill(s->waiting_thread, SIGURG);
2279 ast_mutex_unlock(&s->__lock);
2284 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2288 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2290 return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2293 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2295 return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2298 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2300 return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2303 struct ast_http_uri rawmanuri = {
2304 .description = "Raw HTTP Manager Event Interface",
2307 .callback = rawman_http_callback,
2310 struct ast_http_uri manageruri = {
2311 .description = "HTML Manager Event Interface",
2314 .callback = manager_http_callback,
2317 struct ast_http_uri managerxmluri = {
2318 .description = "XML Manager Event Interface",
2321 .callback = mxml_http_callback,
2324 static int registered = 0;
2325 static int webregged = 0;
2327 int init_manager(void)
2329 struct ast_config *cfg;
2331 int oldportno = portno;
2332 static struct sockaddr_in ba;
2336 int newhttptimeout = 60;
2338 /* Register default actions */
2339 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2340 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2341 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2342 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2343 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2344 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2345 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2346 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2347 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2348 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2349 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2350 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2351 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2352 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2353 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2354 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2356 ast_cli_register(&show_mancmd_cli);
2357 ast_cli_register(&show_mancmds_cli);
2358 ast_cli_register(&show_manconn_cli);
2359 ast_cli_register(&show_maneventq_cli);
2360 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2362 /* Append placeholder event so master_eventq never runs dry */
2363 append_event("Event: Placeholder\r\n\r\n", 0);
2365 portno = DEFAULT_MANAGER_PORT;
2366 displayconnects = 1;
2367 cfg = ast_config_load("manager.conf");
2369 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
2372 val = ast_variable_retrieve(cfg, "general", "enabled");
2374 enabled = ast_true(val);
2376 val = ast_variable_retrieve(cfg, "general", "block-sockets");
2378 block_sockets = ast_true(val);
2380 val = ast_variable_retrieve(cfg, "general", "webenabled");
2382 webenabled = ast_true(val);
2384 if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2385 if (sscanf(val, "%d", &portno) != 1) {
2386 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2387 portno = DEFAULT_MANAGER_PORT;
2391 if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2392 displayconnects = ast_true(val);
2394 if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2395 timestampevents = ast_true(val);
2397 if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2398 newhttptimeout = atoi(val);
2400 memset(&ba, 0, sizeof(ba));
2401 ba.sin_family = AF_INET;
2402 ba.sin_port = htons(portno);
2404 if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2405 if (!inet_aton(val, &ba.sin_addr)) {
2406 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2407 memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2412 if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2414 /* Can't be done yet */
2418 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2421 ast_config_destroy(cfg);
2423 if (webenabled && enabled) {
2425 ast_http_uri_link(&rawmanuri);
2426 ast_http_uri_link(&manageruri);
2427 ast_http_uri_link(&managerxmluri);
2432 ast_http_uri_unlink(&rawmanuri);
2433 ast_http_uri_unlink(&manageruri);
2434 ast_http_uri_unlink(&managerxmluri);
2439 if (newhttptimeout > 0)
2440 httptimeout = newhttptimeout;
2442 /* If not enabled, do nothing */
2448 asock = socket(AF_INET, SOCK_STREAM, 0);
2450 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2453 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2454 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2455 ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2460 if (listen(asock, 2)) {
2461 ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2466 flags = fcntl(asock, F_GETFL);
2467 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2469 ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2470 ast_pthread_create(&t, NULL, accept_thread, NULL);
2475 int reload_manager(void)
2477 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2478 return init_manager();