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
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
44 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <netinet/tcp.h>
49 #include <arpa/inet.h>
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"
69 #include "asterisk/threadstorage.h"
70 #include "asterisk/linkedlists.h"
72 struct fast_originate_helper {
73 char tech[AST_MAX_MANHEADER_LEN];
74 char data[AST_MAX_MANHEADER_LEN];
76 char app[AST_MAX_APP];
77 char appdata[AST_MAX_MANHEADER_LEN];
78 char cid_name[AST_MAX_MANHEADER_LEN];
79 char cid_num[AST_MAX_MANHEADER_LEN];
80 char context[AST_MAX_CONTEXT];
81 char exten[AST_MAX_EXTENSION];
82 char idtext[AST_MAX_MANHEADER_LEN];
83 char account[AST_MAX_ACCOUNT_CODE];
85 struct ast_variable *vars;
91 struct eventqent *next;
92 char eventdata[1]; /* really variable size, allocated by append_event() */
95 static int enabled = 0;
96 static int portno = DEFAULT_MANAGER_PORT;
97 static int asock = -1;
98 static int displayconnects = 1;
99 static int timestampevents = 0;
100 static int httptimeout = 60;
103 static int block_sockets = 0;
104 static int num_sessions = 0;
106 /* Protected by the sessions list lock */
107 struct eventqent *master_eventq = NULL;
109 * XXX for some unclear reasons, we make sure master_eventq always
110 * has one event in it (Placeholder) in init_manager().
113 AST_THREADSTORAGE(manager_event_buf, manager_event_buf_init);
114 #define MANAGER_EVENT_BUF_INITSIZE 256
116 AST_THREADSTORAGE(astman_append_buf, astman_append_buf_init);
117 #define ASTMAN_APPEND_BUF_INITSIZE 256
119 static struct permalias {
123 { EVENT_FLAG_SYSTEM, "system" },
124 { EVENT_FLAG_CALL, "call" },
125 { EVENT_FLAG_LOG, "log" },
126 { EVENT_FLAG_VERBOSE, "verbose" },
127 { EVENT_FLAG_COMMAND, "command" },
128 { EVENT_FLAG_AGENT, "agent" },
129 { EVENT_FLAG_USER, "user" },
130 { EVENT_FLAG_CONFIG, "config" },
136 /*! Execution thread */
138 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
139 /* XXX need to document which fields it is protecting */
141 /*! socket address */
142 struct sockaddr_in sin;
145 /*! Whether or not we're busy doing an action XXX currently useless */
147 /*! Whether or not we're "dead" XXX currently unused */
149 /*! Whether an HTTP manager is in use */
151 /*! Whether an HTTP session should be destroyed */
153 /*! Whether an HTTP session has someone waiting on events */
154 pthread_t waiting_thread;
155 /*! Unique manager identifer */
156 unsigned long managerid;
157 /*! Session timeout if HTTP */
158 time_t sessiontimeout;
159 /*! Output from manager interface */
160 struct ast_dynamic_str *outputstr;
161 /*! Logged in username */
163 /*! Authentication challenge */
165 /*! Authentication status */
167 /*! Authorization for reading */
169 /*! Authorization for writing */
172 char inbuf[AST_MAX_MANHEADER_LEN];
175 int displaysystemname; /*!< Add system name to manager responses and events */
176 /* Queued events that we've not had the ability to send yet */
177 struct eventqent *eventq;
178 /* Timeout for ast_carefulwrite() */
180 AST_LIST_ENTRY(mansession) list;
183 static AST_LIST_HEAD_STATIC(sessions, mansession);
185 struct ast_manager_user {
192 unsigned int displayconnects:1;
194 AST_LIST_ENTRY(ast_manager_user) list;
197 static AST_LIST_HEAD_STATIC(users, ast_manager_user);
199 static struct manager_action *first_action = NULL;
200 AST_MUTEX_DEFINE_STATIC(actionlock);
202 /*! \brief Convert authority code to string with serveral options */
203 static char *authority_to_str(int authority, char *res, int reslen)
205 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);
219 if (ast_strlen_zero(res))
220 ast_copy_string(res, "<none>", reslen);
225 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
227 struct manager_action *cur;
228 int l = strlen(word), which = 0;
231 ast_mutex_lock(&actionlock);
232 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
233 if (!strncasecmp(word, cur->action, l) && ++which > state) {
234 ret = ast_strdup(cur->action);
235 break; /* make sure we exit even if ast_strdup() returns NULL */
238 ast_mutex_unlock(&actionlock);
244 * convert to xml with various conversion:
245 * mode & 1 -> lowercase;
246 * mode & 2 -> replace non-alphanumeric chars with underscore
248 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int mode)
250 for ( ; *src && *maxlen > 6; src++) {
251 if ( (mode & 2) && !isalnum(*src)) {
258 strcpy(*dst, "<");
263 strcpy(*dst, ">");
268 strcpy(*dst, """);
273 strcpy(*dst, "'");
278 strcpy(*dst, "&");
284 *(*dst)++ = mode ? tolower(*src) : *src;
290 static char *xml_translate(char *in, struct ast_variable *vars)
292 struct ast_variable *v;
294 char *out, *tmp, *var, *val;
295 char *objtype = NULL;
304 for (v = vars; v; v = v->next) {
305 if (!dest && !strcasecmp(v->name, "ajaxdest"))
307 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
314 for (x = 0; in[x]; x++) {
317 else if (in[x] == '\n')
319 else if (strchr("&\"<>", in[x]))
322 len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&" */
323 out = ast_malloc(len);
329 while (*in && (*in >= 32))
332 if ((count > 3) && inobj) {
333 ast_build_string(&tmp, &len, " /></response>\n");
337 while (*in && (*in < 32)) {
342 val = strchr(var, ':');
349 ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
352 ast_build_string(&tmp, &len, " ");
353 xml_copy_escape(&tmp, &len, var, 1);
354 ast_build_string(&tmp, &len, "='");
355 xml_copy_escape(&tmp, &len, val, 0);
356 ast_build_string(&tmp, &len, "'");
361 ast_build_string(&tmp, &len, " /></response>\n");
365 static char *html_translate(char *in)
372 char *tmp, *var, *val, *out;
374 for (x=0; in[x]; x++) {
380 len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
381 out = ast_malloc(len);
387 while (*in && (*in >= 32))
390 if ((count % 4) == 0){
391 ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
394 while (*in && (*in < 32)) {
399 val = strchr(var, ':');
405 ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
414 static struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
416 struct ast_manager_user *user = NULL;
418 AST_LIST_TRAVERSE(&users, user, list)
419 if (!strcasecmp(user->username, name))
424 void astman_append(struct mansession *s, const char *fmt, ...)
427 struct ast_dynamic_str *buf;
429 if (!(buf = ast_dynamic_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
433 ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
437 ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->writetimeout);
439 if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr))))
442 ast_dynamic_str_append(&s->outputstr, 0, "%s", buf->str);
446 static int handle_showmancmd(int fd, int argc, char *argv[])
448 struct manager_action *cur;
453 return RESULT_SHOWUSAGE;
455 ast_mutex_lock(&actionlock);
456 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
457 for (num = 3; num < argc; num++) {
458 if (!strcasecmp(cur->action, argv[num])) {
459 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 : "");
463 ast_mutex_unlock(&actionlock);
465 return RESULT_SUCCESS;
468 static int handle_showmanager(int fd, int argc, char *argv[])
470 struct ast_manager_user *user = NULL;
473 return RESULT_SHOWUSAGE;
475 AST_LIST_LOCK(&users);
477 if (!(user = ast_get_manager_by_name_locked(argv[3]))) {
478 ast_cli(fd, "There is no manager called %s\n", argv[3]);
479 AST_LIST_UNLOCK(&users);
491 "displayconnects: %s\n",
492 (user->username ? user->username : "(N/A)"),
493 (user->secret ? user->secret : "(N/A)"),
494 (user->deny ? user->deny : "(N/A)"),
495 (user->permit ? user->permit : "(N/A)"),
496 (user->read ? user->read : "(N/A)"),
497 (user->write ? user->write : "(N/A)"),
498 (user->displayconnects ? "yes" : "no"));
500 AST_LIST_UNLOCK(&users);
502 return RESULT_SUCCESS;
506 static int handle_showmanagers(int fd, int argc, char *argv[])
508 struct ast_manager_user *user = NULL;
512 return RESULT_SHOWUSAGE;
514 AST_LIST_LOCK(&users);
516 /* If there are no users, print out something along those lines */
517 if (AST_LIST_EMPTY(&users)) {
518 ast_cli(fd, "There are no manager users.\n");
519 AST_LIST_UNLOCK(&users);
520 return RESULT_SUCCESS;
523 ast_cli(fd, "\nusername\n--------\n");
525 AST_LIST_TRAVERSE(&users, user, list) {
526 ast_cli(fd, "%s\n", user->username);
530 AST_LIST_UNLOCK(&users);
532 ast_cli(fd,"-------------------\n");
533 ast_cli(fd,"%d manager users configured.\n", count_amu);
535 return RESULT_SUCCESS;
539 /*! \brief CLI command
540 Should change to "manager show commands" */
541 static int handle_showmancmds(int fd, int argc, char *argv[])
543 struct manager_action *cur;
545 char *format = " %-15.15s %-15.15s %-55.55s\n";
547 ast_cli(fd, format, "Action", "Privilege", "Synopsis");
548 ast_cli(fd, format, "------", "---------", "--------");
550 ast_mutex_lock(&actionlock);
551 for (cur = first_action; cur; cur = cur->next) /* Walk the list of actions */
552 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
553 ast_mutex_unlock(&actionlock);
555 return RESULT_SUCCESS;
558 /*! \brief CLI command show manager connected */
559 /* Should change to "manager show connected" */
560 static int handle_showmanconn(int fd, int argc, char *argv[])
562 struct mansession *s;
563 char *format = " %-15.15s %-15.15s\n";
565 ast_cli(fd, format, "Username", "IP Address");
567 AST_LIST_LOCK(&sessions);
568 AST_LIST_TRAVERSE(&sessions, s, list)
569 ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
570 AST_LIST_UNLOCK(&sessions);
572 return RESULT_SUCCESS;
575 /*! \brief CLI command manager list eventq */
576 /* Should change to "manager show connected" */
577 static int handle_showmaneventq(int fd, int argc, char *argv[])
581 AST_LIST_LOCK(&sessions);
582 for (s = master_eventq; s; s = s->next) {
583 ast_cli(fd, "Usecount: %d\n",s->usecount);
584 ast_cli(fd, "Category: %d\n", s->category);
585 ast_cli(fd, "Event:\n%s", s->eventdata);
587 AST_LIST_UNLOCK(&sessions);
589 return RESULT_SUCCESS;
592 static char showmancmd_help[] =
593 "Usage: manager show command <actionname>\n"
594 " Shows the detailed description for a specific Asterisk manager interface command.\n";
596 static char showmancmds_help[] =
597 "Usage: manager list commands\n"
598 " Prints a listing of all the available Asterisk manager interface commands.\n";
600 static char showmanconn_help[] =
601 "Usage: manager list connected\n"
602 " Prints a listing of the users that are currently connected to the\n"
603 "Asterisk manager interface.\n";
605 static char showmaneventq_help[] =
606 "Usage: manager list eventq\n"
607 " Prints a listing of all events pending in the Asterisk manger\n"
610 static char showmanagers_help[] =
611 "Usage: manager list users\n"
612 " Prints a listing of all managers that are currently configured on that\n"
615 static char showmanager_help[] =
616 " Usage: manager show user <user>\n"
617 " Display all information related to the manager user specified.\n";
619 static struct ast_cli_entry cli_manager[] = {
620 { { "manager", "show", "command", NULL },
621 handle_showmancmd, "Show a manager interface command",
622 showmancmd_help, complete_show_mancmd },
624 { { "manager", "list", "commands", NULL },
625 handle_showmancmds, "List manager interface commands",
628 { { "manager", "list", "connected", NULL },
629 handle_showmanconn, "List connected manager interface users",
632 { { "manager", "list", "eventq", NULL },
633 handle_showmaneventq, "List manager interface queued events",
634 showmaneventq_help },
636 { { "manager", "list", "users", NULL },
637 handle_showmanagers, "List configured manager users",
638 showmanagers_help, NULL, NULL },
640 { { "manager", "show", "user", NULL },
641 handle_showmanager, "Display information on a specific manager user",
642 showmanager_help, NULL, NULL },
645 static void unuse_eventqent(struct eventqent *e)
647 if (ast_atomic_dec_and_test(&e->usecount) && e->next)
648 pthread_kill(t, SIGURG);
651 static void free_session(struct mansession *s)
653 struct eventqent *eqe;
658 ast_mutex_destroy(&s->__lock);
659 while ( (eqe = s->eventq) ) {
660 s->eventq = eqe->next;
661 unuse_eventqent(eqe);
666 static void destroy_session(struct mansession *s)
668 AST_LIST_LOCK(&sessions);
669 AST_LIST_REMOVE(&sessions, s, list);
670 AST_LIST_UNLOCK(&sessions);
672 ast_atomic_fetchadd_int(&num_sessions, -1);
676 char *astman_get_header(struct message *m, char *var)
678 int x, l = strlen(var);
680 for (x = 0; x < m->hdrcount; x++) {
681 char *h = m->headers[x];
682 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
689 struct ast_variable *astman_get_variables(struct message *m)
692 struct ast_variable *head = NULL, *cur;
696 AST_DECLARE_APP_ARGS(args,
697 AST_APP_ARG(vars)[32];
700 varlen = strlen("Variable: ");
702 for (x = 0; x < m->hdrcount; x++) {
703 if (strncasecmp("Variable: ", m->headers[x], varlen))
706 parse = ast_strdupa(m->headers[x] + varlen);
708 AST_STANDARD_APP_ARGS(args, parse);
710 for (y = 0; y < args.argc; y++) {
713 var = val = ast_strdupa(args.vars[y]);
715 if (!val || ast_strlen_zero(var))
717 cur = ast_variable_new(var, val);
731 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
732 hold the session lock _or_ be running in an action callback (in which case s->busy will
733 be non-zero). In either of these cases, there is no need to lock-protect the session's
734 fd, since no other output will be sent (events will be queued), and no input will
735 be read until either the current action finishes or get_input() obtains the session
738 void astman_send_error(struct mansession *s, struct message *m, char *error)
740 char *id = astman_get_header(m,"ActionID");
742 astman_append(s, "Response: Error\r\n");
743 if (!ast_strlen_zero(id))
744 astman_append(s, "ActionID: %s\r\n", id);
745 astman_append(s, "Message: %s\r\n\r\n", error);
748 void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
750 char *id = astman_get_header(m,"ActionID");
752 astman_append(s, "Response: %s\r\n", resp);
753 if (!ast_strlen_zero(id))
754 astman_append(s, "ActionID: %s\r\n", id);
756 astman_append(s, "Message: %s\r\n\r\n", msg);
758 astman_append(s, "\r\n");
761 void astman_send_ack(struct mansession *s, struct message *m, char *msg)
763 astman_send_response(s, m, "Success", msg);
766 /*! Tells you if smallstr exists inside bigstr
767 which is delim by delim and uses no buf or stringsep
768 ast_instring("this|that|more","this",',') == 1;
770 feel free to move this to app.c -anthm */
771 static int ast_instring(const char *bigstr, const char *smallstr, char delim)
773 const char *val = bigstr, *next;
776 if ((next = strchr(val, delim))) {
777 if (!strncmp(val, smallstr, (next - val)))
782 return !strcmp(smallstr, val);
784 } while (*(val = (next + 1)));
789 static int get_perm(const char *instr)
796 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
797 if (ast_instring(instr, perms[x].label, ','))
804 static int ast_is_number(char *string)
811 for (x = 0; x < strlen(string); x++) {
812 if (!(string[x] >= 48 && string[x] <= 57)) {
818 return ret ? atoi(string) : 0;
821 static int ast_strings_to_mask(char *string)
825 x = ast_is_number(string);
829 else if (ast_strlen_zero(string))
831 else if (ast_false(string))
833 else if (ast_true(string)) {
835 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
839 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
840 if (ast_instring(string, perms[x].label, ','))
849 Rather than braindead on,off this now can also accept a specific int mask value
850 or a ',' delim list of mask strings (the same as manager.conf) -anthm
852 static int set_eventmask(struct mansession *s, char *eventmask)
854 int maskint = ast_strings_to_mask(eventmask);
856 ast_mutex_lock(&s->__lock);
858 s->send_events = maskint;
859 ast_mutex_unlock(&s->__lock);
864 static int authenticate(struct mansession *s, struct message *m)
866 char *user = astman_get_header(m, "Username");
867 char *pass = astman_get_header(m, "Secret");
868 char *authtype = astman_get_header(m, "AuthType");
869 char *key = astman_get_header(m, "Key");
870 char *events = astman_get_header(m, "Events");
872 struct ast_config *cfg = ast_config_load("manager.conf");
873 int ret = -1; /* default: error return */
877 while ( (cat = ast_category_browse(cfg, cat)) ) {
878 struct ast_variable *v;
879 struct ast_ha *ha = NULL;
880 char *password = NULL;
882 if (!strcasecmp(cat, "general") || strcasecmp(cat, user))
883 continue; /* skip 'general' and non-matching sections */
885 /* collect parameters for the user's entry */
886 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
887 if (!strcasecmp(v->name, "secret")) {
889 } else if (!strcasecmp(v->name, "displaysystemname")) {
890 if (ast_true(v->value)) {
891 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
892 s->displaysystemname = 1;
894 ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
897 } else if (!strcasecmp(v->name, "permit") ||
898 !strcasecmp(v->name, "deny")) {
899 ha = ast_append_ha(v->name, v->value, ha);
900 } else if (!strcasecmp(v->name, "writetimeout")) {
901 int val = atoi(v->value);
904 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
906 s->writetimeout = val;
911 if (!ast_apply_ha(ha, &(s->sin))) {
912 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
918 if (!strcasecmp(authtype, "MD5")) {
919 if (!ast_strlen_zero(key) && s->challenge) {
922 char md5key[256] = "";
923 struct MD5Context md5;
924 unsigned char digest[16];
926 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
927 MD5Update(&md5, (unsigned char *) password, strlen(password));
928 MD5Final(digest, &md5);
930 len += sprintf(md5key + len, "%2.2x", digest[x]);
931 if (!strcmp(md5key, key))
934 } else if (password) {
935 if (!strcmp(password, pass))
938 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
941 /* we get here with user not found (cat = NULL) or successful authentication */
943 ast_copy_string(s->username, cat, sizeof(s->username));
944 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
945 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
947 set_eventmask(s, events);
950 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
953 ast_config_destroy(cfg);
957 /*! \brief Manager PING */
958 static char mandescr_ping[] =
959 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
960 " manager connection open.\n"
963 static int action_ping(struct mansession *s, struct message *m)
965 astman_send_response(s, m, "Pong", NULL);
969 static char mandescr_getconfig[] =
970 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
971 "file by category and contents.\n"
973 " Filename: Configuration filename (e.g. foo.conf)\n";
975 static int action_getconfig(struct mansession *s, struct message *m)
977 struct ast_config *cfg;
978 char *fn = astman_get_header(m, "Filename");
982 struct ast_variable *v;
983 char idText[256] = "";
984 char *id = astman_get_header(m, "ActionID");
986 if (!ast_strlen_zero(id))
987 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
989 if (ast_strlen_zero(fn)) {
990 astman_send_error(s, m, "Filename not specified");
993 if (!(cfg = ast_config_load(fn))) {
994 astman_send_error(s, m, "Config file not found");
997 astman_append(s, "Response: Success\r\n%s", idText);
998 while ((category = ast_category_browse(cfg, category))) {
1000 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1001 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1002 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1005 ast_config_destroy(cfg);
1006 astman_append(s, "\r\n");
1012 static void handle_updates(struct mansession *s, struct message *m, struct ast_config *cfg)
1016 char *action, *cat, *var, *value, *match;
1017 struct ast_category *category;
1018 struct ast_variable *v;
1020 for (x=0;x<100000;x++) {
1021 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1022 action = astman_get_header(m, hdr);
1023 if (ast_strlen_zero(action))
1025 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1026 cat = astman_get_header(m, hdr);
1027 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1028 var = astman_get_header(m, hdr);
1029 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1030 value = astman_get_header(m, hdr);
1031 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1032 match = astman_get_header(m, hdr);
1033 if (!strcasecmp(action, "newcat")) {
1034 if (!ast_strlen_zero(cat)) {
1035 category = ast_category_new(cat);
1037 ast_category_append(cfg, category);
1040 } else if (!strcasecmp(action, "renamecat")) {
1041 if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1042 category = ast_category_get(cfg, cat);
1044 ast_category_rename(category, value);
1046 } else if (!strcasecmp(action, "delcat")) {
1047 if (!ast_strlen_zero(cat))
1048 ast_category_delete(cfg, cat);
1049 } else if (!strcasecmp(action, "update")) {
1050 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1051 ast_variable_update(category, var, value, match);
1052 } else if (!strcasecmp(action, "delete")) {
1053 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1054 ast_variable_delete(category, var, match);
1055 } else if (!strcasecmp(action, "append")) {
1056 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1057 (category = ast_category_get(cfg, cat)) &&
1058 (v = ast_variable_new(var, value))){
1059 if (match && !strcasecmp(match, "object"))
1061 ast_variable_append(category, v);
1067 static char mandescr_updateconfig[] =
1068 "Description: A 'UpdateConfig' action will dump the contents of a configuration\n"
1069 "file by category and contents.\n"
1070 "Variables (X's represent 6 digit number beginning with 000000):\n"
1071 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1072 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1073 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1074 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1075 " Cat-XXXXXX: Category to operate on\n"
1076 " Var-XXXXXX: Variable to work on\n"
1077 " Value-XXXXXX: Value to work on\n"
1078 " Match-XXXXXX: Extra match required to match line\n";
1080 static int action_updateconfig(struct mansession *s, struct message *m)
1082 struct ast_config *cfg;
1083 char *sfn = astman_get_header(m, "SrcFilename");
1084 char *dfn = astman_get_header(m, "DstFilename");
1086 char idText[256] = "";
1087 char *id = astman_get_header(m, "ActionID");
1088 char *rld = astman_get_header(m, "Reload");
1090 if (!ast_strlen_zero(id))
1091 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1093 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1094 astman_send_error(s, m, "Filename not specified");
1097 if (!(cfg = ast_config_load(sfn))) {
1098 astman_send_error(s, m, "Config file not found");
1101 handle_updates(s, m, cfg);
1102 res = config_text_file_save(dfn, cfg, "Manager");
1103 ast_config_destroy(cfg);
1105 astman_send_error(s, m, "Save of config failed");
1108 astman_append(s, "Response: Success\r\n%s\r\n", idText);
1109 if (!ast_strlen_zero(rld)) {
1112 ast_module_reload(rld);
1117 /*! \brief Manager WAITEVENT */
1118 static char mandescr_waitevent[] =
1119 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1120 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1121 "session, events will be generated and queued.\n"
1123 " Timeout: Maximum time to wait for events\n";
1125 static int action_waitevent(struct mansession *s, struct message *m)
1127 char *timeouts = astman_get_header(m, "Timeout");
1128 int timeout = -1, max;
1132 struct eventqent *eqe;
1133 char *id = astman_get_header(m,"ActionID");
1134 char idText[256] = "";
1136 if (!ast_strlen_zero(id))
1137 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1139 if (!ast_strlen_zero(timeouts)) {
1140 sscanf(timeouts, "%i", &timeout);
1143 ast_mutex_lock(&s->__lock);
1144 if (s->waiting_thread != AST_PTHREADT_NULL) {
1145 pthread_kill(s->waiting_thread, SIGURG);
1147 if (s->sessiontimeout) {
1149 max = s->sessiontimeout - now - 10;
1152 if ((timeout < 0) || (timeout > max))
1154 if (!s->send_events)
1155 s->send_events = -1;
1156 /* Once waitevent is called, always queue events from now on */
1160 ast_mutex_unlock(&s->__lock);
1161 s->waiting_thread = pthread_self();
1163 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
1164 for (x=0; ((x < timeout) || (timeout < 0)); x++) {
1165 ast_mutex_lock(&s->__lock);
1166 if (s->eventq && s->eventq->next)
1168 if (s->waiting_thread != pthread_self())
1172 ast_mutex_unlock(&s->__lock);
1175 if (!s->inuse && s->fd > 0) {
1176 if (ast_wait_for_input(s->fd, 1000))
1183 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
1184 ast_mutex_lock(&s->__lock);
1185 if (s->waiting_thread == pthread_self()) {
1186 astman_send_response(s, m, "Success", "Waiting for Event...");
1187 /* Only show events if we're the most recent waiter */
1188 while(s->eventq->next) {
1189 eqe = s->eventq->next;
1190 if (((s->readperm & eqe->category) == eqe->category) &&
1191 ((s->send_events & eqe->category) == eqe->category)) {
1192 astman_append(s, "%s", eqe->eventdata);
1194 unuse_eventqent(s->eventq);
1198 "Event: WaitEventComplete\r\n"
1201 s->waiting_thread = AST_PTHREADT_NULL;
1204 ast_log(LOG_DEBUG, "Abandoning event request!\n");
1206 ast_mutex_unlock(&s->__lock);
1210 static char mandescr_listcommands[] =
1211 "Description: Returns the action name and synopsis for every\n"
1212 " action that is available to the user\n"
1213 "Variables: NONE\n";
1215 static int action_listcommands(struct mansession *s, struct message *m)
1217 struct manager_action *cur;
1218 char idText[256] = "";
1220 char *id = astman_get_header(m,"ActionID");
1222 if (!ast_strlen_zero(id))
1223 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1224 astman_append(s, "Response: Success\r\n%s", idText);
1225 ast_mutex_lock(&actionlock);
1226 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
1227 if ((s->writeperm & cur->authority) == cur->authority)
1228 astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
1230 ast_mutex_unlock(&actionlock);
1231 astman_append(s, "\r\n");
1236 static char mandescr_events[] =
1237 "Description: Enable/Disable sending of events to this manager\n"
1240 " EventMask: 'on' if all events should be sent,\n"
1241 " 'off' if no events should be sent,\n"
1242 " 'system,call,log' to select which flags events should have to be sent.\n";
1244 static int action_events(struct mansession *s, struct message *m)
1246 char *mask = astman_get_header(m, "EventMask");
1249 res = set_eventmask(s, mask);
1251 astman_send_response(s, m, "Events On", NULL);
1253 astman_send_response(s, m, "Events Off", NULL);
1258 static char mandescr_logoff[] =
1259 "Description: Logoff this manager session\n"
1260 "Variables: NONE\n";
1262 static int action_logoff(struct mansession *s, struct message *m)
1264 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1268 static char mandescr_hangup[] =
1269 "Description: Hangup a channel\n"
1271 " Channel: The channel name to be hungup\n";
1273 static int action_hangup(struct mansession *s, struct message *m)
1275 struct ast_channel *c = NULL;
1276 char *name = astman_get_header(m, "Channel");
1277 if (ast_strlen_zero(name)) {
1278 astman_send_error(s, m, "No channel specified");
1281 c = ast_get_channel_by_name_locked(name);
1283 astman_send_error(s, m, "No such channel");
1286 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1287 ast_channel_unlock(c);
1288 astman_send_ack(s, m, "Channel Hungup");
1292 static char mandescr_setvar[] =
1293 "Description: Set a global or local channel variable.\n"
1294 "Variables: (Names marked with * are required)\n"
1295 " Channel: Channel to set variable for\n"
1296 " *Variable: Variable name\n"
1299 static int action_setvar(struct mansession *s, struct message *m)
1301 struct ast_channel *c = NULL;
1302 char *name = astman_get_header(m, "Channel");
1303 char *varname = astman_get_header(m, "Variable");
1304 char *varval = astman_get_header(m, "Value");
1306 if (ast_strlen_zero(varname)) {
1307 astman_send_error(s, m, "No variable specified");
1311 if (ast_strlen_zero(varval)) {
1312 astman_send_error(s, m, "No value specified");
1316 if (!ast_strlen_zero(name)) {
1317 c = ast_get_channel_by_name_locked(name);
1319 astman_send_error(s, m, "No such channel");
1324 pbx_builtin_setvar_helper(c, varname, varval);
1327 ast_channel_unlock(c);
1329 astman_send_ack(s, m, "Variable Set");
1334 static char mandescr_getvar[] =
1335 "Description: Get the value of a global or local channel variable.\n"
1336 "Variables: (Names marked with * are required)\n"
1337 " Channel: Channel to read variable from\n"
1338 " *Variable: Variable name\n"
1339 " ActionID: Optional Action id for message matching.\n";
1341 static int action_getvar(struct mansession *s, struct message *m)
1343 struct ast_channel *c = NULL;
1344 char *name = astman_get_header(m, "Channel");
1345 char *varname = astman_get_header(m, "Variable");
1346 char *id = astman_get_header(m,"ActionID");
1348 char workspace[1024];
1350 if (ast_strlen_zero(varname)) {
1351 astman_send_error(s, m, "No variable specified");
1355 if (!ast_strlen_zero(name)) {
1356 c = ast_get_channel_by_name_locked(name);
1358 astman_send_error(s, m, "No such channel");
1363 if (varname[strlen(varname) - 1] == ')') {
1364 ast_func_read(c, varname, workspace, sizeof(workspace));
1366 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1370 ast_channel_unlock(c);
1371 astman_append(s, "Response: Success\r\n"
1372 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1373 if (!ast_strlen_zero(id))
1374 astman_append(s, "ActionID: %s\r\n",id);
1375 astman_append(s, "\r\n");
1381 /*! \brief Manager "status" command to show channels */
1382 /* Needs documentation... */
1383 static int action_status(struct mansession *s, struct message *m)
1385 char *id = astman_get_header(m,"ActionID");
1386 char *name = astman_get_header(m,"Channel");
1387 char idText[256] = "";
1388 struct ast_channel *c;
1390 struct timeval now = ast_tvnow();
1391 long elapsed_seconds = 0;
1392 int all = ast_strlen_zero(name); /* set if we want all channels */
1394 astman_send_ack(s, m, "Channel status will follow");
1395 if (!ast_strlen_zero(id))
1396 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1398 c = ast_channel_walk_locked(NULL);
1400 c = ast_get_channel_by_name_locked(name);
1402 astman_send_error(s, m, "No such channel");
1406 /* if we look by name, we break after the first iteration */
1409 snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1414 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1418 "Privilege: Call\r\n"
1420 "CallerIDNum: %s\r\n"
1421 "CallerIDName: %s\r\n"
1433 S_OR(c->cid.cid_num, "<unknown>"),
1434 S_OR(c->cid.cid_name, "<unknown>"),
1436 ast_state2str(c->_state), c->context,
1437 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1441 "Privilege: Call\r\n"
1443 "CallerIDNum: %s\r\n"
1444 "CallerIDName: %s\r\n"
1452 S_OR(c->cid.cid_num, "<unknown>"),
1453 S_OR(c->cid.cid_name, "<unknown>"),
1455 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1457 ast_channel_unlock(c);
1460 c = ast_channel_walk_locked(c);
1463 "Event: StatusComplete\r\n"
1469 static char mandescr_redirect[] =
1470 "Description: Redirect (transfer) a call.\n"
1471 "Variables: (Names marked with * are required)\n"
1472 " *Channel: Channel to redirect\n"
1473 " ExtraChannel: Second call leg to transfer (optional)\n"
1474 " *Exten: Extension to transfer to\n"
1475 " *Context: Context to transfer to\n"
1476 " *Priority: Priority to transfer to\n"
1477 " ActionID: Optional Action id for message matching.\n";
1479 /*! \brief action_redirect: The redirect manager command */
1480 static int action_redirect(struct mansession *s, struct message *m)
1482 char *name = astman_get_header(m, "Channel");
1483 char *name2 = astman_get_header(m, "ExtraChannel");
1484 char *exten = astman_get_header(m, "Exten");
1485 char *context = astman_get_header(m, "Context");
1486 char *priority = astman_get_header(m, "Priority");
1487 struct ast_channel *chan, *chan2 = NULL;
1491 if (ast_strlen_zero(name)) {
1492 astman_send_error(s, m, "Channel not specified");
1495 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1496 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1497 astman_send_error(s, m, "Invalid priority\n");
1501 /* XXX watch out, possible deadlock!!! */
1502 chan = ast_get_channel_by_name_locked(name);
1505 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1506 astman_send_error(s, m, buf);
1509 if (!ast_strlen_zero(name2))
1510 chan2 = ast_get_channel_by_name_locked(name2);
1511 res = ast_async_goto(chan, context, exten, pi);
1513 if (!ast_strlen_zero(name2)) {
1515 res = ast_async_goto(chan2, context, exten, pi);
1519 astman_send_ack(s, m, "Dual Redirect successful");
1521 astman_send_error(s, m, "Secondary redirect failed");
1523 astman_send_ack(s, m, "Redirect successful");
1525 astman_send_error(s, m, "Redirect failed");
1527 ast_channel_unlock(chan);
1529 ast_channel_unlock(chan2);
1533 static char mandescr_command[] =
1534 "Description: Run a CLI command.\n"
1535 "Variables: (Names marked with * are required)\n"
1536 " *Command: Asterisk CLI command to run\n"
1537 " ActionID: Optional Action id for message matching.\n";
1539 /*! \brief action_command: Manager command "command" - execute CLI command */
1540 static int action_command(struct mansession *s, struct message *m)
1542 char *cmd = astman_get_header(m, "Command");
1543 char *id = astman_get_header(m, "ActionID");
1544 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1545 if (!ast_strlen_zero(id))
1546 astman_append(s, "ActionID: %s\r\n", id);
1547 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1548 ast_cli_command(s->fd, cmd);
1549 astman_append(s, "--END COMMAND--\r\n\r\n");
1553 static void *fast_originate(void *data)
1555 struct fast_originate_helper *in = data;
1558 struct ast_channel *chan = NULL;
1560 if (!ast_strlen_zero(in->app)) {
1561 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1562 S_OR(in->cid_num, NULL),
1563 S_OR(in->cid_name, NULL),
1564 in->vars, in->account, &chan);
1566 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1567 S_OR(in->cid_num, NULL),
1568 S_OR(in->cid_name, NULL),
1569 in->vars, in->account, &chan);
1572 /* Tell the manager what happened with the channel */
1573 manager_event(EVENT_FLAG_CALL,
1574 res ? "OriginateFailure" : "OriginateSuccess",
1576 "Channel: %s/%s\r\n"
1581 "CallerIDNum: %s\r\n"
1582 "CallerIDName: %s\r\n",
1583 in->idtext, in->tech, in->data, in->context, in->exten, reason,
1584 chan ? chan->uniqueid : "<null>",
1585 S_OR(in->cid_num, "<unknown>"),
1586 S_OR(in->cid_name, "<unknown>")
1589 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1591 ast_channel_unlock(chan);
1596 static char mandescr_originate[] =
1597 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1598 " Application/Data\n"
1599 "Variables: (Names marked with * are required)\n"
1600 " *Channel: Channel name to call\n"
1601 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1602 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1603 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1604 " Application: Application to use\n"
1605 " Data: Data to use (requires 'Application')\n"
1606 " Timeout: How long to wait for call to be answered (in ms)\n"
1607 " CallerID: Caller ID to be set on the outgoing channel\n"
1608 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1609 " Account: Account code\n"
1610 " Async: Set to 'true' for fast origination\n";
1612 static int action_originate(struct mansession *s, struct message *m)
1614 char *name = astman_get_header(m, "Channel");
1615 char *exten = astman_get_header(m, "Exten");
1616 char *context = astman_get_header(m, "Context");
1617 char *priority = astman_get_header(m, "Priority");
1618 char *timeout = astman_get_header(m, "Timeout");
1619 char *callerid = astman_get_header(m, "CallerID");
1620 char *account = astman_get_header(m, "Account");
1621 char *app = astman_get_header(m, "Application");
1622 char *appdata = astman_get_header(m, "Data");
1623 char *async = astman_get_header(m, "Async");
1624 char *id = astman_get_header(m, "ActionID");
1625 struct ast_variable *vars = astman_get_variables(m);
1627 char *l = NULL, *n = NULL;
1636 pthread_attr_t attr;
1638 astman_send_error(s, m, "Channel not specified");
1641 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1642 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1643 astman_send_error(s, m, "Invalid priority\n");
1647 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1648 astman_send_error(s, m, "Invalid timeout\n");
1651 ast_copy_string(tmp, name, sizeof(tmp));
1653 data = strchr(tmp, '/');
1655 astman_send_error(s, m, "Invalid channel\n");
1659 ast_copy_string(tmp2, callerid, sizeof(tmp2));
1660 ast_callerid_parse(tmp2, &n, &l);
1662 if (ast_strlen_zero(n))
1666 ast_shrink_phone_number(l);
1667 if (ast_strlen_zero(l))
1670 if (ast_true(async)) {
1671 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1675 if (!ast_strlen_zero(id))
1676 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1677 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1678 ast_copy_string(fast->data, data, sizeof(fast->data));
1679 ast_copy_string(fast->app, app, sizeof(fast->app));
1680 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1682 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1684 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1686 ast_copy_string(fast->context, context, sizeof(fast->context));
1687 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1688 ast_copy_string(fast->account, account, sizeof(fast->account));
1690 fast->priority = pi;
1691 pthread_attr_init(&attr);
1692 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1693 if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1699 } else if (!ast_strlen_zero(app)) {
1700 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1702 if (exten && context && pi)
1703 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1705 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1710 astman_send_ack(s, m, "Originate successfully queued");
1712 astman_send_error(s, m, "Originate failed");
1716 /*! \brief Help text for manager command mailboxstatus
1718 static char mandescr_mailboxstatus[] =
1719 "Description: Checks a voicemail account for status.\n"
1720 "Variables: (Names marked with * are required)\n"
1721 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1722 " ActionID: Optional ActionID for message matching.\n"
1723 "Returns number of messages.\n"
1724 " Message: Mailbox Status\n"
1725 " Mailbox: <mailboxid>\n"
1726 " Waiting: <count>\n"
1729 static int action_mailboxstatus(struct mansession *s, struct message *m)
1731 char *mailbox = astman_get_header(m, "Mailbox");
1732 char *id = astman_get_header(m,"ActionID");
1733 char idText[256] = "";
1735 if (ast_strlen_zero(mailbox)) {
1736 astman_send_error(s, m, "Mailbox not specified");
1739 if (!ast_strlen_zero(id))
1740 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1741 ret = ast_app_has_voicemail(mailbox, NULL);
1742 astman_append(s, "Response: Success\r\n"
1744 "Message: Mailbox Status\r\n"
1746 "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1750 static char mandescr_mailboxcount[] =
1751 "Description: Checks a voicemail account for new messages.\n"
1752 "Variables: (Names marked with * are required)\n"
1753 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1754 " ActionID: Optional ActionID for message matching.\n"
1755 "Returns number of new and old messages.\n"
1756 " Message: Mailbox Message Count\n"
1757 " Mailbox: <mailboxid>\n"
1758 " NewMessages: <count>\n"
1759 " OldMessages: <count>\n"
1761 static int action_mailboxcount(struct mansession *s, struct message *m)
1763 char *mailbox = astman_get_header(m, "Mailbox");
1764 char *id = astman_get_header(m,"ActionID");
1765 char idText[256] = "";
1766 int newmsgs = 0, oldmsgs = 0;
1767 if (ast_strlen_zero(mailbox)) {
1768 astman_send_error(s, m, "Mailbox not specified");
1771 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
1772 if (!ast_strlen_zero(id)) {
1773 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1775 astman_append(s, "Response: Success\r\n"
1777 "Message: Mailbox Message Count\r\n"
1779 "NewMessages: %d\r\n"
1780 "OldMessages: %d\r\n"
1782 idText,mailbox, newmsgs, oldmsgs);
1786 static char mandescr_extensionstate[] =
1787 "Description: Report the extension state for given extension.\n"
1788 " If the extension has a hint, will use devicestate to check\n"
1789 " the status of the device connected to the extension.\n"
1790 "Variables: (Names marked with * are required)\n"
1791 " *Exten: Extension to check state on\n"
1792 " *Context: Context for extension\n"
1793 " ActionId: Optional ID for this transaction\n"
1794 "Will return an \"Extension Status\" message.\n"
1795 "The response will include the hint for the extension and the status.\n";
1797 static int action_extensionstate(struct mansession *s, struct message *m)
1799 char *exten = astman_get_header(m, "Exten");
1800 char *context = astman_get_header(m, "Context");
1801 char *id = astman_get_header(m,"ActionID");
1802 char idText[256] = "";
1803 char hint[256] = "";
1805 if (ast_strlen_zero(exten)) {
1806 astman_send_error(s, m, "Extension not specified");
1809 if (ast_strlen_zero(context))
1810 context = "default";
1811 status = ast_extension_state(NULL, context, exten);
1812 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1813 if (!ast_strlen_zero(id)) {
1814 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1816 astman_append(s, "Response: Success\r\n"
1818 "Message: Extension Status\r\n"
1822 "Status: %d\r\n\r\n",
1823 idText,exten, context, hint, status);
1827 static char mandescr_timeout[] =
1828 "Description: Hangup a channel after a certain time.\n"
1829 "Variables: (Names marked with * are required)\n"
1830 " *Channel: Channel name to hangup\n"
1831 " *Timeout: Maximum duration of the call (sec)\n"
1832 "Acknowledges set time with 'Timeout Set' message\n";
1834 static int action_timeout(struct mansession *s, struct message *m)
1836 struct ast_channel *c = NULL;
1837 char *name = astman_get_header(m, "Channel");
1838 int timeout = atoi(astman_get_header(m, "Timeout"));
1839 if (ast_strlen_zero(name)) {
1840 astman_send_error(s, m, "No channel specified");
1844 astman_send_error(s, m, "No timeout specified");
1847 c = ast_get_channel_by_name_locked(name);
1849 astman_send_error(s, m, "No such channel");
1852 ast_channel_setwhentohangup(c, timeout);
1853 ast_channel_unlock(c);
1854 astman_send_ack(s, m, "Timeout Set");
1858 static int process_events(struct mansession *s)
1862 ast_mutex_lock(&s->__lock);
1864 struct eventqent *eqe;
1868 s->eventq = master_eventq;
1869 while( (eqe = s->eventq->next) ) {
1870 if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
1871 ((s->send_events & eqe->category) == eqe->category)) {
1872 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
1875 unuse_eventqent(s->eventq);
1879 ast_mutex_unlock(&s->__lock);
1883 static char mandescr_userevent[] =
1884 "Description: Send an event to manager sessions.\n"
1885 "Variables: (Names marked with * are required)\n"
1886 " *UserEvent: EventStringToSend\n"
1887 " Header1: Content1\n"
1888 " HeaderN: ContentN\n";
1890 static int action_userevent(struct mansession *s, struct message *m)
1892 char *event = astman_get_header(m, "UserEvent");
1893 char body[2048] = "";
1895 for (x = 0; x < m->hdrcount; x++) {
1896 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
1897 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
1898 bodylen += strlen(m->headers[x]);
1899 ast_copy_string(body + bodylen, "\r\n", 3);
1904 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
1909 * Process the message, performing desired action.
1910 * Return 0 on success, -1 on error that require the session to be destroyed.
1912 static int process_message(struct mansession *s, struct message *m)
1914 char action[80] = "";
1915 char *id = astman_get_header(m,"ActionID");
1916 char idText[256] = "";
1919 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1921 ast_log(LOG_DEBUG, "Manager received command '%s'\n", action);
1923 if (ast_strlen_zero(action)) {
1924 astman_send_error(s, m, "Missing action in request");
1927 if (!ast_strlen_zero(id)) {
1928 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1930 if (!strcasecmp(action, "Challenge")) {
1931 char *authtype = astman_get_header(m, "AuthType");
1933 if (!strcasecmp(authtype, "MD5")) {
1934 if (ast_strlen_zero(s->challenge))
1935 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1936 ast_mutex_lock(&s->__lock);
1937 astman_append(s, "Response: Success\r\n"
1939 "Challenge: %s\r\n\r\n",
1940 idText, s->challenge);
1941 ast_mutex_unlock(&s->__lock);
1943 astman_send_error(s, m, "Must specify AuthType");
1946 } else if (!strcasecmp(action, "Login")) {
1947 if (authenticate(s, m)) {
1949 astman_send_error(s, m, "Authentication failed");
1952 s->authenticated = 1;
1953 if (option_verbose > 1) {
1954 if (displayconnects) {
1955 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1958 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1959 astman_send_ack(s, m, "Authentication accepted");
1964 struct manager_action *tmp;
1965 ast_mutex_lock(&s->__lock);
1967 ast_mutex_unlock(&s->__lock);
1968 /* XXX should we protect the list navigation ? */
1969 for (tmp = first_action ; tmp; tmp = tmp->next) {
1970 if (!strcasecmp(action, tmp->action)) {
1971 if ((s->writeperm & tmp->authority) == tmp->authority) {
1972 if (tmp->func(s, m))
1975 astman_send_error(s, m, "Permission denied");
1981 astman_send_error(s, m, "Invalid/unknown command");
1985 return process_events(s);
1988 static int get_input(struct mansession *s, char *output)
1990 /* output must have at least sizeof(s->inbuf) space */
1993 struct pollfd fds[1];
1994 for (x = 1; x < s->inlen; x++) {
1995 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
1996 /* Copy output data up to and including \r\n */
1997 memcpy(output, s->inbuf, x + 1);
1998 /* Add trailing \0 */
2000 /* Move remaining data back to the front */
2001 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
2002 s->inlen -= (x + 1);
2006 if (s->inlen >= sizeof(s->inbuf) - 1) {
2007 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
2011 fds[0].events = POLLIN;
2013 ast_mutex_lock(&s->__lock);
2014 s->waiting_thread = pthread_self();
2015 ast_mutex_unlock(&s->__lock);
2017 res = poll(fds, 1, -1);
2019 ast_mutex_lock(&s->__lock);
2020 s->waiting_thread = AST_PTHREADT_NULL;
2021 ast_mutex_unlock(&s->__lock);
2023 if (errno == EINTR) {
2028 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
2030 } else if (res > 0) {
2031 ast_mutex_lock(&s->__lock);
2032 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
2033 ast_mutex_unlock(&s->__lock);
2040 s->inbuf[s->inlen] = '\0';
2044 static void *session_do(void *data)
2046 struct mansession *s = data;
2050 ast_mutex_lock(&s->__lock);
2051 astman_append(s, "Asterisk Call Manager/1.0\r\n");
2052 ast_mutex_unlock(&s->__lock);
2053 memset(&m, 0, sizeof(m));
2055 res = get_input(s, m.headers[m.hdrcount]);
2057 /* Strip trailing \r\n */
2058 if (strlen(m.headers[m.hdrcount]) < 2)
2060 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
2061 if (ast_strlen_zero(m.headers[m.hdrcount])) {
2062 if (process_message(s, &m))
2064 memset(&m, 0, sizeof(m));
2065 } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
2067 } else if (res < 0) {
2069 } else if (s->eventq->next) {
2070 if (process_events(s))
2074 if (s->authenticated) {
2075 if (option_verbose > 1) {
2076 if (displayconnects)
2077 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2079 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2081 if (option_verbose > 1) {
2082 if (displayconnects)
2083 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2085 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2091 static void *accept_thread(void *ignore)
2094 struct sockaddr_in sin;
2096 struct eventqent *eqe;
2097 struct mansession *s;
2101 pthread_attr_t attr;
2103 struct pollfd pfds[1];
2105 pthread_attr_init(&attr);
2106 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2110 AST_LIST_LOCK(&sessions);
2111 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2112 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2113 AST_LIST_REMOVE_CURRENT(&sessions, list);
2114 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2115 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2116 s->username, ast_inet_ntoa(s->sin.sin_addr));
2122 AST_LIST_TRAVERSE_SAFE_END
2123 /* Purge master event queue of old, unused events, but make sure we
2124 always keep at least one in the queue */
2125 /* XXX why do we need one entry in the queue ? */
2126 while (master_eventq->next && !master_eventq->usecount) {
2127 eqe = master_eventq;
2128 master_eventq = master_eventq->next;
2131 AST_LIST_UNLOCK(&sessions);
2133 ast_atomic_fetchadd_int(&num_sessions, -1);
2135 sinlen = sizeof(sin);
2137 pfds[0].events = POLLIN;
2138 /* Wait for something to happen, but timeout every few seconds so
2139 we can ditch any old manager sessions */
2140 if (poll(pfds, 1, 5000) < 1)
2142 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2144 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2147 p = getprotobyname("tcp");
2149 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2150 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2153 if (!(s = ast_calloc(1, sizeof(*s))))
2156 ast_atomic_fetchadd_int(&num_sessions, 1);
2158 memcpy(&s->sin, &sin, sizeof(sin));
2159 s->writetimeout = 100;
2160 s->waiting_thread = AST_PTHREADT_NULL;
2162 if (!block_sockets) {
2163 /* For safety, make sure socket is non-blocking */
2164 flags = fcntl(as, F_GETFL);
2165 fcntl(as, F_SETFL, flags | O_NONBLOCK);
2167 flags = fcntl(as, F_GETFL);
2168 fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
2170 ast_mutex_init(&s->__lock);
2172 s->send_events = -1;
2173 AST_LIST_LOCK(&sessions);
2174 AST_LIST_INSERT_HEAD(&sessions, s, list);
2175 /* Find the last place in the master event queue and hook ourselves
2177 s->eventq = master_eventq;
2178 while(s->eventq->next)
2179 s->eventq = s->eventq->next;
2180 AST_LIST_UNLOCK(&sessions);
2181 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2182 if (ast_pthread_create_background(&s->t, &attr, session_do, s))
2185 pthread_attr_destroy(&attr);
2189 static int append_event(const char *str, int category)
2191 struct eventqent *tmp, *prev = NULL;
2192 tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2198 tmp->category = category;
2199 strcpy(tmp->eventdata, str);
2201 if (master_eventq) {
2202 prev = master_eventq;
2207 master_eventq = tmp;
2210 tmp->usecount = num_sessions;
2215 /*! \brief manager_event: Send AMI event to client */
2216 int manager_event(int category, const char *event, const char *fmt, ...)
2218 struct mansession *s;
2222 struct ast_dynamic_str *buf;
2224 /* Abort if there aren't any manager sessions */
2228 if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2231 ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
2232 "Event: %s\r\nPrivilege: %s\r\n",
2233 event, authority_to_str(category, auth, sizeof(auth)));
2235 if (timestampevents) {
2237 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2238 "Timestamp: %ld.%06lu\r\n",
2239 now.tv_sec, (unsigned long) now.tv_usec);
2243 ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
2246 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
2248 /* Append event to master list and wake up any sleeping sessions */
2249 AST_LIST_LOCK(&sessions);
2250 append_event(buf->str, category);
2251 AST_LIST_TRAVERSE(&sessions, s, list) {
2252 ast_mutex_lock(&s->__lock);
2253 if (s->waiting_thread != AST_PTHREADT_NULL)
2254 pthread_kill(s->waiting_thread, SIGURG);
2255 ast_mutex_unlock(&s->__lock);
2257 AST_LIST_UNLOCK(&sessions);
2262 int ast_manager_unregister(char *action)
2264 struct manager_action *cur = first_action, *prev = first_action;
2266 ast_mutex_lock(&actionlock);
2267 for (cur = first_action, prev = NULL; cur; prev = cur, cur = cur->next) {
2268 if (!strcasecmp(action, cur->action)) {
2270 prev->next = cur->next;
2272 first_action = cur->next;
2274 if (option_verbose > 1)
2275 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2279 ast_mutex_unlock(&actionlock);
2283 static int manager_state_cb(char *context, char *exten, int state, void *data)
2285 /* Notify managers of change */
2286 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2290 static int ast_manager_register_struct(struct manager_action *act)
2292 struct manager_action *cur, *prev = NULL;
2295 ast_mutex_lock(&actionlock);
2296 for (cur = first_action; cur; prev = cur, cur = cur->next) {
2297 ret = strcasecmp(cur->action, act->action);
2299 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2300 ast_mutex_unlock(&actionlock);
2303 if (ret > 0) /* Insert these alphabetically */
2312 if (option_verbose > 1)
2313 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2314 ast_mutex_unlock(&actionlock);
2318 /*! \brief register a new command with manager, including online help. This is
2319 the preferred way to register a manager command */
2320 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2322 struct manager_action *cur;
2324 cur = ast_malloc(sizeof(*cur));
2328 cur->action = action;
2329 cur->authority = auth;
2331 cur->synopsis = synopsis;
2332 cur->description = description;
2335 ast_manager_register_struct(cur);
2340 END Doxygen group */
2342 static struct mansession *find_session(unsigned long ident)
2344 struct mansession *s;
2346 AST_LIST_LOCK(&sessions);
2347 AST_LIST_TRAVERSE(&sessions, s, list) {
2348 ast_mutex_lock(&s->__lock);
2349 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2353 ast_mutex_unlock(&s->__lock);
2355 AST_LIST_UNLOCK(&sessions);
2361 static void vars2msg(struct message *m, struct ast_variable *vars)
2364 for (x = 0; vars && (x < AST_MAX_MANHEADERS); x++, vars = vars->next) {
2367 m->hdrcount = x + 1;
2368 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2377 static char *contenttype[] = { "plain", "html", "xml" };
2379 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2381 struct mansession *s = NULL;
2382 unsigned long ident = 0;
2383 char workspace[512];
2385 size_t len = sizeof(workspace);
2387 char *c = workspace;
2388 char *retval = NULL;
2390 struct ast_variable *v;
2392 for (v = params; v; v = v->next) {
2393 if (!strcasecmp(v->name, "mansession_id")) {
2394 sscanf(v->value, "%lx", &ident);
2395 ast_verbose("session is <%lx>\n", ident);
2400 if (!(s = find_session(ident))) {
2401 /* Create new session */
2402 if (!(s = ast_calloc(1, sizeof(*s)))) {
2404 goto generic_callback_out;
2406 memcpy(&s->sin, requestor, sizeof(s->sin));
2408 s->waiting_thread = AST_PTHREADT_NULL;
2410 ast_mutex_init(&s->__lock);
2411 ast_mutex_lock(&s->__lock);
2413 s->managerid = rand() | (unsigned long)s;
2414 AST_LIST_LOCK(&sessions);
2415 AST_LIST_INSERT_HEAD(&sessions, s, list);
2416 /* Hook into the last spot in the event queue */
2417 s->eventq = master_eventq;
2418 while (s->eventq->next)
2419 s->eventq = s->eventq->next;
2420 AST_LIST_UNLOCK(&sessions);
2421 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2422 ast_atomic_fetchadd_int(&num_sessions, 1);
2425 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2426 time(&s->sessiontimeout);
2427 if (!s->authenticated && (httptimeout > 5))
2428 s->sessiontimeout += 5;
2430 s->sessiontimeout += httptimeout;
2431 ast_mutex_unlock(&s->__lock);
2433 memset(&m, 0, sizeof(m));
2436 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
2437 sprintf(tmp, "%08lx", s->managerid);
2438 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2439 if (format == FORMAT_HTML)
2440 ast_build_string(&c, &len, "<title>Asterisk™ Manager Test Interface</title>");
2441 vars2msg(&m, params);
2442 if (format == FORMAT_XML) {
2443 ast_build_string(&c, &len, "<ajax-response>\n");
2444 } else if (format == FORMAT_HTML) {
2445 ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2446 ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1> Manager Tester</h1></td></tr>\r\n");
2450 ast_copy_string(template, "/tmp/ast-http-XXXXXX", sizeof(template));
2451 s->fd = mkstemp(template);
2453 if (process_message(s, &m)) {
2454 if (s->authenticated) {
2455 if (option_verbose > 1) {
2456 if (displayconnects)
2457 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2459 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2461 if (option_verbose > 1) {
2462 if (displayconnects)
2463 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2465 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2469 if (s->fd > -1) { /* have temporary output */
2471 off_t len = lseek(s->fd, 0, SEEK_END); /* how many chars available */
2473 if (len > 0 && (buf = ast_calloc(1, len+1))) {
2475 s->outputstr = ast_calloc(1, sizeof(*s->outputstr));
2477 lseek(s->fd, 0, SEEK_SET);
2478 read(s->fd, buf, len);
2479 ast_verbose("--- fd %d has %d bytes ---\n%s\n---\n", s->fd, (int)len, buf);
2480 ast_dynamic_str_append(&s->outputstr, 0, "%s", buf);
2490 if (format == FORMAT_XML)
2491 tmp = xml_translate(s->outputstr->str, params);
2492 else if (format == FORMAT_HTML)
2493 tmp = html_translate(s->outputstr->str);
2495 tmp = s->outputstr->str;
2497 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2499 strcpy(retval, workspace);
2500 strcpy(retval + strlen(retval), tmp);
2501 c = retval + strlen(retval);
2505 if (tmp != s->outputstr->str)
2508 s->outputstr = NULL;
2510 /* Still okay because c would safely be pointing to workspace even
2511 if retval failed to allocate above */
2512 if (format == FORMAT_XML) {
2513 ast_build_string(&c, &len, "</ajax-response>\n");
2514 } else if (format == FORMAT_HTML)
2515 ast_build_string(&c, &len, "</table></body>\r\n");
2517 ast_mutex_lock(&s->__lock);
2518 if (s->needdestroy) {
2519 if (s->inuse == 1) {
2521 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2525 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2526 if (s->waiting_thread != AST_PTHREADT_NULL)
2527 pthread_kill(s->waiting_thread, SIGURG);
2532 ast_mutex_unlock(&s->__lock);
2536 generic_callback_out:
2538 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2542 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2544 return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2547 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2549 return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2552 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2554 return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2557 struct ast_http_uri rawmanuri = {
2558 .description = "Raw HTTP Manager Event Interface",
2561 .callback = rawman_http_callback,
2564 struct ast_http_uri manageruri = {
2565 .description = "HTML Manager Event Interface",
2568 .callback = manager_http_callback,
2571 struct ast_http_uri managerxmluri = {
2572 .description = "XML Manager Event Interface",
2575 .callback = mxml_http_callback,
2578 static int registered = 0;
2579 static int webregged = 0;
2581 int init_manager(void)
2583 struct ast_config *cfg = NULL;
2586 int oldportno = portno;
2587 static struct sockaddr_in ba;
2591 int newhttptimeout = 60;
2592 struct ast_manager_user *user = NULL;
2595 /* Register default actions */
2596 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2597 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2598 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2599 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2600 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2601 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2602 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2603 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2604 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2605 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2606 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2607 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2608 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2609 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2610 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2611 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2612 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2613 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2614 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2616 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2617 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2619 /* Append placeholder event so master_eventq never runs dry */
2620 append_event("Event: Placeholder\r\n\r\n", 0);
2622 portno = DEFAULT_MANAGER_PORT;
2623 displayconnects = 1;
2624 cfg = ast_config_load("manager.conf");
2626 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
2629 val = ast_variable_retrieve(cfg, "general", "enabled");
2631 enabled = ast_true(val);
2633 val = ast_variable_retrieve(cfg, "general", "block-sockets");
2635 block_sockets = ast_true(val);
2637 val = ast_variable_retrieve(cfg, "general", "webenabled");
2639 webenabled = ast_true(val);
2641 if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2642 if (sscanf(val, "%d", &portno) != 1) {
2643 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2644 portno = DEFAULT_MANAGER_PORT;
2648 if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2649 displayconnects = ast_true(val);
2651 if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2652 timestampevents = ast_true(val);
2654 if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2655 newhttptimeout = atoi(val);
2657 memset(&ba, 0, sizeof(ba));
2658 ba.sin_family = AF_INET;
2659 ba.sin_port = htons(portno);
2661 if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2662 if (!inet_aton(val, &ba.sin_addr)) {
2663 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2664 memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2669 if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2671 /* Can't be done yet */
2675 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2679 AST_LIST_LOCK(&users);
2681 while ((cat = ast_category_browse(cfg, cat))) {
2682 struct ast_variable *var = NULL;
2684 if (!strcasecmp(cat, "general"))
2687 /* Look for an existing entry, if none found - create one and add it to the list */
2688 if (!(user = ast_get_manager_by_name_locked(cat))) {
2689 if (!(user = ast_calloc(1, sizeof(*user))))
2691 /* Copy name over */
2692 ast_copy_string(user->username, cat, sizeof(user->username));
2693 /* Insert into list */
2694 AST_LIST_INSERT_TAIL(&users, user, list);
2697 /* Make sure we keep this user and don't destroy it during cleanup */
2700 var = ast_variable_browse(cfg, cat);
2702 if (!strcasecmp(var->name, "secret")) {
2705 user->secret = ast_strdup(var->value);
2706 } else if (!strcasecmp(var->name, "deny") ) {
2709 user->deny = ast_strdup(var->value);
2710 } else if (!strcasecmp(var->name, "permit") ) {
2713 user->permit = ast_strdup(var->value);
2714 } else if (!strcasecmp(var->name, "read") ) {
2717 user->read = ast_strdup(var->value);
2718 } else if (!strcasecmp(var->name, "write") ) {
2721 user->write = ast_strdup(var->value);
2722 } else if (!strcasecmp(var->name, "displayconnects") )
2723 user->displayconnects = ast_true(var->value);
2726 ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
2732 /* Perform cleanup - essentially prune out old users that no longer exist */
2733 AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
2738 /* We do not need to keep this user so take them out of the list */
2739 AST_LIST_REMOVE_CURRENT(&users, list);
2740 /* Free their memory now */
2753 AST_LIST_TRAVERSE_SAFE_END
2755 AST_LIST_UNLOCK(&users);
2757 ast_config_destroy(cfg);
2759 if (webenabled && enabled) {
2761 ast_http_uri_link(&rawmanuri);
2762 ast_http_uri_link(&manageruri);
2763 ast_http_uri_link(&managerxmluri);
2768 ast_http_uri_unlink(&rawmanuri);
2769 ast_http_uri_unlink(&manageruri);
2770 ast_http_uri_unlink(&managerxmluri);
2775 if (newhttptimeout > 0)
2776 httptimeout = newhttptimeout;
2778 /* If not enabled, do nothing */
2783 asock = socket(AF_INET, SOCK_STREAM, 0);
2785 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2788 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2789 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2790 ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2795 if (listen(asock, 2)) {
2796 ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2801 flags = fcntl(asock, F_GETFL);
2802 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2804 ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2805 ast_pthread_create_background(&t, NULL, accept_thread, NULL);
2810 int reload_manager(void)
2812 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2813 return init_manager();