reduce stack consumption for AMI and AMI/HTTP requests by nearly 20K in most cases
[asterisk-bristuff.git] / main / manager.c
blob2605950076cdb91af76352645b0d95991e71ba0b
1 /*
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.
19 /*! \file
21 * \brief The Asterisk Management Interface - AMI
23 * \author Mark Spencer <markster@digium.com>
25 * Channel Management and more
27 * \ref amiconf
30 /*! \addtogroup Group_AMI AMI functions
32 /*! @{
33 Doxygen group */
35 #include "asterisk.h"
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <sys/time.h>
44 #include <sys/types.h>
45 #include <netdb.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <netinet/tcp.h>
49 #include <arpa/inet.h>
50 #include <signal.h>
51 #include <errno.h>
52 #include <unistd.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_EXTENSION];
74 char data[AST_MAX_EXTENSION];
75 int timeout;
76 char app[AST_MAX_APP];
77 char appdata[AST_MAX_EXTENSION];
78 char cid_name[AST_MAX_EXTENSION];
79 char cid_num[AST_MAX_EXTENSION];
80 char context[AST_MAX_CONTEXT];
81 char exten[AST_MAX_EXTENSION];
82 char idtext[AST_MAX_EXTENSION];
83 char account[AST_MAX_ACCOUNT_CODE];
84 int priority;
85 struct ast_variable *vars;
88 struct eventqent {
89 int usecount;
90 int category;
91 struct eventqent *next;
92 char eventdata[1];
95 static int enabled;
96 static int portno = DEFAULT_MANAGER_PORT;
97 static int asock = -1;
98 static int displayconnects = 1;
99 static int timestampevents;
100 static int httptimeout = 60;
102 static pthread_t t;
103 static int block_sockets;
104 static int num_sessions;
106 /* Protected by the sessions list lock */
107 struct eventqent *master_eventq = NULL;
109 AST_THREADSTORAGE(manager_event_buf, manager_event_buf_init);
110 #define MANAGER_EVENT_BUF_INITSIZE 256
112 AST_THREADSTORAGE(astman_append_buf, astman_append_buf_init);
113 #define ASTMAN_APPEND_BUF_INITSIZE 256
115 static struct permalias {
116 int num;
117 char *label;
118 } perms[] = {
119 { EVENT_FLAG_SYSTEM, "system" },
120 { EVENT_FLAG_CALL, "call" },
121 { EVENT_FLAG_LOG, "log" },
122 { EVENT_FLAG_VERBOSE, "verbose" },
123 { EVENT_FLAG_COMMAND, "command" },
124 { EVENT_FLAG_AGENT, "agent" },
125 { EVENT_FLAG_USER, "user" },
126 { EVENT_FLAG_CONFIG, "config" },
127 { -1, "all" },
128 { 0, "none" },
131 struct mansession {
132 /*! Execution thread */
133 pthread_t t;
134 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
135 ast_mutex_t __lock;
136 /*! socket address */
137 struct sockaddr_in sin;
138 /*! TCP socket */
139 int fd;
140 /*! Whether an HTTP manager is in use */
141 int inuse;
142 /*! Whether an HTTP session should be destroyed */
143 int needdestroy;
144 /*! Whether an HTTP session has someone waiting on events */
145 pthread_t waiting_thread;
146 /*! Unique manager identifer */
147 unsigned long managerid;
148 /*! Session timeout if HTTP */
149 time_t sessiontimeout;
150 /*! Output from manager interface */
151 struct ast_dynamic_str *outputstr;
152 /*! Logged in username */
153 char username[80];
154 /*! Authentication challenge */
155 char challenge[10];
156 /*! Authentication status */
157 int authenticated;
158 /*! Authorization for reading */
159 int readperm;
160 /*! Authorization for writing */
161 int writeperm;
162 /*! Buffer */
163 char inbuf[1024];
164 int inlen;
165 int send_events;
166 int displaysystemname; /*!< Add system name to manager responses and events */
167 /* Queued events that we've not had the ability to send yet */
168 struct eventqent *eventq;
169 /* Timeout for ast_carefulwrite() */
170 int writetimeout;
171 AST_LIST_ENTRY(mansession) list;
174 static AST_LIST_HEAD_STATIC(sessions, mansession);
176 struct ast_manager_user {
177 char username[80];
178 char *secret;
179 char *deny;
180 char *permit;
181 char *read;
182 char *write;
183 unsigned int displayconnects:1;
184 int keep;
185 AST_LIST_ENTRY(ast_manager_user) list;
188 static AST_LIST_HEAD_STATIC(users, ast_manager_user);
190 static struct manager_action *first_action;
191 AST_MUTEX_DEFINE_STATIC(actionlock);
193 /*! \brief Convert authority code to string with serveral options */
194 static char *authority_to_str(int authority, char *res, int reslen)
196 int running_total = 0, i;
198 memset(res, 0, reslen);
199 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
200 if (authority & perms[i].num) {
201 if (*res) {
202 strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
203 running_total++;
205 strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
206 running_total += strlen(perms[i].label);
210 if (ast_strlen_zero(res))
211 ast_copy_string(res, "<none>", reslen);
213 return res;
216 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
218 struct manager_action *cur;
219 int which = 0;
220 char *ret = NULL;
222 ast_mutex_lock(&actionlock);
223 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
224 if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
225 ret = ast_strdup(cur->action);
226 break; /* make sure we exit even if ast_strdup() returns NULL */
229 ast_mutex_unlock(&actionlock);
231 return ret;
234 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
236 while (*src && (*maxlen > 6)) {
237 switch (*src) {
238 case '<':
239 strcpy(*dst, "&lt;");
240 (*dst) += 4;
241 *maxlen -= 4;
242 break;
243 case '>':
244 strcpy(*dst, "&gt;");
245 (*dst) += 4;
246 *maxlen -= 4;
247 break;
248 case '\"':
249 strcpy(*dst, "&quot;");
250 (*dst) += 6;
251 *maxlen -= 6;
252 break;
253 case '\'':
254 strcpy(*dst, "&apos;");
255 (*dst) += 6;
256 *maxlen -= 6;
257 break;
258 case '&':
259 strcpy(*dst, "&amp;");
260 (*dst) += 5;
261 *maxlen -= 5;
262 break;
263 default:
264 *(*dst)++ = lower ? tolower(*src) : *src;
265 (*maxlen)--;
267 src++;
271 static char *xml_translate(char *in, struct ast_variable *vars)
273 struct ast_variable *v;
274 char *dest = NULL;
275 char *out, *tmp, *var, *val;
276 char *objtype = NULL;
277 int colons = 0;
278 int breaks = 0;
279 size_t len;
280 int count = 1;
281 int escaped = 0;
282 int inobj = 0;
283 int x;
285 for (v = vars; v; v = v->next) {
286 if (!dest && !strcasecmp(v->name, "ajaxdest"))
287 dest = v->value;
288 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
289 objtype = v->value;
291 if (!dest)
292 dest = "unknown";
293 if (!objtype)
294 objtype = "generic";
295 for (x = 0; in[x]; x++) {
296 if (in[x] == ':')
297 colons++;
298 else if (in[x] == '\n')
299 breaks++;
300 else if (strchr("&\"<>", in[x]))
301 escaped++;
303 len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
304 out = ast_malloc(len);
305 if (!out)
306 return 0;
307 tmp = out;
308 while (*in) {
309 var = in;
310 while (*in && (*in >= 32))
311 in++;
312 if (*in) {
313 if ((count > 3) && inobj) {
314 ast_build_string(&tmp, &len, " /></response>\n");
315 inobj = 0;
317 count = 0;
318 while (*in && (*in < 32)) {
319 *in = '\0';
320 in++;
321 count++;
323 val = strchr(var, ':');
324 if (val) {
325 *val = '\0';
326 val++;
327 if (*val == ' ')
328 val++;
329 if (!inobj) {
330 ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
331 inobj = 1;
333 ast_build_string(&tmp, &len, " ");
334 xml_copy_escape(&tmp, &len, var, 1);
335 ast_build_string(&tmp, &len, "='");
336 xml_copy_escape(&tmp, &len, val, 0);
337 ast_build_string(&tmp, &len, "'");
341 if (inobj)
342 ast_build_string(&tmp, &len, " /></response>\n");
343 return out;
346 static char *html_translate(char *in)
348 int x;
349 int colons = 0;
350 int breaks = 0;
351 size_t len;
352 int count = 1;
353 char *tmp, *var, *val, *out;
355 for (x=0; in[x]; x++) {
356 if (in[x] == ':')
357 colons++;
358 if (in[x] == '\n')
359 breaks++;
361 len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
362 out = ast_malloc(len);
363 if (!out)
364 return 0;
365 tmp = out;
366 while (*in) {
367 var = in;
368 while (*in && (*in >= 32))
369 in++;
370 if (*in) {
371 if ((count % 4) == 0){
372 ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
374 count = 0;
375 while (*in && (*in < 32)) {
376 *in = '\0';
377 in++;
378 count++;
380 val = strchr(var, ':');
381 if (val) {
382 *val = '\0';
383 val++;
384 if (*val == ' ')
385 val++;
386 ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
390 return out;
395 static struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
397 struct ast_manager_user *user = NULL;
399 AST_LIST_TRAVERSE(&users, user, list)
400 if (!strcasecmp(user->username, name))
401 break;
402 return user;
405 void astman_append(struct mansession *s, const char *fmt, ...)
407 va_list ap;
408 struct ast_dynamic_str *buf;
410 if (!(buf = ast_dynamic_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
411 return;
413 va_start(ap, fmt);
414 ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
415 va_end(ap);
417 if (s->fd > -1)
418 ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->writetimeout);
419 else {
420 if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr))))
421 return;
423 ast_dynamic_str_append(&s->outputstr, 0, "%s", buf->str);
427 static int handle_showmancmd(int fd, int argc, char *argv[])
429 struct manager_action *cur = first_action;
430 char authority[80];
431 int num;
433 if (argc != 4)
434 return RESULT_SHOWUSAGE;
436 ast_mutex_lock(&actionlock);
437 for (; cur; cur = cur->next) { /* Walk the list of actions */
438 for (num = 3; num < argc; num++) {
439 if (!strcasecmp(cur->action, argv[num])) {
440 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 : "");
444 ast_mutex_unlock(&actionlock);
446 return RESULT_SUCCESS;
449 static int handle_showmanager(int fd, int argc, char *argv[])
451 struct ast_manager_user *user = NULL;
453 if (argc != 4)
454 return RESULT_SHOWUSAGE;
456 AST_LIST_LOCK(&users);
458 if (!(user = ast_get_manager_by_name_locked(argv[3]))) {
459 ast_cli(fd, "There is no manager called %s\n", argv[3]);
460 AST_LIST_UNLOCK(&users);
461 return -1;
464 ast_cli(fd,"\n");
465 ast_cli(fd,
466 " username: %s\n"
467 " secret: %s\n"
468 " deny: %s\n"
469 " permit: %s\n"
470 " read: %s\n"
471 " write: %s\n"
472 "displayconnects: %s\n",
473 (user->username ? user->username : "(N/A)"),
474 (user->secret ? user->secret : "(N/A)"),
475 (user->deny ? user->deny : "(N/A)"),
476 (user->permit ? user->permit : "(N/A)"),
477 (user->read ? user->read : "(N/A)"),
478 (user->write ? user->write : "(N/A)"),
479 (user->displayconnects ? "yes" : "no"));
481 AST_LIST_UNLOCK(&users);
483 return RESULT_SUCCESS;
487 static int handle_showmanagers(int fd, int argc, char *argv[])
489 struct ast_manager_user *user = NULL;
490 int count_amu = 0;
492 if (argc != 3)
493 return RESULT_SHOWUSAGE;
495 AST_LIST_LOCK(&users);
497 /* If there are no users, print out something along those lines */
498 if (AST_LIST_EMPTY(&users)) {
499 ast_cli(fd, "There are no manager users.\n");
500 AST_LIST_UNLOCK(&users);
501 return RESULT_SUCCESS;
504 ast_cli(fd, "\nusername\n--------\n");
506 AST_LIST_TRAVERSE(&users, user, list) {
507 ast_cli(fd, "%s\n", user->username);
508 count_amu++;
511 AST_LIST_UNLOCK(&users);
513 ast_cli(fd,"-------------------\n");
514 ast_cli(fd,"%d manager users configured.\n", count_amu);
516 return RESULT_SUCCESS;
520 /*! \brief CLI command
521 Should change to "manager show commands" */
522 static int handle_showmancmds(int fd, int argc, char *argv[])
524 struct manager_action *cur = first_action;
525 char authority[80];
526 char *format = " %-15.15s %-15.15s %-55.55s\n";
528 ast_cli(fd, format, "Action", "Privilege", "Synopsis");
529 ast_cli(fd, format, "------", "---------", "--------");
531 ast_mutex_lock(&actionlock);
532 for (; cur; cur = cur->next) /* Walk the list of actions */
533 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
534 ast_mutex_unlock(&actionlock);
536 return RESULT_SUCCESS;
539 /*! \brief CLI command show manager connected */
540 /* Should change to "manager show connected" */
541 static int handle_showmanconn(int fd, int argc, char *argv[])
543 struct mansession *s;
544 char *format = " %-15.15s %-15.15s\n";
546 ast_cli(fd, format, "Username", "IP Address");
548 AST_LIST_LOCK(&sessions);
549 AST_LIST_TRAVERSE(&sessions, s, list)
550 ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
551 AST_LIST_UNLOCK(&sessions);
553 return RESULT_SUCCESS;
556 /*! \brief CLI command show manager connected */
557 /* Should change to "manager show connected" */
558 static int handle_showmaneventq(int fd, int argc, char *argv[])
560 struct eventqent *s;
562 AST_LIST_LOCK(&sessions);
563 for (s = master_eventq; s; s = s->next) {
564 ast_cli(fd, "Usecount: %d\n",s->usecount);
565 ast_cli(fd, "Category: %d\n", s->category);
566 ast_cli(fd, "Event:\n%s", s->eventdata);
568 AST_LIST_UNLOCK(&sessions);
570 return RESULT_SUCCESS;
573 static char showmancmd_help[] =
574 "Usage: manager show command <actionname>\n"
575 " Shows the detailed description for a specific Asterisk manager interface command.\n";
577 static char showmancmds_help[] =
578 "Usage: manager show commands\n"
579 " Prints a listing of all the available Asterisk manager interface commands.\n";
581 static char showmanconn_help[] =
582 "Usage: manager show connected\n"
583 " Prints a listing of the users that are currently connected to the\n"
584 "Asterisk manager interface.\n";
586 static char showmaneventq_help[] =
587 "Usage: manager show eventq\n"
588 " Prints a listing of all events pending in the Asterisk manger\n"
589 "event queue.\n";
591 static char showmanagers_help[] =
592 "Usage: manager show users\n"
593 " Prints a listing of all managers that are currently configured on that\n"
594 " system.\n";
596 static char showmanager_help[] =
597 " Usage: manager show user <user>\n"
598 " Display all information related to the manager user specified.\n";
600 static struct ast_cli_entry cli_show_manager_command_deprecated = {
601 { "show", "manager", "command", NULL },
602 handle_showmancmd, NULL,
603 NULL, complete_show_mancmd };
605 static struct ast_cli_entry cli_show_manager_commands_deprecated = {
606 { "show", "manager", "commands", NULL },
607 handle_showmancmds, NULL,
608 NULL };
610 static struct ast_cli_entry cli_show_manager_connected_deprecated = {
611 { "show", "manager", "connected", NULL },
612 handle_showmanconn, NULL,
613 NULL };
615 static struct ast_cli_entry cli_show_manager_eventq_deprecated = {
616 { "show", "manager", "eventq", NULL },
617 handle_showmaneventq, NULL,
618 NULL };
620 static struct ast_cli_entry cli_manager[] = {
621 { { "manager", "show", "command", NULL },
622 handle_showmancmd, "Show a manager interface command",
623 showmancmd_help, complete_show_mancmd, &cli_show_manager_command_deprecated },
625 { { "manager", "show", "commands", NULL },
626 handle_showmancmds, "List manager interface commands",
627 showmancmds_help, NULL, &cli_show_manager_commands_deprecated },
629 { { "manager", "show", "connected", NULL },
630 handle_showmanconn, "List connected manager interface users",
631 showmanconn_help, NULL, &cli_show_manager_connected_deprecated },
633 { { "manager", "show", "eventq", NULL },
634 handle_showmaneventq, "List manager interface queued events",
635 showmaneventq_help, NULL, &cli_show_manager_eventq_deprecated },
637 { { "manager", "show", "users", NULL },
638 handle_showmanagers, "List configured manager users",
639 showmanagers_help, NULL, NULL },
641 { { "manager", "show", "user", NULL },
642 handle_showmanager, "Display information on a specific manager user",
643 showmanager_help, NULL, NULL },
646 static void unuse_eventqent(struct eventqent *e)
648 if (ast_atomic_dec_and_test(&e->usecount) && e->next)
649 pthread_kill(t, SIGURG);
652 static void free_session(struct mansession *s)
654 struct eventqent *eqe;
655 if (s->fd > -1)
656 close(s->fd);
657 if (s->outputstr)
658 free(s->outputstr);
659 ast_mutex_destroy(&s->__lock);
660 while (s->eventq) {
661 eqe = s->eventq;
662 s->eventq = s->eventq->next;
663 unuse_eventqent(eqe);
665 free(s);
668 static void destroy_session(struct mansession *s)
670 AST_LIST_LOCK(&sessions);
671 AST_LIST_REMOVE(&sessions, s, list);
672 AST_LIST_UNLOCK(&sessions);
674 ast_atomic_fetchadd_int(&num_sessions, -1);
675 free_session(s);
678 const char *astman_get_header(const struct message *m, char *var)
680 char cmp[80];
681 int x;
683 snprintf(cmp, sizeof(cmp), "%s: ", var);
685 for (x = 0; x < m->hdrcount; x++) {
686 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
687 return m->headers[x] + strlen(cmp);
690 return "";
693 struct ast_variable *astman_get_variables(const struct message *m)
695 int varlen, x, y;
696 struct ast_variable *head = NULL, *cur;
697 char *var, *val;
699 char *parse;
700 AST_DECLARE_APP_ARGS(args,
701 AST_APP_ARG(vars)[32];
704 varlen = strlen("Variable: ");
706 for (x = 0; x < m->hdrcount; x++) {
707 if (strncasecmp("Variable: ", m->headers[x], varlen))
708 continue;
710 parse = ast_strdupa(m->headers[x] + varlen);
712 AST_STANDARD_APP_ARGS(args, parse);
713 if (args.argc) {
714 for (y = 0; y < args.argc; y++) {
715 if (!args.vars[y])
716 continue;
717 var = val = ast_strdupa(args.vars[y]);
718 strsep(&val, "=");
719 if (!val || ast_strlen_zero(var))
720 continue;
721 cur = ast_variable_new(var, val);
722 if (head) {
723 cur->next = head;
724 head = cur;
725 } else
726 head = cur;
731 return head;
734 /*! \note NOTE:
735 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
736 hold the session lock _or_ be running in an action callback (in which case s->busy will
737 be non-zero). In either of these cases, there is no need to lock-protect the session's
738 fd, since no other output will be sent (events will be queued), and no input will
739 be read until either the current action finishes or get_input() obtains the session
740 lock.
742 void astman_send_error(struct mansession *s, const struct message *m, char *error)
744 const char *id = astman_get_header(m,"ActionID");
746 astman_append(s, "Response: Error\r\n");
747 if (!ast_strlen_zero(id))
748 astman_append(s, "ActionID: %s\r\n", id);
749 astman_append(s, "Message: %s\r\n\r\n", error);
752 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
754 const char *id = astman_get_header(m,"ActionID");
756 astman_append(s, "Response: %s\r\n", resp);
757 if (!ast_strlen_zero(id))
758 astman_append(s, "ActionID: %s\r\n", id);
759 if (msg)
760 astman_append(s, "Message: %s\r\n\r\n", msg);
761 else
762 astman_append(s, "\r\n");
765 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
767 astman_send_response(s, m, "Success", msg);
770 /*! Tells you if smallstr exists inside bigstr
771 which is delim by delim and uses no buf or stringsep
772 ast_instring("this|that|more","this",',') == 1;
774 feel free to move this to app.c -anthm */
775 static int ast_instring(const char *bigstr, const char *smallstr, char delim)
777 const char *val = bigstr, *next;
779 do {
780 if ((next = strchr(val, delim))) {
781 if (!strncmp(val, smallstr, (next - val)))
782 return 1;
783 else
784 continue;
785 } else
786 return !strcmp(smallstr, val);
788 } while (*(val = (next + 1)));
790 return 0;
793 static int get_perm(const char *instr)
795 int x = 0, ret = 0;
797 if (!instr)
798 return 0;
800 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
801 if (ast_instring(instr, perms[x].label, ','))
802 ret |= perms[x].num;
805 return ret;
808 static int ast_is_number(char *string)
810 int ret = 1, x = 0;
812 if (!string)
813 return 0;
815 for (x = 0; x < strlen(string); x++) {
816 if (!(string[x] >= 48 && string[x] <= 57)) {
817 ret = 0;
818 break;
822 return ret ? atoi(string) : 0;
825 static int strings_to_mask(const char *string)
827 int x, ret = -1;
829 x = ast_is_number((char *) string);
831 if (x)
832 ret = x;
833 else if (ast_strlen_zero(string))
834 ret = -1;
835 else if (ast_false(string))
836 ret = 0;
837 else if (ast_true(string)) {
838 ret = 0;
839 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
840 ret |= perms[x].num;
841 } else {
842 ret = 0;
843 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
844 if (ast_instring(string, perms[x].label, ','))
845 ret |= perms[x].num;
849 return ret;
852 /*! \brief
853 Rather than braindead on,off this now can also accept a specific int mask value
854 or a ',' delim list of mask strings (the same as manager.conf) -anthm
856 static int set_eventmask(struct mansession *s, const char *eventmask)
858 int maskint = strings_to_mask(eventmask);
860 ast_mutex_lock(&s->__lock);
861 if (maskint >= 0)
862 s->send_events = maskint;
863 ast_mutex_unlock(&s->__lock);
865 return maskint;
868 static int authenticate(struct mansession *s, const struct message *m)
870 struct ast_config *cfg;
871 char *cat;
872 const char *user = astman_get_header(m, "Username");
873 const char *pass = astman_get_header(m, "Secret");
874 const char *authtype = astman_get_header(m, "AuthType");
875 const char *key = astman_get_header(m, "Key");
876 const char *events = astman_get_header(m, "Events");
878 cfg = ast_config_load("manager.conf");
879 if (!cfg)
880 return -1;
881 cat = ast_category_browse(cfg, NULL);
882 while (cat) {
883 if (strcasecmp(cat, "general")) {
884 /* This is a user */
885 if (!strcasecmp(cat, user)) {
886 struct ast_variable *v;
887 struct ast_ha *ha = NULL;
888 char *password = NULL;
890 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
891 if (!strcasecmp(v->name, "secret")) {
892 password = v->value;
893 } else if (!strcasecmp(v->name, "displaysystemname")) {
894 if (ast_true(v->value)) {
895 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
896 s->displaysystemname = 1;
897 } else {
898 ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
901 } else if (!strcasecmp(v->name, "permit") ||
902 !strcasecmp(v->name, "deny")) {
903 ha = ast_append_ha(v->name, v->value, ha);
904 } else if (!strcasecmp(v->name, "writetimeout")) {
905 int val = atoi(v->value);
907 if (val < 100)
908 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
909 else
910 s->writetimeout = val;
914 if (ha && !ast_apply_ha(ha, &(s->sin))) {
915 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
916 ast_free_ha(ha);
917 ast_config_destroy(cfg);
918 return -1;
919 } else if (ha)
920 ast_free_ha(ha);
921 if (!strcasecmp(authtype, "MD5")) {
922 if (!ast_strlen_zero(key) && s->challenge) {
923 int x;
924 int len = 0;
925 char md5key[256] = "";
926 struct MD5Context md5;
927 unsigned char digest[16];
928 MD5Init(&md5);
929 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
930 MD5Update(&md5, (unsigned char *) password, strlen(password));
931 MD5Final(digest, &md5);
932 for (x=0; x<16; x++)
933 len += sprintf(md5key + len, "%2.2x", digest[x]);
934 if (!strcmp(md5key, key))
935 break;
936 else {
937 ast_config_destroy(cfg);
938 return -1;
941 } else if (password && !strcmp(password, pass)) {
942 break;
943 } else {
944 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
945 ast_config_destroy(cfg);
946 return -1;
950 cat = ast_category_browse(cfg, cat);
952 if (cat) {
953 ast_copy_string(s->username, cat, sizeof(s->username));
954 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
955 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
956 ast_config_destroy(cfg);
957 if (events)
958 set_eventmask(s, events);
959 return 0;
961 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
962 ast_config_destroy(cfg);
963 return -1;
966 /*! \brief Manager PING */
967 static char mandescr_ping[] =
968 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
969 " manager connection open.\n"
970 "Variables: NONE\n";
972 static int action_ping(struct mansession *s, const struct message *m)
974 astman_send_response(s, m, "Pong", NULL);
975 return 0;
978 static char mandescr_getconfig[] =
979 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
980 "file by category and contents.\n"
981 "Variables:\n"
982 " Filename: Configuration filename (e.g. foo.conf)\n";
984 static int action_getconfig(struct mansession *s, const struct message *m)
986 struct ast_config *cfg;
987 const char *fn = astman_get_header(m, "Filename");
988 int catcount = 0;
989 int lineno = 0;
990 char *category=NULL;
991 struct ast_variable *v;
992 char idText[256] = "";
993 const char *id = astman_get_header(m, "ActionID");
995 if (!ast_strlen_zero(id))
996 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
998 if (ast_strlen_zero(fn)) {
999 astman_send_error(s, m, "Filename not specified");
1000 return 0;
1002 if (!(cfg = ast_config_load_with_comments(fn))) {
1003 astman_send_error(s, m, "Config file not found");
1004 return 0;
1006 astman_append(s, "Response: Success\r\n%s", idText);
1007 while ((category = ast_category_browse(cfg, category))) {
1008 lineno = 0;
1009 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1010 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1011 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1012 catcount++;
1014 ast_config_destroy(cfg);
1015 astman_append(s, "\r\n");
1017 return 0;
1021 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
1023 int x;
1024 char hdr[40];
1025 const char *action, *cat, *var, *value, *match;
1026 struct ast_category *category;
1027 struct ast_variable *v;
1029 for (x=0;x<100000;x++) {
1030 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1031 action = astman_get_header(m, hdr);
1032 if (ast_strlen_zero(action))
1033 break;
1034 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1035 cat = astman_get_header(m, hdr);
1036 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1037 var = astman_get_header(m, hdr);
1038 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1039 value = astman_get_header(m, hdr);
1040 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1041 match = astman_get_header(m, hdr);
1042 if (!strcasecmp(action, "newcat")) {
1043 if (!ast_strlen_zero(cat)) {
1044 category = ast_category_new(cat);
1045 if (category) {
1046 ast_category_append(cfg, category);
1049 } else if (!strcasecmp(action, "renamecat")) {
1050 if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1051 category = ast_category_get(cfg, cat);
1052 if (category)
1053 ast_category_rename(category, value);
1055 } else if (!strcasecmp(action, "delcat")) {
1056 if (!ast_strlen_zero(cat))
1057 ast_category_delete(cfg, (char *) cat);
1058 } else if (!strcasecmp(action, "update")) {
1059 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1060 ast_variable_update(category, (char *) var, (char *) value, (char *) match);
1061 } else if (!strcasecmp(action, "delete")) {
1062 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1063 ast_variable_delete(category, (char *) var, (char *) match);
1064 } else if (!strcasecmp(action, "append")) {
1065 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1066 (category = ast_category_get(cfg, cat)) &&
1067 (v = ast_variable_new(var, value))){
1068 if (match && !strcasecmp(match, "object"))
1069 v->object = 1;
1070 ast_variable_append(category, v);
1076 static char mandescr_updateconfig[] =
1077 "Description: A 'UpdateConfig' action will dump the contents of a configuration\n"
1078 "file by category and contents.\n"
1079 "Variables (X's represent 6 digit number beginning with 000000):\n"
1080 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1081 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1082 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1083 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1084 " Cat-XXXXXX: Category to operate on\n"
1085 " Var-XXXXXX: Variable to work on\n"
1086 " Value-XXXXXX: Value to work on\n"
1087 " Match-XXXXXX: Extra match required to match line\n";
1089 static int action_updateconfig(struct mansession *s, const struct message *m)
1091 struct ast_config *cfg;
1092 const char *sfn = astman_get_header(m, "SrcFilename");
1093 const char *dfn = astman_get_header(m, "DstFilename");
1094 int res;
1095 char idText[256] = "";
1096 const char *id = astman_get_header(m, "ActionID");
1097 const char *rld = astman_get_header(m, "Reload");
1099 if (!ast_strlen_zero(id))
1100 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1102 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1103 astman_send_error(s, m, "Filename not specified");
1104 return 0;
1106 if (!(cfg = ast_config_load_with_comments(sfn))) {
1107 astman_send_error(s, m, "Config file not found");
1108 return 0;
1110 handle_updates(s, m, cfg);
1111 res = config_text_file_save(dfn, cfg, "Manager");
1112 ast_config_destroy(cfg);
1113 if (res) {
1114 astman_send_error(s, m, "Save of config failed");
1115 return 0;
1117 astman_append(s, "Response: Success\r\n%s\r\n", idText);
1118 if (!ast_strlen_zero(rld)) {
1119 if (ast_true(rld))
1120 rld = NULL;
1121 ast_module_reload(rld);
1123 return 0;
1126 /*! \brief Manager WAITEVENT */
1127 static char mandescr_waitevent[] =
1128 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1129 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1130 "session, events will be generated and queued.\n"
1131 "Variables: \n"
1132 " Timeout: Maximum time to wait for events\n";
1134 static int action_waitevent(struct mansession *s, const struct message *m)
1136 const char *timeouts = astman_get_header(m, "Timeout");
1137 int timeout = -1, max;
1138 int x;
1139 int needexit = 0;
1140 time_t now;
1141 struct eventqent *eqe;
1142 const char *id = astman_get_header(m,"ActionID");
1143 char idText[256] = "";
1145 if (!ast_strlen_zero(id))
1146 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1148 if (!ast_strlen_zero(timeouts)) {
1149 sscanf(timeouts, "%i", &timeout);
1152 ast_mutex_lock(&s->__lock);
1153 if (s->waiting_thread != AST_PTHREADT_NULL) {
1154 pthread_kill(s->waiting_thread, SIGURG);
1156 if (s->sessiontimeout) {
1157 time(&now);
1158 max = s->sessiontimeout - now - 10;
1159 if (max < 0)
1160 max = 0;
1161 if ((timeout < 0) || (timeout > max))
1162 timeout = max;
1163 if (!s->send_events)
1164 s->send_events = -1;
1165 /* Once waitevent is called, always queue events from now on */
1167 ast_mutex_unlock(&s->__lock);
1168 s->waiting_thread = pthread_self();
1169 if (option_debug)
1170 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
1171 for (x=0; ((x < timeout) || (timeout < 0)); x++) {
1172 ast_mutex_lock(&s->__lock);
1173 if (s->eventq && s->eventq->next)
1174 needexit = 1;
1175 if (s->waiting_thread != pthread_self())
1176 needexit = 1;
1177 if (s->needdestroy)
1178 needexit = 1;
1179 ast_mutex_unlock(&s->__lock);
1180 if (needexit)
1181 break;
1182 if (s->fd > 0) {
1183 if (ast_wait_for_input(s->fd, 1000))
1184 break;
1185 } else {
1186 sleep(1);
1189 if (option_debug)
1190 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
1191 ast_mutex_lock(&s->__lock);
1192 if (s->waiting_thread == pthread_self()) {
1193 astman_send_response(s, m, "Success", "Waiting for Event...");
1194 /* Only show events if we're the most recent waiter */
1195 while(s->eventq->next) {
1196 eqe = s->eventq->next;
1197 if (((s->readperm & eqe->category) == eqe->category) &&
1198 ((s->send_events & eqe->category) == eqe->category)) {
1199 astman_append(s, "%s", eqe->eventdata);
1201 unuse_eventqent(s->eventq);
1202 s->eventq = eqe;
1204 astman_append(s,
1205 "Event: WaitEventComplete\r\n"
1206 "%s"
1207 "\r\n", idText);
1208 s->waiting_thread = AST_PTHREADT_NULL;
1209 } else {
1210 ast_log(LOG_DEBUG, "Abandoning event request!\n");
1212 ast_mutex_unlock(&s->__lock);
1213 return 0;
1216 static char mandescr_listcommands[] =
1217 "Description: Returns the action name and synopsis for every\n"
1218 " action that is available to the user\n"
1219 "Variables: NONE\n";
1221 static int action_listcommands(struct mansession *s, const struct message *m)
1223 struct manager_action *cur = first_action;
1224 char idText[256] = "";
1225 char temp[BUFSIZ];
1226 const char *id = astman_get_header(m,"ActionID");
1228 if (!ast_strlen_zero(id))
1229 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1230 astman_append(s, "Response: Success\r\n%s", idText);
1231 ast_mutex_lock(&actionlock);
1232 while (cur) { /* Walk the list of actions */
1233 if ((s->writeperm & cur->authority) == cur->authority)
1234 astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
1235 cur = cur->next;
1237 ast_mutex_unlock(&actionlock);
1238 astman_append(s, "\r\n");
1240 return 0;
1243 static char mandescr_events[] =
1244 "Description: Enable/Disable sending of events to this manager\n"
1245 " client.\n"
1246 "Variables:\n"
1247 " EventMask: 'on' if all events should be sent,\n"
1248 " 'off' if no events should be sent,\n"
1249 " 'system,call,log' to select which flags events should have to be sent.\n";
1251 static int action_events(struct mansession *s, const struct message *m)
1253 const char *mask = astman_get_header(m, "EventMask");
1254 int res;
1256 res = set_eventmask(s, mask);
1257 if (res > 0)
1258 astman_send_response(s, m, "Events On", NULL);
1259 else if (res == 0)
1260 astman_send_response(s, m, "Events Off", NULL);
1262 return 0;
1265 static char mandescr_logoff[] =
1266 "Description: Logoff this manager session\n"
1267 "Variables: NONE\n";
1269 static int action_logoff(struct mansession *s, const struct message *m)
1271 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1272 return -1;
1275 static char mandescr_hangup[] =
1276 "Description: Hangup a channel\n"
1277 "Variables: \n"
1278 " Channel: The channel name to be hungup\n";
1280 static int action_hangup(struct mansession *s, const struct message *m)
1282 struct ast_channel *c = NULL;
1283 const char *name = astman_get_header(m, "Channel");
1284 if (ast_strlen_zero(name)) {
1285 astman_send_error(s, m, "No channel specified");
1286 return 0;
1288 c = ast_get_channel_by_name_locked(name);
1289 if (!c) {
1290 astman_send_error(s, m, "No such channel");
1291 return 0;
1293 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1294 ast_channel_unlock(c);
1295 astman_send_ack(s, m, "Channel Hungup");
1296 return 0;
1299 static char mandescr_setvar[] =
1300 "Description: Set a global or local channel variable.\n"
1301 "Variables: (Names marked with * are required)\n"
1302 " Channel: Channel to set variable for\n"
1303 " *Variable: Variable name\n"
1304 " *Value: Value\n";
1306 static int action_setvar(struct mansession *s, const struct message *m)
1308 struct ast_channel *c = NULL;
1309 const char *name = astman_get_header(m, "Channel");
1310 const char *varname = astman_get_header(m, "Variable");
1311 const char *varval = astman_get_header(m, "Value");
1313 if (ast_strlen_zero(varname)) {
1314 astman_send_error(s, m, "No variable specified");
1315 return 0;
1318 if (ast_strlen_zero(varval)) {
1319 astman_send_error(s, m, "No value specified");
1320 return 0;
1323 if (!ast_strlen_zero(name)) {
1324 c = ast_get_channel_by_name_locked(name);
1325 if (!c) {
1326 astman_send_error(s, m, "No such channel");
1327 return 0;
1331 pbx_builtin_setvar_helper(c, varname, varval);
1333 if (c)
1334 ast_channel_unlock(c);
1336 astman_send_ack(s, m, "Variable Set");
1338 return 0;
1341 static char mandescr_getvar[] =
1342 "Description: Get the value of a global or local channel variable.\n"
1343 "Variables: (Names marked with * are required)\n"
1344 " Channel: Channel to read variable from\n"
1345 " *Variable: Variable name\n"
1346 " ActionID: Optional Action id for message matching.\n";
1348 static int action_getvar(struct mansession *s, const struct message *m)
1350 struct ast_channel *c = NULL;
1351 const char *name = astman_get_header(m, "Channel");
1352 const char *varname = astman_get_header(m, "Variable");
1353 const char *id = astman_get_header(m,"ActionID");
1354 char *varval;
1355 char workspace[1024];
1357 if (ast_strlen_zero(varname)) {
1358 astman_send_error(s, m, "No variable specified");
1359 return 0;
1362 if (!ast_strlen_zero(name)) {
1363 c = ast_get_channel_by_name_locked(name);
1364 if (!c) {
1365 astman_send_error(s, m, "No such channel");
1366 return 0;
1370 if (varname[strlen(varname) - 1] == ')') {
1371 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1372 } else {
1373 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1376 if (c)
1377 ast_channel_unlock(c);
1378 astman_append(s, "Response: Success\r\n"
1379 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1380 if (!ast_strlen_zero(id))
1381 astman_append(s, "ActionID: %s\r\n",id);
1382 astman_append(s, "\r\n");
1384 return 0;
1388 /*! \brief Manager "status" command to show channels */
1389 /* Needs documentation... */
1390 static int action_status(struct mansession *s, const struct message *m)
1392 const char *id = astman_get_header(m,"ActionID");
1393 const char *name = astman_get_header(m,"Channel");
1394 char idText[256] = "";
1395 struct ast_channel *c;
1396 char bridge[256];
1397 struct timeval now = ast_tvnow();
1398 long elapsed_seconds = 0;
1399 int all = ast_strlen_zero(name); /* set if we want all channels */
1401 astman_send_ack(s, m, "Channel status will follow");
1402 if (!ast_strlen_zero(id))
1403 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1404 if (all)
1405 c = ast_channel_walk_locked(NULL);
1406 else {
1407 c = ast_get_channel_by_name_locked(name);
1408 if (!c) {
1409 astman_send_error(s, m, "No such channel");
1410 return 0;
1413 /* if we look by name, we break after the first iteration */
1414 while (c) {
1415 if (c->_bridge)
1416 snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1417 else
1418 bridge[0] = '\0';
1419 if (c->pbx) {
1420 if (c->cdr) {
1421 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1423 astman_append(s,
1424 "Event: Status\r\n"
1425 "Privilege: Call\r\n"
1426 "Channel: %s\r\n"
1427 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1428 "CallerIDNum: %s\r\n"
1429 "CallerIDName: %s\r\n"
1430 "Account: %s\r\n"
1431 "State: %s\r\n"
1432 "Context: %s\r\n"
1433 "Extension: %s\r\n"
1434 "Priority: %d\r\n"
1435 "Seconds: %ld\r\n"
1436 "%s"
1437 "Uniqueid: %s\r\n"
1438 "%s"
1439 "\r\n",
1440 c->name,
1441 S_OR(c->cid.cid_num, "<unknown>"),
1442 S_OR(c->cid.cid_num, "<unknown>"),
1443 S_OR(c->cid.cid_name, "<unknown>"),
1444 c->accountcode,
1445 ast_state2str(c->_state), c->context,
1446 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1447 } else {
1448 astman_append(s,
1449 "Event: Status\r\n"
1450 "Privilege: Call\r\n"
1451 "Channel: %s\r\n"
1452 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1453 "CallerIDNum: %s\r\n"
1454 "CallerIDName: %s\r\n"
1455 "Account: %s\r\n"
1456 "State: %s\r\n"
1457 "%s"
1458 "Uniqueid: %s\r\n"
1459 "%s"
1460 "\r\n",
1461 c->name,
1462 S_OR(c->cid.cid_num, "<unknown>"),
1463 S_OR(c->cid.cid_num, "<unknown>"),
1464 S_OR(c->cid.cid_name, "<unknown>"),
1465 c->accountcode,
1466 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1468 ast_channel_unlock(c);
1469 if (!all)
1470 break;
1471 c = ast_channel_walk_locked(c);
1473 astman_append(s,
1474 "Event: StatusComplete\r\n"
1475 "%s"
1476 "\r\n",idText);
1477 return 0;
1480 static char mandescr_redirect[] =
1481 "Description: Redirect (transfer) a call.\n"
1482 "Variables: (Names marked with * are required)\n"
1483 " *Channel: Channel to redirect\n"
1484 " ExtraChannel: Second call leg to transfer (optional)\n"
1485 " *Exten: Extension to transfer to\n"
1486 " *Context: Context to transfer to\n"
1487 " *Priority: Priority to transfer to\n"
1488 " ActionID: Optional Action id for message matching.\n";
1490 /*! \brief action_redirect: The redirect manager command */
1491 static int action_redirect(struct mansession *s, const struct message *m)
1493 const char *name = astman_get_header(m, "Channel");
1494 const char *name2 = astman_get_header(m, "ExtraChannel");
1495 const char *exten = astman_get_header(m, "Exten");
1496 const char *context = astman_get_header(m, "Context");
1497 const char *priority = astman_get_header(m, "Priority");
1498 struct ast_channel *chan, *chan2 = NULL;
1499 int pi = 0;
1500 int res;
1502 if (ast_strlen_zero(name)) {
1503 astman_send_error(s, m, "Channel not specified");
1504 return 0;
1506 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1507 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1508 astman_send_error(s, m, "Invalid priority\n");
1509 return 0;
1512 /* XXX watch out, possible deadlock!!! */
1513 chan = ast_get_channel_by_name_locked(name);
1514 if (!chan) {
1515 char buf[BUFSIZ];
1516 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1517 astman_send_error(s, m, buf);
1518 return 0;
1520 if (!ast_strlen_zero(name2))
1521 chan2 = ast_get_channel_by_name_locked(name2);
1522 res = ast_async_goto(chan, context, exten, pi);
1523 if (!res) {
1524 if (!ast_strlen_zero(name2)) {
1525 if (chan2)
1526 res = ast_async_goto(chan2, context, exten, pi);
1527 else
1528 res = -1;
1529 if (!res)
1530 astman_send_ack(s, m, "Dual Redirect successful");
1531 else
1532 astman_send_error(s, m, "Secondary redirect failed");
1533 } else
1534 astman_send_ack(s, m, "Redirect successful");
1535 } else
1536 astman_send_error(s, m, "Redirect failed");
1537 if (chan)
1538 ast_channel_unlock(chan);
1539 if (chan2)
1540 ast_channel_unlock(chan2);
1541 return 0;
1544 static char mandescr_command[] =
1545 "Description: Run a CLI command.\n"
1546 "Variables: (Names marked with * are required)\n"
1547 " *Command: Asterisk CLI command to run\n"
1548 " ActionID: Optional Action id for message matching.\n";
1550 /*! \brief action_command: Manager command "command" - execute CLI command */
1551 static int action_command(struct mansession *s, const struct message *m)
1553 const char *cmd = astman_get_header(m, "Command");
1554 const char *id = astman_get_header(m, "ActionID");
1555 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1556 if (!ast_strlen_zero(id))
1557 astman_append(s, "ActionID: %s\r\n", id);
1558 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1559 ast_cli_command(s->fd, cmd);
1560 astman_append(s, "--END COMMAND--\r\n\r\n");
1561 return 0;
1564 static void *fast_originate(void *data)
1566 struct fast_originate_helper *in = data;
1567 int res;
1568 int reason = 0;
1569 struct ast_channel *chan = NULL;
1570 char requested_channel[AST_CHANNEL_NAME];
1572 if (!ast_strlen_zero(in->app)) {
1573 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1574 S_OR(in->cid_num, NULL),
1575 S_OR(in->cid_name, NULL),
1576 in->vars, in->account, &chan);
1577 } else {
1578 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1579 S_OR(in->cid_num, NULL),
1580 S_OR(in->cid_name, NULL),
1581 in->vars, in->account, &chan);
1584 if (!chan)
1585 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
1586 /* Tell the manager what happened with the channel */
1587 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1588 "%s"
1589 "Response: %s\r\n"
1590 "Channel: %s\r\n"
1591 "Context: %s\r\n"
1592 "Exten: %s\r\n"
1593 "Reason: %d\r\n"
1594 "Uniqueid: %s\r\n"
1595 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1596 "CallerIDNum: %s\r\n"
1597 "CallerIDName: %s\r\n",
1598 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
1599 chan ? chan->uniqueid : "<null>",
1600 S_OR(in->cid_num, "<unknown>"),
1601 S_OR(in->cid_num, "<unknown>"),
1602 S_OR(in->cid_name, "<unknown>")
1605 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1606 if (chan)
1607 ast_channel_unlock(chan);
1608 free(in);
1609 return NULL;
1612 static char mandescr_originate[] =
1613 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1614 " Application/Data\n"
1615 "Variables: (Names marked with * are required)\n"
1616 " *Channel: Channel name to call\n"
1617 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1618 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1619 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1620 " Application: Application to use\n"
1621 " Data: Data to use (requires 'Application')\n"
1622 " Timeout: How long to wait for call to be answered (in ms)\n"
1623 " CallerID: Caller ID to be set on the outgoing channel\n"
1624 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1625 " Account: Account code\n"
1626 " Async: Set to 'true' for fast origination\n";
1628 static int action_originate(struct mansession *s, const struct message *m)
1630 const char *name = astman_get_header(m, "Channel");
1631 const char *exten = astman_get_header(m, "Exten");
1632 const char *context = astman_get_header(m, "Context");
1633 const char *priority = astman_get_header(m, "Priority");
1634 const char *timeout = astman_get_header(m, "Timeout");
1635 const char *callerid = astman_get_header(m, "CallerID");
1636 const char *account = astman_get_header(m, "Account");
1637 const char *app = astman_get_header(m, "Application");
1638 const char *appdata = astman_get_header(m, "Data");
1639 const char *async = astman_get_header(m, "Async");
1640 const char *id = astman_get_header(m, "ActionID");
1641 struct ast_variable *vars = astman_get_variables(m);
1642 char *tech, *data;
1643 char *l = NULL, *n = NULL;
1644 int pi = 0;
1645 int res;
1646 int to = 30000;
1647 int reason = 0;
1648 char tmp[256];
1649 char tmp2[256];
1651 pthread_t th;
1652 pthread_attr_t attr;
1653 if (!name) {
1654 astman_send_error(s, m, "Channel not specified");
1655 return 0;
1657 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1658 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1659 astman_send_error(s, m, "Invalid priority\n");
1660 return 0;
1663 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1664 astman_send_error(s, m, "Invalid timeout\n");
1665 return 0;
1667 ast_copy_string(tmp, name, sizeof(tmp));
1668 tech = tmp;
1669 data = strchr(tmp, '/');
1670 if (!data) {
1671 astman_send_error(s, m, "Invalid channel\n");
1672 return 0;
1674 *data++ = '\0';
1675 ast_copy_string(tmp2, callerid, sizeof(tmp2));
1676 ast_callerid_parse(tmp2, &n, &l);
1677 if (n) {
1678 if (ast_strlen_zero(n))
1679 n = NULL;
1681 if (l) {
1682 ast_shrink_phone_number(l);
1683 if (ast_strlen_zero(l))
1684 l = NULL;
1686 if (ast_true(async)) {
1687 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1688 if (!fast) {
1689 res = -1;
1690 } else {
1691 if (!ast_strlen_zero(id))
1692 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1693 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1694 ast_copy_string(fast->data, data, sizeof(fast->data));
1695 ast_copy_string(fast->app, app, sizeof(fast->app));
1696 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1697 if (l)
1698 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1699 if (n)
1700 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1701 fast->vars = vars;
1702 ast_copy_string(fast->context, context, sizeof(fast->context));
1703 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1704 ast_copy_string(fast->account, account, sizeof(fast->account));
1705 fast->timeout = to;
1706 fast->priority = pi;
1707 pthread_attr_init(&attr);
1708 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1709 if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1710 res = -1;
1711 } else {
1712 res = 0;
1715 } else if (!ast_strlen_zero(app)) {
1716 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1717 } else {
1718 if (exten && context && pi)
1719 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1720 else {
1721 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1722 return 0;
1725 if (!res)
1726 astman_send_ack(s, m, "Originate successfully queued");
1727 else
1728 astman_send_error(s, m, "Originate failed");
1729 return 0;
1732 /*! \brief Help text for manager command mailboxstatus
1734 static char mandescr_mailboxstatus[] =
1735 "Description: Checks a voicemail account for status.\n"
1736 "Variables: (Names marked with * are required)\n"
1737 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1738 " ActionID: Optional ActionID for message matching.\n"
1739 "Returns number of messages.\n"
1740 " Message: Mailbox Status\n"
1741 " Mailbox: <mailboxid>\n"
1742 " Waiting: <count>\n"
1743 "\n";
1745 static int action_mailboxstatus(struct mansession *s, const struct message *m)
1747 const char *mailbox = astman_get_header(m, "Mailbox");
1748 const char *id = astman_get_header(m,"ActionID");
1749 char idText[256] = "";
1750 int ret;
1751 if (ast_strlen_zero(mailbox)) {
1752 astman_send_error(s, m, "Mailbox not specified");
1753 return 0;
1755 if (!ast_strlen_zero(id))
1756 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1757 ret = ast_app_has_voicemail(mailbox, NULL);
1758 astman_append(s, "Response: Success\r\n"
1759 "%s"
1760 "Message: Mailbox Status\r\n"
1761 "Mailbox: %s\r\n"
1762 "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1763 return 0;
1766 static char mandescr_mailboxcount[] =
1767 "Description: Checks a voicemail account for new messages.\n"
1768 "Variables: (Names marked with * are required)\n"
1769 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1770 " ActionID: Optional ActionID for message matching.\n"
1771 "Returns number of new and old messages.\n"
1772 " Message: Mailbox Message Count\n"
1773 " Mailbox: <mailboxid>\n"
1774 " NewMessages: <count>\n"
1775 " OldMessages: <count>\n"
1776 "\n";
1777 static int action_mailboxcount(struct mansession *s, const struct message *m)
1779 const char *mailbox = astman_get_header(m, "Mailbox");
1780 const char *id = astman_get_header(m,"ActionID");
1781 char idText[256] = "";
1782 int newmsgs = 0, oldmsgs = 0;
1783 if (ast_strlen_zero(mailbox)) {
1784 astman_send_error(s, m, "Mailbox not specified");
1785 return 0;
1787 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
1788 if (!ast_strlen_zero(id)) {
1789 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1791 astman_append(s, "Response: Success\r\n"
1792 "%s"
1793 "Message: Mailbox Message Count\r\n"
1794 "Mailbox: %s\r\n"
1795 "NewMessages: %d\r\n"
1796 "OldMessages: %d\r\n"
1797 "\r\n",
1798 idText,mailbox, newmsgs, oldmsgs);
1799 return 0;
1802 static char mandescr_extensionstate[] =
1803 "Description: Report the extension state for given extension.\n"
1804 " If the extension has a hint, will use devicestate to check\n"
1805 " the status of the device connected to the extension.\n"
1806 "Variables: (Names marked with * are required)\n"
1807 " *Exten: Extension to check state on\n"
1808 " *Context: Context for extension\n"
1809 " ActionId: Optional ID for this transaction\n"
1810 "Will return an \"Extension Status\" message.\n"
1811 "The response will include the hint for the extension and the status.\n";
1813 static int action_extensionstate(struct mansession *s, const struct message *m)
1815 const char *exten = astman_get_header(m, "Exten");
1816 const char *context = astman_get_header(m, "Context");
1817 const char *id = astman_get_header(m,"ActionID");
1818 char idText[256] = "";
1819 char hint[256] = "";
1820 int status;
1821 if (ast_strlen_zero(exten)) {
1822 astman_send_error(s, m, "Extension not specified");
1823 return 0;
1825 if (ast_strlen_zero(context))
1826 context = "default";
1827 status = ast_extension_state(NULL, context, exten);
1828 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1829 if (!ast_strlen_zero(id)) {
1830 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1832 astman_append(s, "Response: Success\r\n"
1833 "%s"
1834 "Message: Extension Status\r\n"
1835 "Exten: %s\r\n"
1836 "Context: %s\r\n"
1837 "Hint: %s\r\n"
1838 "Status: %d\r\n\r\n",
1839 idText,exten, context, hint, status);
1840 return 0;
1843 static char mandescr_timeout[] =
1844 "Description: Hangup a channel after a certain time.\n"
1845 "Variables: (Names marked with * are required)\n"
1846 " *Channel: Channel name to hangup\n"
1847 " *Timeout: Maximum duration of the call (sec)\n"
1848 "Acknowledges set time with 'Timeout Set' message\n";
1850 static int action_timeout(struct mansession *s, const struct message *m)
1852 struct ast_channel *c = NULL;
1853 const char *name = astman_get_header(m, "Channel");
1854 int timeout = atoi(astman_get_header(m, "Timeout"));
1855 if (ast_strlen_zero(name)) {
1856 astman_send_error(s, m, "No channel specified");
1857 return 0;
1859 if (!timeout) {
1860 astman_send_error(s, m, "No timeout specified");
1861 return 0;
1863 c = ast_get_channel_by_name_locked(name);
1864 if (!c) {
1865 astman_send_error(s, m, "No such channel");
1866 return 0;
1868 ast_channel_setwhentohangup(c, timeout);
1869 ast_channel_unlock(c);
1870 astman_send_ack(s, m, "Timeout Set");
1871 return 0;
1874 static int process_events(struct mansession *s)
1876 struct eventqent *eqe;
1877 int ret = 0;
1878 ast_mutex_lock(&s->__lock);
1879 if (s->fd > -1) {
1880 if (!s->eventq)
1881 s->eventq = master_eventq;
1882 while(s->eventq->next) {
1883 eqe = s->eventq->next;
1884 if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
1885 ((s->send_events & eqe->category) == eqe->category)) {
1886 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
1887 ret = -1;
1889 unuse_eventqent(s->eventq);
1890 s->eventq = eqe;
1893 ast_mutex_unlock(&s->__lock);
1894 return ret;
1897 static char mandescr_userevent[] =
1898 "Description: Send an event to manager sessions.\n"
1899 "Variables: (Names marked with * are required)\n"
1900 " *UserEvent: EventStringToSend\n"
1901 " Header1: Content1\n"
1902 " HeaderN: ContentN\n";
1904 static int action_userevent(struct mansession *s, const struct message *m)
1906 const char *event = astman_get_header(m, "UserEvent");
1907 char body[2048] = "";
1908 int x, bodylen = 0;
1909 for (x = 0; x < m->hdrcount; x++) {
1910 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
1911 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
1912 bodylen += strlen(m->headers[x]);
1913 ast_copy_string(body + bodylen, "\r\n", 3);
1914 bodylen += 2;
1918 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
1919 return 0;
1922 static int process_message(struct mansession *s, const struct message *m)
1924 char action[80] = "";
1925 struct manager_action *tmp = first_action;
1926 const char *id = astman_get_header(m,"ActionID");
1927 char idText[256] = "";
1928 int ret = 0;
1930 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1931 ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
1933 if (ast_strlen_zero(action)) {
1934 astman_send_error(s, m, "Missing action in request");
1935 return 0;
1937 if (!ast_strlen_zero(id)) {
1938 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1940 if (!s->authenticated) {
1941 if (!strcasecmp(action, "Challenge")) {
1942 const char *authtype = astman_get_header(m, "AuthType");
1944 if (!strcasecmp(authtype, "MD5")) {
1945 if (ast_strlen_zero(s->challenge))
1946 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1947 ast_mutex_lock(&s->__lock);
1948 astman_append(s, "Response: Success\r\n"
1949 "%s"
1950 "Challenge: %s\r\n\r\n",
1951 idText, s->challenge);
1952 ast_mutex_unlock(&s->__lock);
1953 return 0;
1954 } else {
1955 astman_send_error(s, m, "Must specify AuthType");
1956 return 0;
1958 } else if (!strcasecmp(action, "Login")) {
1959 if (authenticate(s, m)) {
1960 sleep(1);
1961 astman_send_error(s, m, "Authentication failed");
1962 return -1;
1963 } else {
1964 s->authenticated = 1;
1965 if (option_verbose > 1) {
1966 if (displayconnects) {
1967 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1970 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1971 astman_send_ack(s, m, "Authentication accepted");
1973 } else if (!strcasecmp(action, "Logoff")) {
1974 astman_send_ack(s, m, "See ya");
1975 return -1;
1976 } else
1977 astman_send_error(s, m, "Authentication Required");
1978 } else {
1979 while (tmp) {
1980 if (!strcasecmp(action, tmp->action)) {
1981 if ((s->writeperm & tmp->authority) == tmp->authority) {
1982 if (tmp->func(s, m))
1983 ret = -1;
1984 } else {
1985 astman_send_error(s, m, "Permission denied");
1987 break;
1989 tmp = tmp->next;
1991 if (!tmp)
1992 astman_send_error(s, m, "Invalid/unknown command");
1994 if (ret)
1995 return ret;
1996 return process_events(s);
1999 static int get_input(struct mansession *s, char *output)
2001 /* output must have at least sizeof(s->inbuf) space */
2002 int res;
2003 int x;
2004 struct pollfd fds[1];
2005 for (x = 1; x < s->inlen; x++) {
2006 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
2007 /* Copy output data up to and including \r\n */
2008 memcpy(output, s->inbuf, x + 1);
2009 /* Add trailing \0 */
2010 output[x+1] = '\0';
2011 /* Move remaining data back to the front */
2012 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
2013 s->inlen -= (x + 1);
2014 return 1;
2017 if (s->inlen >= sizeof(s->inbuf) - 1) {
2018 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
2019 s->inlen = 0;
2021 fds[0].fd = s->fd;
2022 fds[0].events = POLLIN;
2023 do {
2024 ast_mutex_lock(&s->__lock);
2025 s->waiting_thread = pthread_self();
2026 ast_mutex_unlock(&s->__lock);
2028 res = poll(fds, 1, -1);
2030 ast_mutex_lock(&s->__lock);
2031 s->waiting_thread = AST_PTHREADT_NULL;
2032 ast_mutex_unlock(&s->__lock);
2033 if (res < 0) {
2034 if (errno == EINTR) {
2035 return 0;
2037 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
2038 return -1;
2039 } else if (res > 0) {
2040 ast_mutex_lock(&s->__lock);
2041 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
2042 ast_mutex_unlock(&s->__lock);
2043 if (res < 1)
2044 return -1;
2045 break;
2047 } while(1);
2048 s->inlen += res;
2049 s->inbuf[s->inlen] = '\0';
2050 return 0;
2053 static int do_message(struct mansession *s)
2055 struct message m = { 0 };
2056 char header_buf[sizeof(s->inbuf)] = { '\0' };
2057 int res;
2059 for (;;) {
2060 res = get_input(s, header_buf);
2061 if (res > 0) {
2062 /* Strip trailing \r\n */
2063 if (strlen(header_buf) < 2)
2064 continue;
2065 header_buf[strlen(header_buf) - 2] = '\0';
2066 if (ast_strlen_zero(header_buf))
2067 return process_message(s, &m) ? -1 : 0;
2068 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2069 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2070 } else {
2071 return res;
2076 static void *session_do(void *data)
2078 struct mansession *s = data;
2079 int res;
2081 ast_mutex_lock(&s->__lock);
2082 astman_append(s, "Asterisk Call Manager/1.0\r\n");
2083 ast_mutex_unlock(&s->__lock);
2084 for (;;) {
2085 res = do_message(s);
2087 if (res == 0) {
2088 continue;
2089 } else if (res < 0) {
2090 break;
2091 } else if (s->eventq->next) {
2092 if (process_events(s))
2093 break;
2096 if (s->authenticated) {
2097 if (option_verbose > 1) {
2098 if (displayconnects)
2099 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2101 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2102 } else {
2103 if (option_verbose > 1) {
2104 if (displayconnects)
2105 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2107 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2109 destroy_session(s);
2110 return NULL;
2113 static void *accept_thread(void *ignore)
2115 int as;
2116 struct sockaddr_in sin;
2117 socklen_t sinlen;
2118 struct eventqent *eqe;
2119 struct mansession *s;
2120 struct protoent *p;
2121 int arg = 1;
2122 int flags;
2123 pthread_attr_t attr;
2124 time_t now;
2125 struct pollfd pfds[1];
2127 pthread_attr_init(&attr);
2128 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2130 for (;;) {
2131 time(&now);
2132 AST_LIST_LOCK(&sessions);
2133 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2134 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2135 AST_LIST_REMOVE_CURRENT(&sessions, list);
2136 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2137 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2138 s->username, ast_inet_ntoa(s->sin.sin_addr));
2140 free_session(s);
2141 break;
2144 AST_LIST_TRAVERSE_SAFE_END
2145 /* Purge master event queue of old, unused events, but make sure we
2146 always keep at least one in the queue */
2147 eqe = master_eventq;
2148 while (master_eventq->next && !master_eventq->usecount) {
2149 eqe = master_eventq;
2150 master_eventq = master_eventq->next;
2151 free(eqe);
2153 AST_LIST_UNLOCK(&sessions);
2154 if (s)
2155 ast_atomic_fetchadd_int(&num_sessions, -1);
2157 sinlen = sizeof(sin);
2158 pfds[0].fd = asock;
2159 pfds[0].events = POLLIN;
2160 /* Wait for something to happen, but timeout every few seconds so
2161 we can ditch any old manager sessions */
2162 if (poll(pfds, 1, 5000) < 1)
2163 continue;
2164 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2165 if (as < 0) {
2166 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2167 continue;
2169 p = getprotobyname("tcp");
2170 if (p) {
2171 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2172 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2175 if (!(s = ast_calloc(1, sizeof(*s))))
2176 continue;
2178 ast_atomic_fetchadd_int(&num_sessions, 1);
2180 memcpy(&s->sin, &sin, sizeof(sin));
2181 s->writetimeout = 100;
2182 s->waiting_thread = AST_PTHREADT_NULL;
2184 if (!block_sockets) {
2185 /* For safety, make sure socket is non-blocking */
2186 flags = fcntl(as, F_GETFL);
2187 fcntl(as, F_SETFL, flags | O_NONBLOCK);
2188 } else {
2189 flags = fcntl(as, F_GETFL);
2190 fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
2192 ast_mutex_init(&s->__lock);
2193 s->fd = as;
2194 s->send_events = -1;
2195 AST_LIST_LOCK(&sessions);
2196 AST_LIST_INSERT_HEAD(&sessions, s, list);
2197 /* Find the last place in the master event queue and hook ourselves
2198 in there */
2199 s->eventq = master_eventq;
2200 while(s->eventq->next)
2201 s->eventq = s->eventq->next;
2202 AST_LIST_UNLOCK(&sessions);
2203 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2204 if (ast_pthread_create_background(&s->t, &attr, session_do, s))
2205 destroy_session(s);
2207 pthread_attr_destroy(&attr);
2208 return NULL;
2211 static int append_event(const char *str, int category)
2213 struct eventqent *tmp, *prev = NULL;
2214 tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2216 if (!tmp)
2217 return -1;
2219 tmp->next = NULL;
2220 tmp->category = category;
2221 strcpy(tmp->eventdata, str);
2223 if (master_eventq) {
2224 prev = master_eventq;
2225 while (prev->next)
2226 prev = prev->next;
2227 prev->next = tmp;
2228 } else {
2229 master_eventq = tmp;
2232 tmp->usecount = num_sessions;
2234 return 0;
2237 /*! \brief manager_event: Send AMI event to client */
2238 int manager_event(int category, const char *event, const char *fmt, ...)
2240 struct mansession *s;
2241 char auth[80];
2242 va_list ap;
2243 struct timeval now;
2244 struct ast_dynamic_str *buf;
2246 /* Abort if there aren't any manager sessions */
2247 if (!num_sessions)
2248 return 0;
2250 if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2251 return -1;
2253 ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
2254 "Event: %s\r\nPrivilege: %s\r\n",
2255 event, authority_to_str(category, auth, sizeof(auth)));
2257 if (timestampevents) {
2258 now = ast_tvnow();
2259 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2260 "Timestamp: %ld.%06lu\r\n",
2261 now.tv_sec, (unsigned long) now.tv_usec);
2264 va_start(ap, fmt);
2265 ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
2266 va_end(ap);
2268 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
2270 /* Append event to master list and wake up any sleeping sessions */
2271 AST_LIST_LOCK(&sessions);
2272 append_event(buf->str, category);
2273 AST_LIST_TRAVERSE(&sessions, s, list) {
2274 ast_mutex_lock(&s->__lock);
2275 if (s->waiting_thread != AST_PTHREADT_NULL)
2276 pthread_kill(s->waiting_thread, SIGURG);
2277 ast_mutex_unlock(&s->__lock);
2279 AST_LIST_UNLOCK(&sessions);
2281 return 0;
2284 int ast_manager_unregister(char *action)
2286 struct manager_action *cur = first_action, *prev = first_action;
2288 ast_mutex_lock(&actionlock);
2289 while (cur) {
2290 if (!strcasecmp(action, cur->action)) {
2291 prev->next = cur->next;
2292 free(cur);
2293 if (option_verbose > 1)
2294 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2295 ast_mutex_unlock(&actionlock);
2296 return 0;
2298 prev = cur;
2299 cur = cur->next;
2301 ast_mutex_unlock(&actionlock);
2302 return 0;
2305 static int manager_state_cb(char *context, char *exten, int state, void *data)
2307 /* Notify managers of change */
2308 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2309 return 0;
2312 static int ast_manager_register_struct(struct manager_action *act)
2314 struct manager_action *cur = first_action, *prev = NULL;
2315 int ret;
2317 ast_mutex_lock(&actionlock);
2318 while (cur) { /* Walk the list of actions */
2319 ret = strcasecmp(cur->action, act->action);
2320 if (ret == 0) {
2321 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2322 ast_mutex_unlock(&actionlock);
2323 return -1;
2324 } else if (ret > 0) {
2325 /* Insert these alphabetically */
2326 if (prev) {
2327 act->next = prev->next;
2328 prev->next = act;
2329 } else {
2330 act->next = first_action;
2331 first_action = act;
2333 break;
2335 prev = cur;
2336 cur = cur->next;
2339 if (!cur) {
2340 if (prev)
2341 prev->next = act;
2342 else
2343 first_action = act;
2344 act->next = NULL;
2347 if (option_verbose > 1)
2348 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2349 ast_mutex_unlock(&actionlock);
2350 return 0;
2353 /*! \brief register a new command with manager, including online help. This is
2354 the preferred way to register a manager command */
2355 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2357 struct manager_action *cur;
2359 cur = ast_malloc(sizeof(*cur));
2360 if (!cur)
2361 return -1;
2363 cur->action = action;
2364 cur->authority = auth;
2365 cur->func = func;
2366 cur->synopsis = synopsis;
2367 cur->description = description;
2368 cur->next = NULL;
2370 ast_manager_register_struct(cur);
2372 return 0;
2374 /*! @}
2375 END Doxygen group */
2377 static struct mansession *find_session(unsigned long ident)
2379 struct mansession *s;
2381 AST_LIST_LOCK(&sessions);
2382 AST_LIST_TRAVERSE(&sessions, s, list) {
2383 ast_mutex_lock(&s->__lock);
2384 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2385 s->inuse++;
2386 break;
2388 ast_mutex_unlock(&s->__lock);
2390 AST_LIST_UNLOCK(&sessions);
2392 return s;
2396 enum {
2397 FORMAT_RAW,
2398 FORMAT_HTML,
2399 FORMAT_XML,
2401 static char *contenttype[] = { "plain", "html", "xml" };
2403 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2405 struct mansession *s = NULL;
2406 unsigned long ident = 0;
2407 char workspace[512];
2408 char cookie[128];
2409 size_t len = sizeof(workspace);
2410 int blastaway = 0;
2411 char *c = workspace;
2412 char *retval = NULL;
2413 struct ast_variable *v;
2415 for (v = params; v; v = v->next) {
2416 if (!strcasecmp(v->name, "mansession_id")) {
2417 sscanf(v->value, "%lx", &ident);
2418 break;
2422 if (!(s = find_session(ident))) {
2423 /* Create new session */
2424 if (!(s = ast_calloc(1, sizeof(*s)))) {
2425 *status = 500;
2426 goto generic_callback_out;
2428 memcpy(&s->sin, requestor, sizeof(s->sin));
2429 s->fd = -1;
2430 s->waiting_thread = AST_PTHREADT_NULL;
2431 s->send_events = 0;
2432 ast_mutex_init(&s->__lock);
2433 ast_mutex_lock(&s->__lock);
2434 s->inuse = 1;
2435 s->managerid = rand() | (unsigned long)s;
2436 AST_LIST_LOCK(&sessions);
2437 AST_LIST_INSERT_HEAD(&sessions, s, list);
2438 /* Hook into the last spot in the event queue */
2439 s->eventq = master_eventq;
2440 while (s->eventq->next)
2441 s->eventq = s->eventq->next;
2442 AST_LIST_UNLOCK(&sessions);
2443 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2444 ast_atomic_fetchadd_int(&num_sessions, 1);
2447 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2448 time(&s->sessiontimeout);
2449 if (!s->authenticated && (httptimeout > 5))
2450 s->sessiontimeout += 5;
2451 else
2452 s->sessiontimeout += httptimeout;
2453 ast_mutex_unlock(&s->__lock);
2455 if (s) {
2456 struct message m = { 0 };
2457 char tmp[80];
2458 unsigned int x;
2459 size_t hdrlen;
2461 for (x = 0; params && (x < AST_MAX_MANHEADERS); x++, params = params->next) {
2462 hdrlen = strlen(params->name) + strlen(params->value) + 3;
2463 m.headers[m.hdrcount] = alloca(hdrlen);
2464 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", params->name, params->value);
2465 m.hdrcount = x + 1;
2468 if (process_message(s, &m)) {
2469 if (s->authenticated) {
2470 if (option_verbose > 1) {
2471 if (displayconnects)
2472 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2474 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2475 } else {
2476 if (option_verbose > 1) {
2477 if (displayconnects)
2478 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2480 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2482 s->needdestroy = 1;
2484 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
2485 sprintf(tmp, "%08lx", s->managerid);
2486 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2487 if (format == FORMAT_HTML)
2488 ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Interface</title>");
2489 if (format == FORMAT_XML) {
2490 ast_build_string(&c, &len, "<ajax-response>\n");
2491 } else if (format == FORMAT_HTML) {
2492 ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2493 ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
2495 if (s->outputstr) {
2496 char *tmp;
2497 if (format == FORMAT_XML)
2498 tmp = xml_translate(s->outputstr->str, params);
2499 else if (format == FORMAT_HTML)
2500 tmp = html_translate(s->outputstr->str);
2501 else
2502 tmp = s->outputstr->str;
2503 if (tmp) {
2504 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2505 if (retval) {
2506 strcpy(retval, workspace);
2507 strcpy(retval + strlen(retval), tmp);
2508 c = retval + strlen(retval);
2509 len = 120;
2512 if (tmp != s->outputstr->str)
2513 free(tmp);
2514 free(s->outputstr);
2515 s->outputstr = NULL;
2517 /* Still okay because c would safely be pointing to workspace even
2518 if retval failed to allocate above */
2519 if (format == FORMAT_XML) {
2520 ast_build_string(&c, &len, "</ajax-response>\n");
2521 } else if (format == FORMAT_HTML)
2522 ast_build_string(&c, &len, "</table></body>\r\n");
2523 } else {
2524 *status = 500;
2525 *title = strdup("Server Error");
2527 ast_mutex_lock(&s->__lock);
2528 if (s->needdestroy) {
2529 if (s->inuse == 1) {
2530 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2531 blastaway = 1;
2532 } else {
2533 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2534 if (s->waiting_thread != AST_PTHREADT_NULL)
2535 pthread_kill(s->waiting_thread, SIGURG);
2536 s->inuse--;
2538 } else
2539 s->inuse--;
2540 ast_mutex_unlock(&s->__lock);
2542 if (blastaway)
2543 destroy_session(s);
2544 generic_callback_out:
2545 if (*status != 200)
2546 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2547 return retval;
2550 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2552 return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2555 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2557 return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2560 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2562 return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2565 struct ast_http_uri rawmanuri = {
2566 .description = "Raw HTTP Manager Event Interface",
2567 .uri = "rawman",
2568 .has_subtree = 0,
2569 .callback = rawman_http_callback,
2572 struct ast_http_uri manageruri = {
2573 .description = "HTML Manager Event Interface",
2574 .uri = "manager",
2575 .has_subtree = 0,
2576 .callback = manager_http_callback,
2579 struct ast_http_uri managerxmluri = {
2580 .description = "XML Manager Event Interface",
2581 .uri = "mxml",
2582 .has_subtree = 0,
2583 .callback = mxml_http_callback,
2586 static int registered = 0;
2587 static int webregged = 0;
2589 int init_manager(void)
2591 struct ast_config *cfg = NULL;
2592 const char *val;
2593 char *cat = NULL;
2594 int oldportno = portno;
2595 static struct sockaddr_in ba;
2596 int x = 1;
2597 int flags;
2598 int webenabled = 0;
2599 int newhttptimeout = 60;
2600 struct ast_manager_user *user = NULL;
2602 if (!registered) {
2603 /* Register default actions */
2604 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2605 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2606 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2607 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2608 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2609 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2610 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2611 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2612 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2613 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2614 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2615 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2616 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2617 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2618 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2619 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2620 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2621 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2622 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2624 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2625 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2626 registered = 1;
2627 /* Append placeholder event so master_eventq never runs dry */
2628 append_event("Event: Placeholder\r\n\r\n", 0);
2630 portno = DEFAULT_MANAGER_PORT;
2631 displayconnects = 1;
2632 cfg = ast_config_load("manager.conf");
2633 if (!cfg) {
2634 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
2635 return 0;
2637 val = ast_variable_retrieve(cfg, "general", "enabled");
2638 if (val)
2639 enabled = ast_true(val);
2641 val = ast_variable_retrieve(cfg, "general", "block-sockets");
2642 if (val)
2643 block_sockets = ast_true(val);
2645 val = ast_variable_retrieve(cfg, "general", "webenabled");
2646 if (val)
2647 webenabled = ast_true(val);
2649 if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2650 if (sscanf(val, "%d", &portno) != 1) {
2651 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2652 portno = DEFAULT_MANAGER_PORT;
2656 if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2657 displayconnects = ast_true(val);
2659 if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2660 timestampevents = ast_true(val);
2662 if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2663 newhttptimeout = atoi(val);
2665 memset(&ba, 0, sizeof(ba));
2666 ba.sin_family = AF_INET;
2667 ba.sin_port = htons(portno);
2669 if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2670 if (!inet_aton(val, &ba.sin_addr)) {
2671 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2672 memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2677 if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2678 #if 0
2679 /* Can't be done yet */
2680 close(asock);
2681 asock = -1;
2682 #else
2683 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2684 #endif
2687 AST_LIST_LOCK(&users);
2689 while ((cat = ast_category_browse(cfg, cat))) {
2690 struct ast_variable *var = NULL;
2692 if (!strcasecmp(cat, "general"))
2693 continue;
2695 /* Look for an existing entry, if none found - create one and add it to the list */
2696 if (!(user = ast_get_manager_by_name_locked(cat))) {
2697 if (!(user = ast_calloc(1, sizeof(*user))))
2698 break;
2699 /* Copy name over */
2700 ast_copy_string(user->username, cat, sizeof(user->username));
2701 /* Insert into list */
2702 AST_LIST_INSERT_TAIL(&users, user, list);
2705 /* Make sure we keep this user and don't destroy it during cleanup */
2706 user->keep = 1;
2708 var = ast_variable_browse(cfg, cat);
2709 while (var) {
2710 if (!strcasecmp(var->name, "secret")) {
2711 if (user->secret)
2712 free(user->secret);
2713 user->secret = ast_strdup(var->value);
2714 } else if (!strcasecmp(var->name, "deny") ) {
2715 if (user->deny)
2716 free(user->deny);
2717 user->deny = ast_strdup(var->value);
2718 } else if (!strcasecmp(var->name, "permit") ) {
2719 if (user->permit)
2720 free(user->permit);
2721 user->permit = ast_strdup(var->value);
2722 } else if (!strcasecmp(var->name, "read") ) {
2723 if (user->read)
2724 free(user->read);
2725 user->read = ast_strdup(var->value);
2726 } else if (!strcasecmp(var->name, "write") ) {
2727 if (user->write)
2728 free(user->write);
2729 user->write = ast_strdup(var->value);
2730 } else if (!strcasecmp(var->name, "displayconnects") )
2731 user->displayconnects = ast_true(var->value);
2732 else
2733 ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
2734 var = var->next;
2738 /* Perform cleanup - essentially prune out old users that no longer exist */
2739 AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
2740 if (user->keep) {
2741 user->keep = 0;
2742 continue;
2744 /* We do not need to keep this user so take them out of the list */
2745 AST_LIST_REMOVE_CURRENT(&users, list);
2746 /* Free their memory now */
2747 if (user->secret)
2748 free(user->secret);
2749 if (user->deny)
2750 free(user->deny);
2751 if (user->permit)
2752 free(user->permit);
2753 if (user->read)
2754 free(user->read);
2755 if (user->write)
2756 free(user->write);
2757 free(user);
2759 AST_LIST_TRAVERSE_SAFE_END
2761 AST_LIST_UNLOCK(&users);
2763 ast_config_destroy(cfg);
2765 if (webenabled && enabled) {
2766 if (!webregged) {
2767 ast_http_uri_link(&rawmanuri);
2768 ast_http_uri_link(&manageruri);
2769 ast_http_uri_link(&managerxmluri);
2770 webregged = 1;
2772 } else {
2773 if (webregged) {
2774 ast_http_uri_unlink(&rawmanuri);
2775 ast_http_uri_unlink(&manageruri);
2776 ast_http_uri_unlink(&managerxmluri);
2777 webregged = 0;
2781 if (newhttptimeout > 0)
2782 httptimeout = newhttptimeout;
2784 /* If not enabled, do nothing */
2785 if (!enabled)
2786 return 0;
2788 if (asock < 0) {
2789 asock = socket(AF_INET, SOCK_STREAM, 0);
2790 if (asock < 0) {
2791 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2792 return -1;
2794 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2795 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2796 ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2797 close(asock);
2798 asock = -1;
2799 return -1;
2801 if (listen(asock, 2)) {
2802 ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2803 close(asock);
2804 asock = -1;
2805 return -1;
2807 flags = fcntl(asock, F_GETFL);
2808 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2809 if (option_verbose)
2810 ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2811 ast_pthread_create_background(&t, NULL, accept_thread, NULL);
2813 return 0;
2816 int reload_manager(void)
2818 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2819 return init_manager();