put this value into the correct property
[asterisk-bristuff.git] / main / manager.c
blobc0fa09a470bab5b55b495e917e64b196dbf935b3
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_MANHEADER_LEN];
74 char data[AST_MAX_MANHEADER_LEN];
75 int timeout;
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];
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[AST_MAX_MANHEADER_LEN];
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 char *astman_get_header(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(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, struct message *m, char *error)
744 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, struct message *m, char *resp, char *msg)
754 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, 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 ast_strings_to_mask(char *string)
827 int x, ret = -1;
829 x = ast_is_number(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, char *eventmask)
858 int maskint = ast_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, struct message *m)
870 struct ast_config *cfg;
871 char *cat;
872 char *user = astman_get_header(m, "Username");
873 char *pass = astman_get_header(m, "Secret");
874 char *authtype = astman_get_header(m, "AuthType");
875 char *key = astman_get_header(m, "Key");
876 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, 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, struct message *m)
986 struct ast_config *cfg;
987 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 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, struct message *m, struct ast_config *cfg)
1023 int x;
1024 char hdr[40];
1025 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, 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, var, value, 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, var, 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, struct message *m)
1091 struct ast_config *cfg;
1092 char *sfn = astman_get_header(m, "SrcFilename");
1093 char *dfn = astman_get_header(m, "DstFilename");
1094 int res;
1095 char idText[256] = "";
1096 char *id = astman_get_header(m, "ActionID");
1097 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, struct message *m)
1136 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 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, struct message *m)
1223 struct manager_action *cur = first_action;
1224 char idText[256] = "";
1225 char temp[BUFSIZ];
1226 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, struct message *m)
1253 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, 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, struct message *m)
1282 struct ast_channel *c = NULL;
1283 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, struct message *m)
1308 struct ast_channel *c = NULL;
1309 char *name = astman_get_header(m, "Channel");
1310 char *varname = astman_get_header(m, "Variable");
1311 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, struct message *m)
1350 struct ast_channel *c = NULL;
1351 char *name = astman_get_header(m, "Channel");
1352 char *varname = astman_get_header(m, "Variable");
1353 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, 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, struct message *m)
1392 char *id = astman_get_header(m,"ActionID");
1393 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, struct message *m)
1493 char *name = astman_get_header(m, "Channel");
1494 char *name2 = astman_get_header(m, "ExtraChannel");
1495 char *exten = astman_get_header(m, "Exten");
1496 char *context = astman_get_header(m, "Context");
1497 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, struct message *m)
1553 char *cmd = astman_get_header(m, "Command");
1554 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, struct message *m)
1630 char *name = astman_get_header(m, "Channel");
1631 char *exten = astman_get_header(m, "Exten");
1632 char *context = astman_get_header(m, "Context");
1633 char *priority = astman_get_header(m, "Priority");
1634 char *timeout = astman_get_header(m, "Timeout");
1635 char *callerid = astman_get_header(m, "CallerID");
1636 char *account = astman_get_header(m, "Account");
1637 char *app = astman_get_header(m, "Application");
1638 char *appdata = astman_get_header(m, "Data");
1639 char *async = astman_get_header(m, "Async");
1640 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, struct message *m)
1747 char *mailbox = astman_get_header(m, "Mailbox");
1748 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, struct message *m)
1779 char *mailbox = astman_get_header(m, "Mailbox");
1780 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, struct message *m)
1815 char *exten = astman_get_header(m, "Exten");
1816 char *context = astman_get_header(m, "Context");
1817 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, struct message *m)
1852 struct ast_channel *c = NULL;
1853 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, struct message *m)
1906 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, struct message *m)
1924 char action[80] = "";
1925 struct manager_action *tmp = first_action;
1926 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 char *authtype;
1943 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 void *session_do(void *data)
2055 struct mansession *s = data;
2056 struct message m;
2057 int res;
2059 ast_mutex_lock(&s->__lock);
2060 astman_append(s, "Asterisk Call Manager/1.0\r\n");
2061 ast_mutex_unlock(&s->__lock);
2062 memset(&m, 0, sizeof(m));
2063 for (;;) {
2064 res = get_input(s, m.headers[m.hdrcount]);
2065 if (res > 0) {
2066 /* Strip trailing \r\n */
2067 if (strlen(m.headers[m.hdrcount]) < 2)
2068 continue;
2069 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
2070 if (ast_strlen_zero(m.headers[m.hdrcount])) {
2071 if (process_message(s, &m))
2072 break;
2073 memset(&m, 0, sizeof(m));
2074 } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
2075 m.hdrcount++;
2076 } else if (res < 0) {
2077 break;
2078 } else if (s->eventq->next) {
2079 if (process_events(s))
2080 break;
2083 if (s->authenticated) {
2084 if (option_verbose > 1) {
2085 if (displayconnects)
2086 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2088 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2089 } else {
2090 if (option_verbose > 1) {
2091 if (displayconnects)
2092 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2094 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2096 destroy_session(s);
2097 return NULL;
2100 static void *accept_thread(void *ignore)
2102 int as;
2103 struct sockaddr_in sin;
2104 socklen_t sinlen;
2105 struct eventqent *eqe;
2106 struct mansession *s;
2107 struct protoent *p;
2108 int arg = 1;
2109 int flags;
2110 pthread_attr_t attr;
2111 time_t now;
2112 struct pollfd pfds[1];
2114 pthread_attr_init(&attr);
2115 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2117 for (;;) {
2118 time(&now);
2119 AST_LIST_LOCK(&sessions);
2120 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2121 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2122 AST_LIST_REMOVE_CURRENT(&sessions, list);
2123 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2124 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2125 s->username, ast_inet_ntoa(s->sin.sin_addr));
2127 free_session(s);
2128 break;
2131 AST_LIST_TRAVERSE_SAFE_END
2132 /* Purge master event queue of old, unused events, but make sure we
2133 always keep at least one in the queue */
2134 eqe = master_eventq;
2135 while (master_eventq->next && !master_eventq->usecount) {
2136 eqe = master_eventq;
2137 master_eventq = master_eventq->next;
2138 free(eqe);
2140 AST_LIST_UNLOCK(&sessions);
2141 if (s)
2142 ast_atomic_fetchadd_int(&num_sessions, -1);
2144 sinlen = sizeof(sin);
2145 pfds[0].fd = asock;
2146 pfds[0].events = POLLIN;
2147 /* Wait for something to happen, but timeout every few seconds so
2148 we can ditch any old manager sessions */
2149 if (poll(pfds, 1, 5000) < 1)
2150 continue;
2151 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2152 if (as < 0) {
2153 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2154 continue;
2156 p = getprotobyname("tcp");
2157 if (p) {
2158 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2159 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2162 if (!(s = ast_calloc(1, sizeof(*s))))
2163 continue;
2165 ast_atomic_fetchadd_int(&num_sessions, 1);
2167 memcpy(&s->sin, &sin, sizeof(sin));
2168 s->writetimeout = 100;
2169 s->waiting_thread = AST_PTHREADT_NULL;
2171 if (!block_sockets) {
2172 /* For safety, make sure socket is non-blocking */
2173 flags = fcntl(as, F_GETFL);
2174 fcntl(as, F_SETFL, flags | O_NONBLOCK);
2175 } else {
2176 flags = fcntl(as, F_GETFL);
2177 fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
2179 ast_mutex_init(&s->__lock);
2180 s->fd = as;
2181 s->send_events = -1;
2182 AST_LIST_LOCK(&sessions);
2183 AST_LIST_INSERT_HEAD(&sessions, s, list);
2184 /* Find the last place in the master event queue and hook ourselves
2185 in there */
2186 s->eventq = master_eventq;
2187 while(s->eventq->next)
2188 s->eventq = s->eventq->next;
2189 AST_LIST_UNLOCK(&sessions);
2190 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2191 if (ast_pthread_create_background(&s->t, &attr, session_do, s))
2192 destroy_session(s);
2194 pthread_attr_destroy(&attr);
2195 return NULL;
2198 static int append_event(const char *str, int category)
2200 struct eventqent *tmp, *prev = NULL;
2201 tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2203 if (!tmp)
2204 return -1;
2206 tmp->next = NULL;
2207 tmp->category = category;
2208 strcpy(tmp->eventdata, str);
2210 if (master_eventq) {
2211 prev = master_eventq;
2212 while (prev->next)
2213 prev = prev->next;
2214 prev->next = tmp;
2215 } else {
2216 master_eventq = tmp;
2219 tmp->usecount = num_sessions;
2221 return 0;
2224 /*! \brief manager_event: Send AMI event to client */
2225 int manager_event(int category, const char *event, const char *fmt, ...)
2227 struct mansession *s;
2228 char auth[80];
2229 va_list ap;
2230 struct timeval now;
2231 struct ast_dynamic_str *buf;
2233 /* Abort if there aren't any manager sessions */
2234 if (!num_sessions)
2235 return 0;
2237 if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2238 return -1;
2240 ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
2241 "Event: %s\r\nPrivilege: %s\r\n",
2242 event, authority_to_str(category, auth, sizeof(auth)));
2244 if (timestampevents) {
2245 now = ast_tvnow();
2246 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2247 "Timestamp: %ld.%06lu\r\n",
2248 now.tv_sec, (unsigned long) now.tv_usec);
2251 va_start(ap, fmt);
2252 ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
2253 va_end(ap);
2255 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
2257 /* Append event to master list and wake up any sleeping sessions */
2258 AST_LIST_LOCK(&sessions);
2259 append_event(buf->str, category);
2260 AST_LIST_TRAVERSE(&sessions, s, list) {
2261 ast_mutex_lock(&s->__lock);
2262 if (s->waiting_thread != AST_PTHREADT_NULL)
2263 pthread_kill(s->waiting_thread, SIGURG);
2264 ast_mutex_unlock(&s->__lock);
2266 AST_LIST_UNLOCK(&sessions);
2268 return 0;
2271 int ast_manager_unregister(char *action)
2273 struct manager_action *cur = first_action, *prev = first_action;
2275 ast_mutex_lock(&actionlock);
2276 while (cur) {
2277 if (!strcasecmp(action, cur->action)) {
2278 prev->next = cur->next;
2279 free(cur);
2280 if (option_verbose > 1)
2281 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2282 ast_mutex_unlock(&actionlock);
2283 return 0;
2285 prev = cur;
2286 cur = cur->next;
2288 ast_mutex_unlock(&actionlock);
2289 return 0;
2292 static int manager_state_cb(char *context, char *exten, int state, void *data)
2294 /* Notify managers of change */
2295 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2296 return 0;
2299 static int ast_manager_register_struct(struct manager_action *act)
2301 struct manager_action *cur = first_action, *prev = NULL;
2302 int ret;
2304 ast_mutex_lock(&actionlock);
2305 while (cur) { /* Walk the list of actions */
2306 ret = strcasecmp(cur->action, act->action);
2307 if (ret == 0) {
2308 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2309 ast_mutex_unlock(&actionlock);
2310 return -1;
2311 } else if (ret > 0) {
2312 /* Insert these alphabetically */
2313 if (prev) {
2314 act->next = prev->next;
2315 prev->next = act;
2316 } else {
2317 act->next = first_action;
2318 first_action = act;
2320 break;
2322 prev = cur;
2323 cur = cur->next;
2326 if (!cur) {
2327 if (prev)
2328 prev->next = act;
2329 else
2330 first_action = act;
2331 act->next = NULL;
2334 if (option_verbose > 1)
2335 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2336 ast_mutex_unlock(&actionlock);
2337 return 0;
2340 /*! \brief register a new command with manager, including online help. This is
2341 the preferred way to register a manager command */
2342 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2344 struct manager_action *cur;
2346 cur = ast_malloc(sizeof(*cur));
2347 if (!cur)
2348 return -1;
2350 cur->action = action;
2351 cur->authority = auth;
2352 cur->func = func;
2353 cur->synopsis = synopsis;
2354 cur->description = description;
2355 cur->next = NULL;
2357 ast_manager_register_struct(cur);
2359 return 0;
2361 /*! @}
2362 END Doxygen group */
2364 static struct mansession *find_session(unsigned long ident)
2366 struct mansession *s;
2368 AST_LIST_LOCK(&sessions);
2369 AST_LIST_TRAVERSE(&sessions, s, list) {
2370 ast_mutex_lock(&s->__lock);
2371 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2372 s->inuse++;
2373 break;
2375 ast_mutex_unlock(&s->__lock);
2377 AST_LIST_UNLOCK(&sessions);
2379 return s;
2383 static void vars2msg(struct message *m, struct ast_variable *vars)
2385 int x;
2386 for (x = 0; vars && (x < AST_MAX_MANHEADERS); x++, vars = vars->next) {
2387 if (!vars)
2388 break;
2389 m->hdrcount = x + 1;
2390 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2394 enum {
2395 FORMAT_RAW,
2396 FORMAT_HTML,
2397 FORMAT_XML,
2399 static char *contenttype[] = { "plain", "html", "xml" };
2401 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2403 struct mansession *s = NULL;
2404 unsigned long ident = 0;
2405 char workspace[512];
2406 char cookie[128];
2407 size_t len = sizeof(workspace);
2408 int blastaway = 0;
2409 char *c = workspace;
2410 char *retval = NULL;
2411 struct message m;
2412 struct ast_variable *v;
2414 for (v = params; v; v = v->next) {
2415 if (!strcasecmp(v->name, "mansession_id")) {
2416 sscanf(v->value, "%lx", &ident);
2417 break;
2421 if (!(s = find_session(ident))) {
2422 /* Create new session */
2423 if (!(s = ast_calloc(1, sizeof(*s)))) {
2424 *status = 500;
2425 goto generic_callback_out;
2427 memcpy(&s->sin, requestor, sizeof(s->sin));
2428 s->fd = -1;
2429 s->waiting_thread = AST_PTHREADT_NULL;
2430 s->send_events = 0;
2431 ast_mutex_init(&s->__lock);
2432 ast_mutex_lock(&s->__lock);
2433 s->inuse = 1;
2434 s->managerid = rand() | (unsigned long)s;
2435 AST_LIST_LOCK(&sessions);
2436 AST_LIST_INSERT_HEAD(&sessions, s, list);
2437 /* Hook into the last spot in the event queue */
2438 s->eventq = master_eventq;
2439 while (s->eventq->next)
2440 s->eventq = s->eventq->next;
2441 AST_LIST_UNLOCK(&sessions);
2442 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2443 ast_atomic_fetchadd_int(&num_sessions, 1);
2446 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2447 time(&s->sessiontimeout);
2448 if (!s->authenticated && (httptimeout > 5))
2449 s->sessiontimeout += 5;
2450 else
2451 s->sessiontimeout += httptimeout;
2452 ast_mutex_unlock(&s->__lock);
2454 memset(&m, 0, sizeof(m));
2455 if (s) {
2456 char tmp[80];
2457 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
2458 sprintf(tmp, "%08lx", s->managerid);
2459 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2460 if (format == FORMAT_HTML)
2461 ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Test Interface</title>");
2462 vars2msg(&m, params);
2463 if (format == FORMAT_XML) {
2464 ast_build_string(&c, &len, "<ajax-response>\n");
2465 } else if (format == FORMAT_HTML) {
2466 ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2467 ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
2469 if (process_message(s, &m)) {
2470 if (s->authenticated) {
2471 if (option_verbose > 1) {
2472 if (displayconnects)
2473 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2475 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2476 } else {
2477 if (option_verbose > 1) {
2478 if (displayconnects)
2479 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2481 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2483 s->needdestroy = 1;
2485 if (s->outputstr) {
2486 char *tmp;
2487 if (format == FORMAT_XML)
2488 tmp = xml_translate(s->outputstr->str, params);
2489 else if (format == FORMAT_HTML)
2490 tmp = html_translate(s->outputstr->str);
2491 else
2492 tmp = s->outputstr->str;
2493 if (tmp) {
2494 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2495 if (retval) {
2496 strcpy(retval, workspace);
2497 strcpy(retval + strlen(retval), tmp);
2498 c = retval + strlen(retval);
2499 len = 120;
2502 if (tmp != s->outputstr->str)
2503 free(tmp);
2504 free(s->outputstr);
2505 s->outputstr = NULL;
2507 /* Still okay because c would safely be pointing to workspace even
2508 if retval failed to allocate above */
2509 if (format == FORMAT_XML) {
2510 ast_build_string(&c, &len, "</ajax-response>\n");
2511 } else if (format == FORMAT_HTML)
2512 ast_build_string(&c, &len, "</table></body>\r\n");
2513 } else {
2514 *status = 500;
2515 *title = strdup("Server Error");
2517 ast_mutex_lock(&s->__lock);
2518 if (s->needdestroy) {
2519 if (s->inuse == 1) {
2520 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2521 blastaway = 1;
2522 } else {
2523 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2524 if (s->waiting_thread != AST_PTHREADT_NULL)
2525 pthread_kill(s->waiting_thread, SIGURG);
2526 s->inuse--;
2528 } else
2529 s->inuse--;
2530 ast_mutex_unlock(&s->__lock);
2532 if (blastaway)
2533 destroy_session(s);
2534 generic_callback_out:
2535 if (*status != 200)
2536 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2537 return retval;
2540 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2542 return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2545 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2547 return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2550 static char *rawman_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_RAW, requestor, uri, params, status, title, contentlength);
2555 struct ast_http_uri rawmanuri = {
2556 .description = "Raw HTTP Manager Event Interface",
2557 .uri = "rawman",
2558 .has_subtree = 0,
2559 .callback = rawman_http_callback,
2562 struct ast_http_uri manageruri = {
2563 .description = "HTML Manager Event Interface",
2564 .uri = "manager",
2565 .has_subtree = 0,
2566 .callback = manager_http_callback,
2569 struct ast_http_uri managerxmluri = {
2570 .description = "XML Manager Event Interface",
2571 .uri = "mxml",
2572 .has_subtree = 0,
2573 .callback = mxml_http_callback,
2576 static int registered = 0;
2577 static int webregged = 0;
2579 int init_manager(void)
2581 struct ast_config *cfg = NULL;
2582 const char *val;
2583 char *cat = NULL;
2584 int oldportno = portno;
2585 static struct sockaddr_in ba;
2586 int x = 1;
2587 int flags;
2588 int webenabled = 0;
2589 int newhttptimeout = 60;
2590 struct ast_manager_user *user = NULL;
2592 if (!registered) {
2593 /* Register default actions */
2594 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2595 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2596 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2597 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2598 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2599 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2600 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2601 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2602 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2603 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2604 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2605 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2606 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2607 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2608 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2609 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2610 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2611 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2612 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2614 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2615 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2616 registered = 1;
2617 /* Append placeholder event so master_eventq never runs dry */
2618 append_event("Event: Placeholder\r\n\r\n", 0);
2620 portno = DEFAULT_MANAGER_PORT;
2621 displayconnects = 1;
2622 cfg = ast_config_load("manager.conf");
2623 if (!cfg) {
2624 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
2625 return 0;
2627 val = ast_variable_retrieve(cfg, "general", "enabled");
2628 if (val)
2629 enabled = ast_true(val);
2631 val = ast_variable_retrieve(cfg, "general", "block-sockets");
2632 if (val)
2633 block_sockets = ast_true(val);
2635 val = ast_variable_retrieve(cfg, "general", "webenabled");
2636 if (val)
2637 webenabled = ast_true(val);
2639 if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2640 if (sscanf(val, "%d", &portno) != 1) {
2641 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2642 portno = DEFAULT_MANAGER_PORT;
2646 if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2647 displayconnects = ast_true(val);
2649 if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2650 timestampevents = ast_true(val);
2652 if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2653 newhttptimeout = atoi(val);
2655 memset(&ba, 0, sizeof(ba));
2656 ba.sin_family = AF_INET;
2657 ba.sin_port = htons(portno);
2659 if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2660 if (!inet_aton(val, &ba.sin_addr)) {
2661 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2662 memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2667 if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2668 #if 0
2669 /* Can't be done yet */
2670 close(asock);
2671 asock = -1;
2672 #else
2673 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2674 #endif
2677 AST_LIST_LOCK(&users);
2679 while ((cat = ast_category_browse(cfg, cat))) {
2680 struct ast_variable *var = NULL;
2682 if (!strcasecmp(cat, "general"))
2683 continue;
2685 /* Look for an existing entry, if none found - create one and add it to the list */
2686 if (!(user = ast_get_manager_by_name_locked(cat))) {
2687 if (!(user = ast_calloc(1, sizeof(*user))))
2688 break;
2689 /* Copy name over */
2690 ast_copy_string(user->username, cat, sizeof(user->username));
2691 /* Insert into list */
2692 AST_LIST_INSERT_TAIL(&users, user, list);
2695 /* Make sure we keep this user and don't destroy it during cleanup */
2696 user->keep = 1;
2698 var = ast_variable_browse(cfg, cat);
2699 while (var) {
2700 if (!strcasecmp(var->name, "secret")) {
2701 if (user->secret)
2702 free(user->secret);
2703 user->secret = ast_strdup(var->value);
2704 } else if (!strcasecmp(var->name, "deny") ) {
2705 if (user->deny)
2706 free(user->deny);
2707 user->deny = ast_strdup(var->value);
2708 } else if (!strcasecmp(var->name, "permit") ) {
2709 if (user->permit)
2710 free(user->permit);
2711 user->permit = ast_strdup(var->value);
2712 } else if (!strcasecmp(var->name, "read") ) {
2713 if (user->read)
2714 free(user->read);
2715 user->read = ast_strdup(var->value);
2716 } else if (!strcasecmp(var->name, "write") ) {
2717 if (user->write)
2718 free(user->write);
2719 user->write = ast_strdup(var->value);
2720 } else if (!strcasecmp(var->name, "displayconnects") )
2721 user->displayconnects = ast_true(var->value);
2722 else
2723 ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
2724 var = var->next;
2728 /* Perform cleanup - essentially prune out old users that no longer exist */
2729 AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
2730 if (user->keep) {
2731 user->keep = 0;
2732 continue;
2734 /* We do not need to keep this user so take them out of the list */
2735 AST_LIST_REMOVE_CURRENT(&users, list);
2736 /* Free their memory now */
2737 if (user->secret)
2738 free(user->secret);
2739 if (user->deny)
2740 free(user->deny);
2741 if (user->permit)
2742 free(user->permit);
2743 if (user->read)
2744 free(user->read);
2745 if (user->write)
2746 free(user->write);
2747 free(user);
2749 AST_LIST_TRAVERSE_SAFE_END
2751 AST_LIST_UNLOCK(&users);
2753 ast_config_destroy(cfg);
2755 if (webenabled && enabled) {
2756 if (!webregged) {
2757 ast_http_uri_link(&rawmanuri);
2758 ast_http_uri_link(&manageruri);
2759 ast_http_uri_link(&managerxmluri);
2760 webregged = 1;
2762 } else {
2763 if (webregged) {
2764 ast_http_uri_unlink(&rawmanuri);
2765 ast_http_uri_unlink(&manageruri);
2766 ast_http_uri_unlink(&managerxmluri);
2767 webregged = 0;
2771 if (newhttptimeout > 0)
2772 httptimeout = newhttptimeout;
2774 /* If not enabled, do nothing */
2775 if (!enabled)
2776 return 0;
2778 if (asock < 0) {
2779 asock = socket(AF_INET, SOCK_STREAM, 0);
2780 if (asock < 0) {
2781 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2782 return -1;
2784 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2785 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2786 ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2787 close(asock);
2788 asock = -1;
2789 return -1;
2791 if (listen(asock, 2)) {
2792 ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2793 close(asock);
2794 asock = -1;
2795 return -1;
2797 flags = fcntl(asock, F_GETFL);
2798 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2799 if (option_verbose)
2800 ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2801 ast_pthread_create_background(&t, NULL, accept_thread, NULL);
2803 return 0;
2806 int reload_manager(void)
2808 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2809 return init_manager();