2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief The Asterisk Management Interface - AMI
23 * \author Mark Spencer <markster@digium.com>
25 * Channel Management and more
30 /*! \addtogroup Group_AMI AMI functions
37 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
44 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <netinet/tcp.h>
49 #include <arpa/inet.h>
54 #include "asterisk/channel.h"
55 #include "asterisk/file.h"
56 #include "asterisk/manager.h"
57 #include "asterisk/config.h"
58 #include "asterisk/callerid.h"
59 #include "asterisk/lock.h"
60 #include "asterisk/logger.h"
61 #include "asterisk/options.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/app.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/md5.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/http.h"
69 #include "asterisk/threadstorage.h"
70 #include "asterisk/linkedlists.h"
71 #include "asterisk/term.h"
72 #include "asterisk/astobj2.h"
74 struct fast_originate_helper
{
75 char tech
[AST_MAX_EXTENSION
];
76 char data
[AST_MAX_EXTENSION
];
78 char app
[AST_MAX_APP
];
79 char appdata
[AST_MAX_EXTENSION
];
80 char cid_name
[AST_MAX_EXTENSION
];
81 char cid_num
[AST_MAX_EXTENSION
];
82 char context
[AST_MAX_CONTEXT
];
83 char exten
[AST_MAX_EXTENSION
];
84 char idtext
[AST_MAX_EXTENSION
];
85 char account
[AST_MAX_ACCOUNT_CODE
];
87 struct ast_variable
*vars
;
93 struct eventqent
*next
;
98 static int portno
= DEFAULT_MANAGER_PORT
;
99 static int asock
= -1;
100 static int displayconnects
= 1;
101 static int timestampevents
;
102 static int httptimeout
= 60;
105 static int block_sockets
;
106 static int num_sessions
;
108 /* Protected by the sessions list lock */
109 struct eventqent
*master_eventq
= NULL
;
111 AST_THREADSTORAGE(manager_event_buf
, manager_event_buf_init
);
112 #define MANAGER_EVENT_BUF_INITSIZE 256
114 AST_THREADSTORAGE(astman_append_buf
, astman_append_buf_init
);
115 #define ASTMAN_APPEND_BUF_INITSIZE 256
117 static struct permalias
{
121 { EVENT_FLAG_SYSTEM
, "system" },
122 { EVENT_FLAG_CALL
, "call" },
123 { EVENT_FLAG_LOG
, "log" },
124 { EVENT_FLAG_VERBOSE
, "verbose" },
125 { EVENT_FLAG_COMMAND
, "command" },
126 { EVENT_FLAG_AGENT
, "agent" },
127 { EVENT_FLAG_USER
, "user" },
128 { EVENT_FLAG_CONFIG
, "config" },
133 #define MAX_BLACKLIST_CMD_LEN 2
135 char *words
[AST_MAX_CMD_LEN
];
136 } command_blacklist
[] = {
137 {{ "module", "load", NULL
}},
138 {{ "module", "unload", NULL
}},
142 /*! Execution thread */
144 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
146 /*! socket address */
147 struct sockaddr_in sin
;
150 /*! Whether an HTTP manager is in use */
152 /*! Whether an HTTP session should be destroyed */
154 /*! Whether an HTTP session has someone waiting on events */
155 pthread_t waiting_thread
;
156 /*! Unique manager identifer */
158 /*! Session timeout if HTTP */
159 time_t sessiontimeout
;
160 /*! Output from manager interface */
161 struct ast_dynamic_str
*outputstr
;
162 /*! Logged in username */
164 /*! Authentication challenge */
166 /*! Authentication status */
168 /*! Authorization for reading */
170 /*! Authorization for writing */
176 int displaysystemname
; /*!< Add system name to manager responses and events */
177 /* Queued events that we've not had the ability to send yet */
178 struct eventqent
*eventq
;
179 /* Timeout for ast_carefulwrite() */
181 int pending_event
; /*!< Pending events indicator in case when waiting_thread is NULL */
182 AST_LIST_ENTRY(mansession
) list
;
185 static AST_LIST_HEAD_STATIC(sessions
, mansession
);
187 struct ast_manager_user
{
194 unsigned int displayconnects
:1;
196 AST_LIST_ENTRY(ast_manager_user
) list
;
199 static AST_LIST_HEAD_STATIC(users
, ast_manager_user
);
201 static struct manager_action
*first_action
;
202 AST_RWLOCK_DEFINE_STATIC(actionlock
);
204 /*! \brief Convert authority code to string with serveral options */
205 static char *authority_to_str(int authority
, char *res
, int reslen
)
207 int running_total
= 0, i
;
209 memset(res
, 0, reslen
);
210 for (i
= 0; i
< (sizeof(perms
) / sizeof(perms
[0])) - 1; i
++) {
211 if (authority
& perms
[i
].num
) {
213 strncat(res
, ",", (reslen
> running_total
) ? reslen
- running_total
- 1 : 0);
216 strncat(res
, perms
[i
].label
, (reslen
> running_total
) ? reslen
- running_total
- 1 : 0);
217 running_total
+= strlen(perms
[i
].label
);
221 if (ast_strlen_zero(res
))
222 ast_copy_string(res
, "<none>", reslen
);
227 static char *complete_show_mancmd(const char *line
, const char *word
, int pos
, int state
)
229 struct manager_action
*cur
;
233 ast_rwlock_rdlock(&actionlock
);
234 for (cur
= first_action
; cur
; cur
= cur
->next
) { /* Walk the list of actions */
235 if (!strncasecmp(word
, cur
->action
, strlen(word
)) && ++which
> state
) {
236 ret
= ast_strdup(cur
->action
);
237 break; /* make sure we exit even if ast_strdup() returns NULL */
240 ast_rwlock_unlock(&actionlock
);
245 static void xml_copy_escape(char **dst
, size_t *maxlen
, const char *src
, int lower
)
247 while (*src
&& (*maxlen
> 6)) {
250 strcpy(*dst
, "<");
255 strcpy(*dst
, ">");
260 strcpy(*dst
, """);
265 strcpy(*dst
, "'");
270 strcpy(*dst
, "&");
275 *(*dst
)++ = lower
? tolower(*src
) : *src
;
282 struct variable_count
{
287 static int compress_char(char c
)
292 else if (c
>= 'a' && c
<= 'z')
300 static int variable_count_hash_fn(const void *vvc
, const int flags
)
302 const struct variable_count
*vc
= vvc
;
304 for (i
= 0; i
< 5; i
++) {
305 if (vc
->varname
[i
] == '\0')
307 res
+= compress_char(vc
->varname
[i
]) << (i
* 6);
312 static int variable_count_cmp_fn(void *obj
, void *vstr
, int flags
)
314 /* Due to the simplicity of struct variable_count, it makes no difference
315 * if you pass in objects or strings, the same operation applies. This is
316 * due to the fact that the hash occurs on the first element, which means
317 * the address of both the struct and the string are exactly the same. */
318 struct variable_count
*vc
= obj
;
320 return !strcmp(vc
->varname
, str
) ? CMP_MATCH
: 0;
323 static char *xml_translate(char *in
, struct ast_variable
*vars
)
325 struct ast_variable
*v
;
327 char *out
, *tmp
, *var
, *val
;
328 char *objtype
= NULL
;
336 struct variable_count
*vc
= NULL
;
337 struct ao2_container
*vco
= NULL
;
339 for (v
= vars
; v
; v
= v
->next
) {
340 if (!dest
&& !strcasecmp(v
->name
, "ajaxdest"))
342 else if (!objtype
&& !strcasecmp(v
->name
, "ajaxobjtype"))
349 for (x
= 0; in
[x
]; x
++) {
352 else if (in
[x
] == '\n')
354 else if (strchr("&\"<>\'", in
[x
]))
357 len
= (size_t) (strlen(in
) + colons
* 5 + breaks
* (40 + strlen(dest
) + strlen(objtype
)) + escaped
* 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&" */
358 out
= ast_malloc(len
);
364 while (*in
&& (*in
>= 32))
367 if ((count
> 3) && inobj
) {
368 ast_build_string(&tmp
, &len
, " /></response>\n");
371 /* Entity is closed, so close out the name cache */
376 while (*in
&& (*in
< 32)) {
381 val
= strchr(var
, ':');
388 vco
= ao2_container_alloc(37, variable_count_hash_fn
, variable_count_cmp_fn
);
389 ast_build_string(&tmp
, &len
, "<response type='object' id='%s'><%s", dest
, objtype
);
393 /* Check if the var has been used already */
394 if ((vc
= ao2_find(vco
, var
, 0)))
397 /* Create a new entry for this one */
398 vc
= ao2_alloc(sizeof(*vc
), NULL
);
404 ast_build_string(&tmp
, &len
, " ");
405 xml_copy_escape(&tmp
, &len
, var
, 1);
407 ast_build_string(&tmp
, &len
, "-%d", vc
->count
);
408 ast_build_string(&tmp
, &len
, "='");
409 xml_copy_escape(&tmp
, &len
, val
, 0);
410 ast_build_string(&tmp
, &len
, "'");
416 ast_build_string(&tmp
, &len
, " /></response>\n");
422 static char *html_translate(char *in
)
429 char *tmp
, *var
, *val
, *out
;
431 for (x
=0; in
[x
]; x
++) {
437 len
= strlen(in
) + colons
* 40 + breaks
* 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
438 out
= ast_malloc(len
);
444 while (*in
&& (*in
>= 32))
447 if ((count
% 4) == 0){
448 ast_build_string(&tmp
, &len
, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
451 while (*in
&& (*in
< 32)) {
456 val
= strchr(var
, ':');
462 ast_build_string(&tmp
, &len
, "<tr><td>%s</td><td>%s</td></tr>\r\n", var
, val
);
471 static struct ast_manager_user
*ast_get_manager_by_name_locked(const char *name
)
473 struct ast_manager_user
*user
= NULL
;
475 AST_LIST_TRAVERSE(&users
, user
, list
)
476 if (!strcasecmp(user
->username
, name
))
481 void astman_append(struct mansession
*s
, const char *fmt
, ...)
484 struct ast_dynamic_str
*buf
;
486 ast_mutex_lock(&s
->__lock
);
488 if (!(buf
= ast_dynamic_str_thread_get(&astman_append_buf
, ASTMAN_APPEND_BUF_INITSIZE
))) {
489 ast_mutex_unlock(&s
->__lock
);
494 ast_dynamic_str_thread_set_va(&buf
, 0, &astman_append_buf
, fmt
, ap
);
498 ast_carefulwrite(s
->fd
, buf
->str
, strlen(buf
->str
), s
->writetimeout
);
500 if (!s
->outputstr
&& !(s
->outputstr
= ast_calloc(1, sizeof(*s
->outputstr
)))) {
501 ast_mutex_unlock(&s
->__lock
);
505 ast_dynamic_str_append(&s
->outputstr
, 0, "%s", buf
->str
);
508 ast_mutex_unlock(&s
->__lock
);
511 static int handle_showmancmd(int fd
, int argc
, char *argv
[])
513 struct manager_action
*cur
;
518 return RESULT_SHOWUSAGE
;
520 ast_rwlock_rdlock(&actionlock
);
521 for (cur
= first_action
; cur
; cur
= cur
->next
) { /* Walk the list of actions */
522 for (num
= 3; num
< argc
; num
++) {
523 if (!strcasecmp(cur
->action
, argv
[num
])) {
524 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
: "");
528 ast_rwlock_unlock(&actionlock
);
530 return RESULT_SUCCESS
;
533 static int handle_showmanager(int fd
, int argc
, char *argv
[])
535 struct ast_manager_user
*user
= NULL
;
538 return RESULT_SHOWUSAGE
;
540 AST_LIST_LOCK(&users
);
542 if (!(user
= ast_get_manager_by_name_locked(argv
[3]))) {
543 ast_cli(fd
, "There is no manager called %s\n", argv
[3]);
544 AST_LIST_UNLOCK(&users
);
556 "displayconnects: %s\n",
557 (user
->username
? user
->username
: "(N/A)"),
558 (user
->secret
? "<Set>" : "(N/A)"),
559 (user
->deny
? user
->deny
: "(N/A)"),
560 (user
->permit
? user
->permit
: "(N/A)"),
561 (user
->read
? user
->read
: "(N/A)"),
562 (user
->write
? user
->write
: "(N/A)"),
563 (user
->displayconnects
? "yes" : "no"));
565 AST_LIST_UNLOCK(&users
);
567 return RESULT_SUCCESS
;
571 static int handle_showmanagers(int fd
, int argc
, char *argv
[])
573 struct ast_manager_user
*user
= NULL
;
577 return RESULT_SHOWUSAGE
;
579 AST_LIST_LOCK(&users
);
581 /* If there are no users, print out something along those lines */
582 if (AST_LIST_EMPTY(&users
)) {
583 ast_cli(fd
, "There are no manager users.\n");
584 AST_LIST_UNLOCK(&users
);
585 return RESULT_SUCCESS
;
588 ast_cli(fd
, "\nusername\n--------\n");
590 AST_LIST_TRAVERSE(&users
, user
, list
) {
591 ast_cli(fd
, "%s\n", user
->username
);
595 AST_LIST_UNLOCK(&users
);
597 ast_cli(fd
,"-------------------\n");
598 ast_cli(fd
,"%d manager users configured.\n", count_amu
);
600 return RESULT_SUCCESS
;
604 /*! \brief CLI command
605 Should change to "manager show commands" */
606 static int handle_showmancmds(int fd
, int argc
, char *argv
[])
608 struct manager_action
*cur
;
610 char *format
= " %-15.15s %-15.15s %-55.55s\n";
612 ast_cli(fd
, format
, "Action", "Privilege", "Synopsis");
613 ast_cli(fd
, format
, "------", "---------", "--------");
615 ast_rwlock_rdlock(&actionlock
);
616 for (cur
= first_action
; cur
; cur
= cur
->next
) /* Walk the list of actions */
617 ast_cli(fd
, format
, cur
->action
, authority_to_str(cur
->authority
, authority
, sizeof(authority
) -1), cur
->synopsis
);
618 ast_rwlock_unlock(&actionlock
);
620 return RESULT_SUCCESS
;
623 /*! \brief CLI command show manager connected */
624 /* Should change to "manager show connected" */
625 static int handle_showmanconn(int fd
, int argc
, char *argv
[])
627 struct mansession
*s
;
628 char *format
= " %-15.15s %-15.15s\n";
630 ast_cli(fd
, format
, "Username", "IP Address");
632 AST_LIST_LOCK(&sessions
);
633 AST_LIST_TRAVERSE(&sessions
, s
, list
)
634 ast_cli(fd
, format
,s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
635 AST_LIST_UNLOCK(&sessions
);
637 return RESULT_SUCCESS
;
640 /*! \brief CLI command show manager connected */
641 /* Should change to "manager show connected" */
642 static int handle_showmaneventq(int fd
, int argc
, char *argv
[])
646 AST_LIST_LOCK(&sessions
);
647 for (s
= master_eventq
; s
; s
= s
->next
) {
648 ast_cli(fd
, "Usecount: %d\n",s
->usecount
);
649 ast_cli(fd
, "Category: %d\n", s
->category
);
650 ast_cli(fd
, "Event:\n%s", s
->eventdata
);
652 AST_LIST_UNLOCK(&sessions
);
654 return RESULT_SUCCESS
;
657 static char showmancmd_help
[] =
658 "Usage: manager show command <actionname>\n"
659 " Shows the detailed description for a specific Asterisk manager interface command.\n";
661 static char showmancmds_help
[] =
662 "Usage: manager show commands\n"
663 " Prints a listing of all the available Asterisk manager interface commands.\n";
665 static char showmanconn_help
[] =
666 "Usage: manager show connected\n"
667 " Prints a listing of the users that are currently connected to the\n"
668 "Asterisk manager interface.\n";
670 static char showmaneventq_help
[] =
671 "Usage: manager show eventq\n"
672 " Prints a listing of all events pending in the Asterisk manger\n"
675 static char showmanagers_help
[] =
676 "Usage: manager show users\n"
677 " Prints a listing of all managers that are currently configured on that\n"
680 static char showmanager_help
[] =
681 " Usage: manager show user <user>\n"
682 " Display all information related to the manager user specified.\n";
684 static struct ast_cli_entry cli_show_manager_command_deprecated
= {
685 { "show", "manager", "command", NULL
},
686 handle_showmancmd
, NULL
,
687 NULL
, complete_show_mancmd
};
689 static struct ast_cli_entry cli_show_manager_commands_deprecated
= {
690 { "show", "manager", "commands", NULL
},
691 handle_showmancmds
, NULL
,
694 static struct ast_cli_entry cli_show_manager_connected_deprecated
= {
695 { "show", "manager", "connected", NULL
},
696 handle_showmanconn
, NULL
,
699 static struct ast_cli_entry cli_show_manager_eventq_deprecated
= {
700 { "show", "manager", "eventq", NULL
},
701 handle_showmaneventq
, NULL
,
704 static struct ast_cli_entry cli_manager
[] = {
705 { { "manager", "show", "command", NULL
},
706 handle_showmancmd
, "Show a manager interface command",
707 showmancmd_help
, complete_show_mancmd
, &cli_show_manager_command_deprecated
},
709 { { "manager", "show", "commands", NULL
},
710 handle_showmancmds
, "List manager interface commands",
711 showmancmds_help
, NULL
, &cli_show_manager_commands_deprecated
},
713 { { "manager", "show", "connected", NULL
},
714 handle_showmanconn
, "List connected manager interface users",
715 showmanconn_help
, NULL
, &cli_show_manager_connected_deprecated
},
717 { { "manager", "show", "eventq", NULL
},
718 handle_showmaneventq
, "List manager interface queued events",
719 showmaneventq_help
, NULL
, &cli_show_manager_eventq_deprecated
},
721 { { "manager", "show", "users", NULL
},
722 handle_showmanagers
, "List configured manager users",
723 showmanagers_help
, NULL
, NULL
},
725 { { "manager", "show", "user", NULL
},
726 handle_showmanager
, "Display information on a specific manager user",
727 showmanager_help
, NULL
, NULL
},
730 static void unuse_eventqent(struct eventqent
*e
)
732 if (ast_atomic_dec_and_test(&e
->usecount
) && e
->next
)
733 pthread_kill(t
, SIGURG
);
736 static void free_session(struct mansession
*s
)
738 struct eventqent
*eqe
;
743 ast_mutex_destroy(&s
->__lock
);
746 s
->eventq
= s
->eventq
->next
;
747 unuse_eventqent(eqe
);
752 static void destroy_session(struct mansession
*s
)
754 AST_LIST_LOCK(&sessions
);
755 AST_LIST_REMOVE(&sessions
, s
, list
);
758 AST_LIST_UNLOCK(&sessions
);
761 const char *astman_get_header(const struct message
*m
, char *var
)
766 snprintf(cmp
, sizeof(cmp
), "%s: ", var
);
768 for (x
= 0; x
< m
->hdrcount
; x
++) {
769 if (!strncasecmp(cmp
, m
->headers
[x
], strlen(cmp
)))
770 return m
->headers
[x
] + strlen(cmp
);
776 struct ast_variable
*astman_get_variables(const struct message
*m
)
779 struct ast_variable
*head
= NULL
, *cur
;
783 AST_DECLARE_APP_ARGS(args
,
784 AST_APP_ARG(vars
)[32];
787 varlen
= strlen("Variable: ");
789 for (x
= 0; x
< m
->hdrcount
; x
++) {
790 if (strncasecmp("Variable: ", m
->headers
[x
], varlen
))
793 parse
= ast_strdupa(m
->headers
[x
] + varlen
);
795 AST_STANDARD_APP_ARGS(args
, parse
);
797 for (y
= 0; y
< args
.argc
; y
++) {
800 var
= val
= ast_strdupa(args
.vars
[y
]);
802 if (!val
|| ast_strlen_zero(var
))
804 cur
= ast_variable_new(var
, val
);
818 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
819 hold the session lock _or_ be running in an action callback (in which case s->busy will
820 be non-zero). In either of these cases, there is no need to lock-protect the session's
821 fd, since no other output will be sent (events will be queued), and no input will
822 be read until either the current action finishes or get_input() obtains the session
825 void astman_send_error(struct mansession
*s
, const struct message
*m
, char *error
)
827 const char *id
= astman_get_header(m
,"ActionID");
829 astman_append(s
, "Response: Error\r\n");
830 if (!ast_strlen_zero(id
))
831 astman_append(s
, "ActionID: %s\r\n", id
);
832 astman_append(s
, "Message: %s\r\n\r\n", error
);
835 void astman_send_response(struct mansession
*s
, const struct message
*m
, char *resp
, char *msg
)
837 const char *id
= astman_get_header(m
,"ActionID");
839 astman_append(s
, "Response: %s\r\n", resp
);
840 if (!ast_strlen_zero(id
))
841 astman_append(s
, "ActionID: %s\r\n", id
);
843 astman_append(s
, "Message: %s\r\n\r\n", msg
);
845 astman_append(s
, "\r\n");
848 void astman_send_ack(struct mansession
*s
, const struct message
*m
, char *msg
)
850 astman_send_response(s
, m
, "Success", msg
);
853 /*! Tells you if smallstr exists inside bigstr
854 which is delim by delim and uses no buf or stringsep
855 ast_instring("this|that|more","this",',') == 1;
857 feel free to move this to app.c -anthm */
858 static int ast_instring(const char *bigstr
, const char *smallstr
, char delim
)
860 const char *val
= bigstr
, *next
;
863 if ((next
= strchr(val
, delim
))) {
864 if (!strncmp(val
, smallstr
, (next
- val
)))
869 return !strcmp(smallstr
, val
);
871 } while (*(val
= (next
+ 1)));
876 static int get_perm(const char *instr
)
883 for (x
= 0; x
< (sizeof(perms
) / sizeof(perms
[0])); x
++) {
884 if (ast_instring(instr
, perms
[x
].label
, ','))
891 static int ast_is_number(const char *string
)
898 for (x
= 0; x
< strlen(string
); x
++) {
899 if (!(string
[x
] >= 48 && string
[x
] <= 57)) {
905 return ret
? atoi(string
) : 0;
908 static int strings_to_mask(const char *string
)
912 x
= ast_is_number(string
);
916 else if (ast_strlen_zero(string
))
918 else if (ast_false(string
))
920 else if (ast_true(string
)) {
922 for (x
=0; x
<sizeof(perms
) / sizeof(perms
[0]); x
++)
926 for (x
=0; x
<sizeof(perms
) / sizeof(perms
[0]); x
++) {
927 if (ast_instring(string
, perms
[x
].label
, ','))
936 Rather than braindead on,off this now can also accept a specific int mask value
937 or a ',' delim list of mask strings (the same as manager.conf) -anthm
939 static int set_eventmask(struct mansession
*s
, const char *eventmask
)
941 int maskint
= strings_to_mask(eventmask
);
943 ast_mutex_lock(&s
->__lock
);
945 s
->send_events
= maskint
;
946 ast_mutex_unlock(&s
->__lock
);
951 static int authenticate(struct mansession
*s
, const struct message
*m
)
953 struct ast_config
*cfg
;
955 const char *user
= astman_get_header(m
, "Username");
956 const char *pass
= astman_get_header(m
, "Secret");
957 const char *authtype
= astman_get_header(m
, "AuthType");
958 const char *key
= astman_get_header(m
, "Key");
959 const char *events
= astman_get_header(m
, "Events");
961 cfg
= ast_config_load("manager.conf");
964 cat
= ast_category_browse(cfg
, NULL
);
966 if (strcasecmp(cat
, "general")) {
968 if (!strcasecmp(cat
, user
)) {
969 struct ast_variable
*v
;
970 struct ast_ha
*ha
= NULL
;
971 char *password
= NULL
;
973 for (v
= ast_variable_browse(cfg
, cat
); v
; v
= v
->next
) {
974 if (!strcasecmp(v
->name
, "secret")) {
976 } else if (!strcasecmp(v
->name
, "displaysystemname")) {
977 if (ast_true(v
->value
)) {
978 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME
)) {
979 s
->displaysystemname
= 1;
981 ast_log(LOG_ERROR
, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
984 } else if (!strcasecmp(v
->name
, "permit") ||
985 !strcasecmp(v
->name
, "deny")) {
986 ha
= ast_append_ha(v
->name
, v
->value
, ha
);
987 } else if (!strcasecmp(v
->name
, "writetimeout")) {
988 int val
= atoi(v
->value
);
991 ast_log(LOG_WARNING
, "Invalid writetimeout value '%s' at line %d\n", v
->value
, v
->lineno
);
993 s
->writetimeout
= val
;
997 if (ha
&& !ast_apply_ha(ha
, &(s
->sin
))) {
998 ast_log(LOG_NOTICE
, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1000 ast_config_destroy(cfg
);
1004 if (!strcasecmp(authtype
, "MD5")) {
1005 if (!ast_strlen_zero(key
) &&
1006 !ast_strlen_zero(s
->challenge
) && !ast_strlen_zero(password
)) {
1009 char md5key
[256] = "";
1010 struct MD5Context md5
;
1011 unsigned char digest
[16];
1013 MD5Update(&md5
, (unsigned char *) s
->challenge
, strlen(s
->challenge
));
1014 MD5Update(&md5
, (unsigned char *) password
, strlen(password
));
1015 MD5Final(digest
, &md5
);
1016 for (x
=0; x
<16; x
++)
1017 len
+= sprintf(md5key
+ len
, "%2.2x", digest
[x
]);
1018 if (!strcmp(md5key
, key
))
1021 ast_config_destroy(cfg
);
1025 ast_log(LOG_DEBUG
, "MD5 authentication is not possible. challenge: '%s'\n",
1026 S_OR(s
->challenge
, ""));
1027 ast_config_destroy(cfg
);
1030 } else if (password
&& !strcmp(password
, pass
)) {
1033 ast_log(LOG_NOTICE
, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1034 ast_config_destroy(cfg
);
1039 cat
= ast_category_browse(cfg
, cat
);
1042 ast_copy_string(s
->username
, cat
, sizeof(s
->username
));
1043 s
->readperm
= get_perm(ast_variable_retrieve(cfg
, cat
, "read"));
1044 s
->writeperm
= get_perm(ast_variable_retrieve(cfg
, cat
, "write"));
1045 ast_config_destroy(cfg
);
1047 set_eventmask(s
, events
);
1050 ast_config_destroy(cfg
);
1051 cfg
= ast_config_load("users.conf");
1054 cat
= ast_category_browse(cfg
, NULL
);
1056 struct ast_variable
*v
;
1057 const char *password
= NULL
;
1059 const char *readperms
= NULL
;
1060 const char *writeperms
= NULL
;
1062 if (strcasecmp(cat
, user
) || !strcasecmp(cat
, "general")) {
1063 cat
= ast_category_browse(cfg
, cat
);
1066 for (v
= ast_variable_browse(cfg
, cat
); v
; v
= v
->next
) {
1067 if (!strcasecmp(v
->name
, "secret"))
1068 password
= v
->value
;
1069 else if (!strcasecmp(v
->name
, "hasmanager"))
1070 hasmanager
= ast_true(v
->value
);
1071 else if (!strcasecmp(v
->name
, "managerread"))
1072 readperms
= v
->value
;
1073 else if (!strcasecmp(v
->name
, "managerwrite"))
1074 writeperms
= v
->value
;
1078 if (!password
|| strcmp(password
, pass
)) {
1079 ast_log(LOG_NOTICE
, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1080 ast_config_destroy(cfg
);
1083 ast_copy_string(s
->username
, cat
, sizeof(s
->username
));
1084 s
->readperm
= readperms
? get_perm(readperms
) : -1;
1085 s
->writeperm
= writeperms
? get_perm(writeperms
) : -1;
1086 ast_config_destroy(cfg
);
1088 set_eventmask(s
, events
);
1091 ast_log(LOG_NOTICE
, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1092 ast_config_destroy(cfg
);
1096 /*! \brief Manager PING */
1097 static char mandescr_ping
[] =
1098 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1099 " manager connection open.\n"
1100 "Variables: NONE\n";
1102 static int action_ping(struct mansession
*s
, const struct message
*m
)
1104 astman_send_response(s
, m
, "Pong", NULL
);
1108 static char mandescr_getconfig
[] =
1109 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1110 "file by category and contents.\n"
1112 " Filename: Configuration filename (e.g. foo.conf)\n";
1114 static int action_getconfig(struct mansession
*s
, const struct message
*m
)
1116 struct ast_config
*cfg
;
1117 const char *fn
= astman_get_header(m
, "Filename");
1120 char *category
=NULL
;
1121 struct ast_variable
*v
;
1122 char idText
[256] = "";
1123 const char *id
= astman_get_header(m
, "ActionID");
1125 if (!ast_strlen_zero(id
))
1126 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1128 if (ast_strlen_zero(fn
)) {
1129 astman_send_error(s
, m
, "Filename not specified");
1132 if (!(cfg
= ast_config_load_with_comments(fn
))) {
1133 astman_send_error(s
, m
, "Config file not found");
1136 astman_append(s
, "Response: Success\r\n%s", idText
);
1137 while ((category
= ast_category_browse(cfg
, category
))) {
1139 astman_append(s
, "Category-%06d: %s\r\n", catcount
, category
);
1140 for (v
= ast_variable_browse(cfg
, category
); v
; v
= v
->next
)
1141 astman_append(s
, "Line-%06d-%06d: %s=%s\r\n", catcount
, lineno
++, v
->name
, v
->value
);
1144 ast_config_destroy(cfg
);
1145 astman_append(s
, "\r\n");
1151 static void handle_updates(struct mansession
*s
, const struct message
*m
, struct ast_config
*cfg
)
1155 const char *action
, *cat
, *var
, *value
, *match
;
1156 struct ast_category
*category
;
1157 struct ast_variable
*v
;
1159 for (x
=0;x
<100000;x
++) {
1160 unsigned int object
= 0;
1162 snprintf(hdr
, sizeof(hdr
), "Action-%06d", x
);
1163 action
= astman_get_header(m
, hdr
);
1164 if (ast_strlen_zero(action
))
1166 snprintf(hdr
, sizeof(hdr
), "Cat-%06d", x
);
1167 cat
= astman_get_header(m
, hdr
);
1168 snprintf(hdr
, sizeof(hdr
), "Var-%06d", x
);
1169 var
= astman_get_header(m
, hdr
);
1170 snprintf(hdr
, sizeof(hdr
), "Value-%06d", x
);
1171 value
= astman_get_header(m
, hdr
);
1172 if (!ast_strlen_zero(value
) && *value
== '>') {
1176 snprintf(hdr
, sizeof(hdr
), "Match-%06d", x
);
1177 match
= astman_get_header(m
, hdr
);
1178 if (!strcasecmp(action
, "newcat")) {
1179 if (!ast_strlen_zero(cat
)) {
1180 category
= ast_category_new(cat
);
1182 ast_category_append(cfg
, category
);
1185 } else if (!strcasecmp(action
, "renamecat")) {
1186 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(value
)) {
1187 category
= ast_category_get(cfg
, cat
);
1189 ast_category_rename(category
, value
);
1191 } else if (!strcasecmp(action
, "delcat")) {
1192 if (!ast_strlen_zero(cat
))
1193 ast_category_delete(cfg
, (char *) cat
);
1194 } else if (!strcasecmp(action
, "update")) {
1195 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) && (category
= ast_category_get(cfg
, cat
)))
1196 ast_variable_update(category
, var
, value
, match
, object
);
1197 } else if (!strcasecmp(action
, "delete")) {
1198 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) && (category
= ast_category_get(cfg
, cat
)))
1199 ast_variable_delete(category
, (char *) var
, (char *) match
);
1200 } else if (!strcasecmp(action
, "append")) {
1201 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) &&
1202 (category
= ast_category_get(cfg
, cat
)) &&
1203 (v
= ast_variable_new(var
, value
))){
1204 if (object
|| (match
&& !strcasecmp(match
, "object")))
1206 ast_variable_append(category
, v
);
1212 static char mandescr_updateconfig
[] =
1213 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1214 "configuration elements in Asterisk configuration files.\n"
1215 "Variables (X's represent 6 digit number beginning with 000000):\n"
1216 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1217 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1218 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1219 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1220 " Cat-XXXXXX: Category to operate on\n"
1221 " Var-XXXXXX: Variable to work on\n"
1222 " Value-XXXXXX: Value to work on\n"
1223 " Match-XXXXXX: Extra match required to match line\n";
1225 static int action_updateconfig(struct mansession
*s
, const struct message
*m
)
1227 struct ast_config
*cfg
;
1228 const char *sfn
= astman_get_header(m
, "SrcFilename");
1229 const char *dfn
= astman_get_header(m
, "DstFilename");
1231 char idText
[256] = "";
1232 const char *id
= astman_get_header(m
, "ActionID");
1233 const char *rld
= astman_get_header(m
, "Reload");
1235 if (!ast_strlen_zero(id
))
1236 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1238 if (ast_strlen_zero(sfn
) || ast_strlen_zero(dfn
)) {
1239 astman_send_error(s
, m
, "Filename not specified");
1242 if (!(cfg
= ast_config_load_with_comments(sfn
))) {
1243 astman_send_error(s
, m
, "Config file not found");
1246 handle_updates(s
, m
, cfg
);
1247 res
= config_text_file_save(dfn
, cfg
, "Manager");
1248 ast_config_destroy(cfg
);
1250 astman_send_error(s
, m
, "Save of config failed");
1253 astman_append(s
, "Response: Success\r\n%s\r\n", idText
);
1254 if (!ast_strlen_zero(rld
)) {
1257 ast_module_reload(rld
);
1262 /*! \brief Manager WAITEVENT */
1263 static char mandescr_waitevent
[] =
1264 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1265 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1266 "session, events will be generated and queued.\n"
1268 " Timeout: Maximum time to wait for events\n";
1270 static int action_waitevent(struct mansession
*s
, const struct message
*m
)
1272 const char *timeouts
= astman_get_header(m
, "Timeout");
1273 int timeout
= -1, max
;
1277 struct eventqent
*eqe
;
1278 const char *id
= astman_get_header(m
,"ActionID");
1279 char idText
[256] = "";
1281 if (!ast_strlen_zero(id
))
1282 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1284 if (!ast_strlen_zero(timeouts
)) {
1285 sscanf(timeouts
, "%i", &timeout
);
1288 ast_mutex_lock(&s
->__lock
);
1289 if (s
->waiting_thread
!= AST_PTHREADT_NULL
) {
1290 pthread_kill(s
->waiting_thread
, SIGURG
);
1292 if (s
->sessiontimeout
) {
1294 max
= s
->sessiontimeout
- now
- 10;
1297 if ((timeout
< 0) || (timeout
> max
))
1299 if (!s
->send_events
)
1300 s
->send_events
= -1;
1301 /* Once waitevent is called, always queue events from now on */
1303 ast_mutex_unlock(&s
->__lock
);
1304 s
->waiting_thread
= pthread_self();
1306 ast_log(LOG_DEBUG
, "Starting waiting for an event!\n");
1307 for (x
=0; ((x
< timeout
) || (timeout
< 0)); x
++) {
1308 ast_mutex_lock(&s
->__lock
);
1309 if (s
->eventq
&& s
->eventq
->next
)
1311 if (s
->waiting_thread
!= pthread_self())
1315 ast_mutex_unlock(&s
->__lock
);
1319 if (ast_wait_for_input(s
->fd
, 1000))
1326 ast_log(LOG_DEBUG
, "Finished waiting for an event!\n");
1327 ast_mutex_lock(&s
->__lock
);
1328 if (s
->waiting_thread
== pthread_self()) {
1329 astman_send_response(s
, m
, "Success", "Waiting for Event...");
1330 /* Only show events if we're the most recent waiter */
1331 while(s
->eventq
->next
) {
1332 eqe
= s
->eventq
->next
;
1333 if (((s
->readperm
& eqe
->category
) == eqe
->category
) &&
1334 ((s
->send_events
& eqe
->category
) == eqe
->category
)) {
1335 astman_append(s
, "%s", eqe
->eventdata
);
1337 unuse_eventqent(s
->eventq
);
1341 "Event: WaitEventComplete\r\n"
1344 s
->waiting_thread
= AST_PTHREADT_NULL
;
1346 ast_log(LOG_DEBUG
, "Abandoning event request!\n");
1348 ast_mutex_unlock(&s
->__lock
);
1352 static char mandescr_listcommands
[] =
1353 "Description: Returns the action name and synopsis for every\n"
1354 " action that is available to the user\n"
1355 "Variables: NONE\n";
1357 /*! \note The actionlock is read-locked by the caller of this function */
1358 static int action_listcommands(struct mansession
*s
, const struct message
*m
)
1360 struct manager_action
*cur
;
1361 char idText
[256] = "";
1363 const char *id
= astman_get_header(m
,"ActionID");
1365 if (!ast_strlen_zero(id
))
1366 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1367 astman_append(s
, "Response: Success\r\n%s", idText
);
1368 for (cur
= first_action
; cur
; cur
= cur
->next
) {
1369 if ((s
->writeperm
& cur
->authority
) == cur
->authority
)
1370 astman_append(s
, "%s: %s (Priv: %s)\r\n", cur
->action
, cur
->synopsis
, authority_to_str(cur
->authority
, temp
, sizeof(temp
)));
1372 astman_append(s
, "\r\n");
1377 static char mandescr_events
[] =
1378 "Description: Enable/Disable sending of events to this manager\n"
1381 " EventMask: 'on' if all events should be sent,\n"
1382 " 'off' if no events should be sent,\n"
1383 " 'system,call,log' to select which flags events should have to be sent.\n";
1385 static int action_events(struct mansession
*s
, const struct message
*m
)
1387 const char *mask
= astman_get_header(m
, "EventMask");
1390 res
= set_eventmask(s
, mask
);
1392 astman_send_response(s
, m
, "Events On", NULL
);
1394 astman_send_response(s
, m
, "Events Off", NULL
);
1399 static char mandescr_logoff
[] =
1400 "Description: Logoff this manager session\n"
1401 "Variables: NONE\n";
1403 static int action_logoff(struct mansession
*s
, const struct message
*m
)
1405 astman_send_response(s
, m
, "Goodbye", "Thanks for all the fish.");
1409 static char mandescr_hangup
[] =
1410 "Description: Hangup a channel\n"
1412 " Channel: The channel name to be hungup\n";
1414 static int action_hangup(struct mansession
*s
, const struct message
*m
)
1416 struct ast_channel
*c
= NULL
;
1417 const char *name
= astman_get_header(m
, "Channel");
1418 if (ast_strlen_zero(name
)) {
1419 astman_send_error(s
, m
, "No channel specified");
1422 c
= ast_get_channel_by_name_locked(name
);
1424 astman_send_error(s
, m
, "No such channel");
1427 ast_softhangup(c
, AST_SOFTHANGUP_EXPLICIT
);
1428 ast_channel_unlock(c
);
1429 astman_send_ack(s
, m
, "Channel Hungup");
1433 static char mandescr_setvar
[] =
1434 "Description: Set a global or local channel variable.\n"
1435 "Variables: (Names marked with * are required)\n"
1436 " Channel: Channel to set variable for\n"
1437 " *Variable: Variable name\n"
1440 static int action_setvar(struct mansession
*s
, const struct message
*m
)
1442 struct ast_channel
*c
= NULL
;
1443 const char *name
= astman_get_header(m
, "Channel");
1444 const char *varname
= astman_get_header(m
, "Variable");
1445 const char *varval
= astman_get_header(m
, "Value");
1447 if (ast_strlen_zero(varname
)) {
1448 astman_send_error(s
, m
, "No variable specified");
1452 if (!ast_strlen_zero(name
)) {
1453 c
= ast_get_channel_by_name_locked(name
);
1455 astman_send_error(s
, m
, "No such channel");
1460 pbx_builtin_setvar_helper(c
, varname
, S_OR(varval
, ""));
1463 ast_channel_unlock(c
);
1465 astman_send_ack(s
, m
, "Variable Set");
1470 static char mandescr_getvar
[] =
1471 "Description: Get the value of a global or local channel variable.\n"
1472 "Variables: (Names marked with * are required)\n"
1473 " Channel: Channel to read variable from\n"
1474 " *Variable: Variable name\n"
1475 " ActionID: Optional Action id for message matching.\n";
1477 static int action_getvar(struct mansession
*s
, const struct message
*m
)
1479 struct ast_channel
*c
= NULL
;
1480 const char *name
= astman_get_header(m
, "Channel");
1481 const char *varname
= astman_get_header(m
, "Variable");
1482 const char *id
= astman_get_header(m
,"ActionID");
1484 char workspace
[1024] = "";
1486 if (ast_strlen_zero(varname
)) {
1487 astman_send_error(s
, m
, "No variable specified");
1491 if (!ast_strlen_zero(name
)) {
1492 c
= ast_get_channel_by_name_locked(name
);
1494 astman_send_error(s
, m
, "No such channel");
1499 if (varname
[strlen(varname
) - 1] == ')') {
1500 char *copy
= ast_strdupa(varname
);
1502 ast_func_read(c
, copy
, workspace
, sizeof(workspace
));
1505 pbx_retrieve_variable(c
, varname
, &varval
, workspace
, sizeof(workspace
), NULL
);
1509 ast_channel_unlock(c
);
1510 astman_append(s
, "Response: Success\r\n"
1511 "Variable: %s\r\nValue: %s\r\n", varname
, varval
);
1512 if (!ast_strlen_zero(id
))
1513 astman_append(s
, "ActionID: %s\r\n",id
);
1514 astman_append(s
, "\r\n");
1520 /*! \brief Manager "status" command to show channels */
1521 /* Needs documentation... */
1522 static int action_status(struct mansession
*s
, const struct message
*m
)
1524 const char *id
= astman_get_header(m
,"ActionID");
1525 const char *name
= astman_get_header(m
,"Channel");
1526 char idText
[256] = "";
1527 struct ast_channel
*c
;
1529 struct timeval now
= ast_tvnow();
1530 long elapsed_seconds
= 0;
1531 int all
= ast_strlen_zero(name
); /* set if we want all channels */
1533 if (!ast_strlen_zero(id
))
1534 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1536 c
= ast_channel_walk_locked(NULL
);
1538 c
= ast_get_channel_by_name_locked(name
);
1540 astman_send_error(s
, m
, "No such channel");
1544 astman_send_ack(s
, m
, "Channel status will follow");
1545 /* if we look by name, we break after the first iteration */
1548 snprintf(bridge
, sizeof(bridge
), "Link: %s\r\n", c
->_bridge
->name
);
1553 elapsed_seconds
= now
.tv_sec
- c
->cdr
->start
.tv_sec
;
1557 "Privilege: Call\r\n"
1559 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1560 "CallerIDNum: %s\r\n"
1561 "CallerIDName: %s\r\n"
1573 S_OR(c
->cid
.cid_num
, "<unknown>"),
1574 S_OR(c
->cid
.cid_num
, "<unknown>"),
1575 S_OR(c
->cid
.cid_name
, "<unknown>"),
1577 ast_state2str(c
->_state
), c
->context
,
1578 c
->exten
, c
->priority
, (long)elapsed_seconds
, bridge
, c
->uniqueid
, idText
);
1582 "Privilege: Call\r\n"
1584 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1585 "CallerIDNum: %s\r\n"
1586 "CallerIDName: %s\r\n"
1594 S_OR(c
->cid
.cid_num
, "<unknown>"),
1595 S_OR(c
->cid
.cid_num
, "<unknown>"),
1596 S_OR(c
->cid
.cid_name
, "<unknown>"),
1598 ast_state2str(c
->_state
), bridge
, c
->uniqueid
, idText
);
1600 ast_channel_unlock(c
);
1603 c
= ast_channel_walk_locked(c
);
1606 "Event: StatusComplete\r\n"
1612 static char mandescr_redirect
[] =
1613 "Description: Redirect (transfer) a call.\n"
1614 "Variables: (Names marked with * are required)\n"
1615 " *Channel: Channel to redirect\n"
1616 " ExtraChannel: Second call leg to transfer (optional)\n"
1617 " *Exten: Extension to transfer to\n"
1618 " *Context: Context to transfer to\n"
1619 " *Priority: Priority to transfer to\n"
1620 " ActionID: Optional Action id for message matching.\n";
1622 /*! \brief action_redirect: The redirect manager command */
1623 static int action_redirect(struct mansession
*s
, const struct message
*m
)
1625 const char *name
= astman_get_header(m
, "Channel");
1626 const char *name2
= astman_get_header(m
, "ExtraChannel");
1627 const char *exten
= astman_get_header(m
, "Exten");
1628 const char *context
= astman_get_header(m
, "Context");
1629 const char *priority
= astman_get_header(m
, "Priority");
1630 struct ast_channel
*chan
, *chan2
= NULL
;
1634 if (ast_strlen_zero(name
)) {
1635 astman_send_error(s
, m
, "Channel not specified");
1638 if (!ast_strlen_zero(priority
) && (sscanf(priority
, "%d", &pi
) != 1)) {
1639 if ((pi
= ast_findlabel_extension(NULL
, context
, exten
, priority
, NULL
)) < 1) {
1640 astman_send_error(s
, m
, "Invalid priority");
1644 /* XXX watch out, possible deadlock!!! */
1645 chan
= ast_get_channel_by_name_locked(name
);
1648 snprintf(buf
, sizeof(buf
), "Channel does not exist: %s", name
);
1649 astman_send_error(s
, m
, buf
);
1652 if (ast_check_hangup(chan
)) {
1653 astman_send_error(s
, m
, "Redirect failed, channel not up.");
1654 ast_channel_unlock(chan
);
1657 if (!ast_strlen_zero(name2
))
1658 chan2
= ast_get_channel_by_name_locked(name2
);
1659 if (chan2
&& ast_check_hangup(chan2
)) {
1660 astman_send_error(s
, m
, "Redirect failed, extra channel not up.");
1661 ast_channel_unlock(chan
);
1662 ast_channel_unlock(chan2
);
1665 res
= ast_async_goto(chan
, context
, exten
, pi
);
1667 if (!ast_strlen_zero(name2
)) {
1669 res
= ast_async_goto(chan2
, context
, exten
, pi
);
1673 astman_send_ack(s
, m
, "Dual Redirect successful");
1675 astman_send_error(s
, m
, "Secondary redirect failed");
1677 astman_send_ack(s
, m
, "Redirect successful");
1679 astman_send_error(s
, m
, "Redirect failed");
1681 ast_channel_unlock(chan
);
1683 ast_channel_unlock(chan2
);
1687 static int check_blacklist(const char *cmd
)
1689 char *cmd_copy
, *cur_cmd
;
1690 char *cmd_words
[MAX_BLACKLIST_CMD_LEN
] = { NULL
, };
1693 cmd_copy
= ast_strdupa(cmd
);
1694 for (i
= 0; i
< MAX_BLACKLIST_CMD_LEN
&& (cur_cmd
= strsep(&cmd_copy
, " ")); i
++) {
1695 cur_cmd
= ast_strip(cur_cmd
);
1696 if (ast_strlen_zero(cur_cmd
)) {
1701 cmd_words
[i
] = cur_cmd
;
1704 for (i
= 0; i
< ARRAY_LEN(command_blacklist
); i
++) {
1707 for (j
= 0; command_blacklist
[i
].words
[j
]; j
++) {
1708 if (ast_strlen_zero(cmd_words
[j
]) || strcasecmp(cmd_words
[j
], command_blacklist
[i
].words
[j
])) {
1722 static char mandescr_command
[] =
1723 "Description: Run a CLI command.\n"
1724 "Variables: (Names marked with * are required)\n"
1725 " *Command: Asterisk CLI command to run\n"
1726 " ActionID: Optional Action id for message matching.\n";
1728 /*! \brief action_command: Manager command "command" - execute CLI command */
1729 static int action_command(struct mansession
*s
, const struct message
*m
)
1731 const char *cmd
= astman_get_header(m
, "Command");
1732 const char *id
= astman_get_header(m
, "ActionID");
1733 char *buf
, *final_buf
;
1734 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1735 int fd
= mkstemp(template);
1738 if (ast_strlen_zero(cmd
)) {
1739 astman_send_error(s
, m
, "No command provided");
1743 if (check_blacklist(cmd
)) {
1744 astman_send_error(s
, m
, "Command blacklisted");
1748 astman_append(s
, "Response: Follows\r\nPrivilege: Command\r\n");
1749 if (!ast_strlen_zero(id
))
1750 astman_append(s
, "ActionID: %s\r\n", id
);
1751 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1752 ast_cli_command(fd
, cmd
); /* XXX need to change this to use a FILE * */
1753 l
= lseek(fd
, 0, SEEK_END
); /* how many chars available */
1755 /* This has a potential to overflow the stack. Hence, use the heap. */
1756 buf
= ast_calloc(1, l
+ 1);
1757 final_buf
= ast_calloc(1, l
+ 1);
1759 lseek(fd
, 0, SEEK_SET
);
1763 term_strip(final_buf
, buf
, l
);
1764 final_buf
[l
] = '\0';
1766 astman_append(s
, "%s", S_OR(final_buf
, buf
));
1771 astman_append(s
, "--END COMMAND--\r\n\r\n");
1773 ast_free(final_buf
);
1777 static void *fast_originate(void *data
)
1779 struct fast_originate_helper
*in
= data
;
1782 struct ast_channel
*chan
= NULL
;
1783 char requested_channel
[AST_CHANNEL_NAME
];
1785 if (!ast_strlen_zero(in
->app
)) {
1786 res
= ast_pbx_outgoing_app(in
->tech
, AST_FORMAT_SLINEAR
, in
->data
, in
->timeout
, in
->app
, in
->appdata
, &reason
, 1,
1787 S_OR(in
->cid_num
, NULL
),
1788 S_OR(in
->cid_name
, NULL
),
1789 in
->vars
, in
->account
, &chan
);
1791 res
= ast_pbx_outgoing_exten(in
->tech
, AST_FORMAT_SLINEAR
, in
->data
, in
->timeout
, in
->context
, in
->exten
, in
->priority
, &reason
, 1,
1792 S_OR(in
->cid_num
, NULL
),
1793 S_OR(in
->cid_name
, NULL
),
1794 in
->vars
, in
->account
, &chan
);
1798 snprintf(requested_channel
, AST_CHANNEL_NAME
, "%s/%s", in
->tech
, in
->data
);
1799 /* Tell the manager what happened with the channel */
1800 manager_event(EVENT_FLAG_CALL
, "OriginateResponse",
1808 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1809 "CallerIDNum: %s\r\n"
1810 "CallerIDName: %s\r\n",
1811 in
->idtext
, res
? "Failure" : "Success", chan
? chan
->name
: requested_channel
, in
->context
, in
->exten
, reason
,
1812 chan
? chan
->uniqueid
: "<null>",
1813 S_OR(in
->cid_num
, "<unknown>"),
1814 S_OR(in
->cid_num
, "<unknown>"),
1815 S_OR(in
->cid_name
, "<unknown>")
1818 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1820 ast_channel_unlock(chan
);
1825 static char mandescr_originate
[] =
1826 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1827 " Application/Data\n"
1828 "Variables: (Names marked with * are required)\n"
1829 " *Channel: Channel name to call\n"
1830 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1831 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1832 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1833 " Application: Application to use\n"
1834 " Data: Data to use (requires 'Application')\n"
1835 " Timeout: How long to wait for call to be answered (in ms)\n"
1836 " CallerID: Caller ID to be set on the outgoing channel\n"
1837 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1838 " Account: Account code\n"
1839 " Async: Set to 'true' for fast origination\n";
1841 static int action_originate(struct mansession
*s
, const struct message
*m
)
1843 const char *name
= astman_get_header(m
, "Channel");
1844 const char *exten
= astman_get_header(m
, "Exten");
1845 const char *context
= astman_get_header(m
, "Context");
1846 const char *priority
= astman_get_header(m
, "Priority");
1847 const char *timeout
= astman_get_header(m
, "Timeout");
1848 const char *callerid
= astman_get_header(m
, "CallerID");
1849 const char *account
= astman_get_header(m
, "Account");
1850 const char *app
= astman_get_header(m
, "Application");
1851 const char *appdata
= astman_get_header(m
, "Data");
1852 const char *async
= astman_get_header(m
, "Async");
1853 const char *id
= astman_get_header(m
, "ActionID");
1854 struct ast_variable
*vars
= astman_get_variables(m
);
1856 char *l
= NULL
, *n
= NULL
;
1865 pthread_attr_t attr
;
1867 astman_send_error(s
, m
, "Channel not specified");
1870 if (!ast_strlen_zero(priority
) && (sscanf(priority
, "%d", &pi
) != 1)) {
1871 if ((pi
= ast_findlabel_extension(NULL
, context
, exten
, priority
, NULL
)) < 1) {
1872 astman_send_error(s
, m
, "Invalid priority");
1876 if (!ast_strlen_zero(timeout
) && (sscanf(timeout
, "%d", &to
) != 1)) {
1877 astman_send_error(s
, m
, "Invalid timeout");
1880 ast_copy_string(tmp
, name
, sizeof(tmp
));
1882 data
= strchr(tmp
, '/');
1884 astman_send_error(s
, m
, "Invalid channel");
1888 ast_copy_string(tmp2
, callerid
, sizeof(tmp2
));
1889 ast_callerid_parse(tmp2
, &n
, &l
);
1891 if (ast_strlen_zero(n
))
1895 ast_shrink_phone_number(l
);
1896 if (ast_strlen_zero(l
))
1899 if (ast_true(async
)) {
1900 struct fast_originate_helper
*fast
= ast_calloc(1, sizeof(*fast
));
1904 if (!ast_strlen_zero(id
))
1905 snprintf(fast
->idtext
, sizeof(fast
->idtext
), "ActionID: %s\r\n", id
);
1906 ast_copy_string(fast
->tech
, tech
, sizeof(fast
->tech
));
1907 ast_copy_string(fast
->data
, data
, sizeof(fast
->data
));
1908 ast_copy_string(fast
->app
, app
, sizeof(fast
->app
));
1909 ast_copy_string(fast
->appdata
, appdata
, sizeof(fast
->appdata
));
1911 ast_copy_string(fast
->cid_num
, l
, sizeof(fast
->cid_num
));
1913 ast_copy_string(fast
->cid_name
, n
, sizeof(fast
->cid_name
));
1915 ast_copy_string(fast
->context
, context
, sizeof(fast
->context
));
1916 ast_copy_string(fast
->exten
, exten
, sizeof(fast
->exten
));
1917 ast_copy_string(fast
->account
, account
, sizeof(fast
->account
));
1919 fast
->priority
= pi
;
1920 pthread_attr_init(&attr
);
1921 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1922 if (ast_pthread_create(&th
, &attr
, fast_originate
, fast
)) {
1928 pthread_attr_destroy(&attr
);
1930 } else if (!ast_strlen_zero(app
)) {
1931 res
= ast_pbx_outgoing_app(tech
, AST_FORMAT_SLINEAR
, data
, to
, app
, appdata
, &reason
, 1, l
, n
, vars
, account
, NULL
);
1933 if (exten
&& context
&& pi
)
1934 res
= ast_pbx_outgoing_exten(tech
, AST_FORMAT_SLINEAR
, data
, to
, context
, exten
, pi
, &reason
, 1, l
, n
, vars
, account
, NULL
);
1936 astman_send_error(s
, m
, "Originate with 'Exten' requires 'Context' and 'Priority'");
1941 astman_send_ack(s
, m
, "Originate successfully queued");
1943 astman_send_error(s
, m
, "Originate failed");
1947 /*! \brief Help text for manager command mailboxstatus
1949 static char mandescr_mailboxstatus
[] =
1950 "Description: Checks a voicemail account for status.\n"
1951 "Variables: (Names marked with * are required)\n"
1952 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1953 " ActionID: Optional ActionID for message matching.\n"
1954 "Returns number of messages.\n"
1955 " Message: Mailbox Status\n"
1956 " Mailbox: <mailboxid>\n"
1957 " Waiting: <count>\n"
1960 static int action_mailboxstatus(struct mansession
*s
, const struct message
*m
)
1962 const char *mailbox
= astman_get_header(m
, "Mailbox");
1963 const char *id
= astman_get_header(m
,"ActionID");
1964 char idText
[256] = "";
1966 if (ast_strlen_zero(mailbox
)) {
1967 astman_send_error(s
, m
, "Mailbox not specified");
1970 if (!ast_strlen_zero(id
))
1971 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1972 ret
= ast_app_has_voicemail(mailbox
, NULL
);
1973 astman_append(s
, "Response: Success\r\n"
1975 "Message: Mailbox Status\r\n"
1977 "Waiting: %d\r\n\r\n", idText
, mailbox
, ret
);
1981 static char mandescr_mailboxcount
[] =
1982 "Description: Checks a voicemail account for new messages.\n"
1983 "Variables: (Names marked with * are required)\n"
1984 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1985 " ActionID: Optional ActionID for message matching.\n"
1986 "Returns number of new and old messages.\n"
1987 " Message: Mailbox Message Count\n"
1988 " Mailbox: <mailboxid>\n"
1989 " NewMessages: <count>\n"
1990 " OldMessages: <count>\n"
1992 static int action_mailboxcount(struct mansession
*s
, const struct message
*m
)
1994 const char *mailbox
= astman_get_header(m
, "Mailbox");
1995 const char *id
= astman_get_header(m
,"ActionID");
1996 char idText
[256] = "";
1997 int newmsgs
= 0, oldmsgs
= 0;
1998 if (ast_strlen_zero(mailbox
)) {
1999 astman_send_error(s
, m
, "Mailbox not specified");
2002 ast_app_inboxcount(mailbox
, &newmsgs
, &oldmsgs
);
2003 if (!ast_strlen_zero(id
)) {
2004 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n",id
);
2006 astman_append(s
, "Response: Success\r\n"
2008 "Message: Mailbox Message Count\r\n"
2010 "NewMessages: %d\r\n"
2011 "OldMessages: %d\r\n"
2013 idText
,mailbox
, newmsgs
, oldmsgs
);
2017 static char mandescr_extensionstate
[] =
2018 "Description: Report the extension state for given extension.\n"
2019 " If the extension has a hint, will use devicestate to check\n"
2020 " the status of the device connected to the extension.\n"
2021 "Variables: (Names marked with * are required)\n"
2022 " *Exten: Extension to check state on\n"
2023 " *Context: Context for extension\n"
2024 " ActionId: Optional ID for this transaction\n"
2025 "Will return an \"Extension Status\" message.\n"
2026 "The response will include the hint for the extension and the status.\n";
2028 static int action_extensionstate(struct mansession
*s
, const struct message
*m
)
2030 const char *exten
= astman_get_header(m
, "Exten");
2031 const char *context
= astman_get_header(m
, "Context");
2032 const char *id
= astman_get_header(m
,"ActionID");
2033 char idText
[256] = "";
2034 char hint
[256] = "";
2036 if (ast_strlen_zero(exten
)) {
2037 astman_send_error(s
, m
, "Extension not specified");
2040 if (ast_strlen_zero(context
))
2041 context
= "default";
2042 status
= ast_extension_state(NULL
, context
, exten
);
2043 ast_get_hint(hint
, sizeof(hint
) - 1, NULL
, 0, NULL
, context
, exten
);
2044 if (!ast_strlen_zero(id
)) {
2045 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
2047 astman_append(s
, "Response: Success\r\n"
2049 "Message: Extension Status\r\n"
2053 "Status: %d\r\n\r\n",
2054 idText
,exten
, context
, hint
, status
);
2058 static char mandescr_timeout
[] =
2059 "Description: Hangup a channel after a certain time.\n"
2060 "Variables: (Names marked with * are required)\n"
2061 " *Channel: Channel name to hangup\n"
2062 " *Timeout: Maximum duration of the call (sec)\n"
2063 "Acknowledges set time with 'Timeout Set' message\n";
2065 static int action_timeout(struct mansession
*s
, const struct message
*m
)
2067 struct ast_channel
*c
= NULL
;
2068 const char *name
= astman_get_header(m
, "Channel");
2069 int timeout
= atoi(astman_get_header(m
, "Timeout"));
2070 if (ast_strlen_zero(name
)) {
2071 astman_send_error(s
, m
, "No channel specified");
2075 astman_send_error(s
, m
, "No timeout specified");
2078 c
= ast_get_channel_by_name_locked(name
);
2080 astman_send_error(s
, m
, "No such channel");
2083 ast_channel_setwhentohangup(c
, timeout
);
2084 ast_channel_unlock(c
);
2085 astman_send_ack(s
, m
, "Timeout Set");
2089 static int process_events(struct mansession
*s
)
2091 struct eventqent
*eqe
;
2093 ast_mutex_lock(&s
->__lock
);
2095 s
->eventq
= master_eventq
;
2096 while(s
->eventq
->next
) {
2097 eqe
= s
->eventq
->next
;
2098 if ((s
->authenticated
&& (s
->readperm
& eqe
->category
) == eqe
->category
) &&
2099 ((s
->send_events
& eqe
->category
) == eqe
->category
)) {
2101 if (!ret
&& ast_carefulwrite(s
->fd
, eqe
->eventdata
, strlen(eqe
->eventdata
), s
->writetimeout
) < 0)
2103 } else if (!s
->outputstr
&& !(s
->outputstr
= ast_calloc(1, sizeof(*s
->outputstr
))))
2106 ast_dynamic_str_append(&s
->outputstr
, 0, "%s", eqe
->eventdata
);
2108 unuse_eventqent(s
->eventq
);
2111 ast_mutex_unlock(&s
->__lock
);
2115 static char mandescr_userevent
[] =
2116 "Description: Send an event to manager sessions.\n"
2117 "Variables: (Names marked with * are required)\n"
2118 " *UserEvent: EventStringToSend\n"
2119 " Header1: Content1\n"
2120 " HeaderN: ContentN\n";
2122 static int action_userevent(struct mansession
*s
, const struct message
*m
)
2124 const char *event
= astman_get_header(m
, "UserEvent");
2125 char body
[2048] = "";
2127 for (x
= 0; x
< m
->hdrcount
; x
++) {
2128 if (strncasecmp("UserEvent:", m
->headers
[x
], strlen("UserEvent:"))) {
2129 ast_copy_string(body
+ bodylen
, m
->headers
[x
], sizeof(body
) - bodylen
- 3);
2130 bodylen
+= strlen(m
->headers
[x
]);
2131 ast_copy_string(body
+ bodylen
, "\r\n", 3);
2136 manager_event(EVENT_FLAG_USER
, "UserEvent", "UserEvent: %s\r\n%s", event
, body
);
2140 static int process_message(struct mansession
*s
, const struct message
*m
)
2142 char action
[80] = "";
2143 struct manager_action
*tmp
;
2144 const char *id
= astman_get_header(m
,"ActionID");
2145 char idText
[256] = "";
2148 ast_copy_string(action
, astman_get_header(m
, "Action"), sizeof(action
));
2150 ast_log( LOG_DEBUG
, "Manager received command '%s'\n", action
);
2152 if (ast_strlen_zero(action
)) {
2153 astman_send_error(s
, m
, "Missing action in request");
2156 if (!ast_strlen_zero(id
)) {
2157 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
2159 if (!s
->authenticated
) {
2160 if (!strcasecmp(action
, "Challenge")) {
2161 const char *authtype
= astman_get_header(m
, "AuthType");
2163 if (!strcasecmp(authtype
, "MD5")) {
2164 if (ast_strlen_zero(s
->challenge
))
2165 snprintf(s
->challenge
, sizeof(s
->challenge
), "%ld", ast_random());
2166 astman_append(s
, "Response: Success\r\n"
2168 "Challenge: %s\r\n\r\n",
2169 idText
, s
->challenge
);
2172 astman_send_error(s
, m
, "Must specify AuthType");
2175 } else if (!strcasecmp(action
, "Login")) {
2176 if (authenticate(s
, m
)) {
2178 astman_send_error(s
, m
, "Authentication failed");
2181 s
->authenticated
= 1;
2182 if (option_verbose
> 1) {
2183 if (displayconnects
) {
2184 ast_verbose(VERBOSE_PREFIX_2
"%sManager '%s' logged on from %s\n",
2185 (s
->sessiontimeout
? "HTTP " : ""), s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2188 ast_log(LOG_EVENT
, "%sManager '%s' logged on from %s\n",
2189 (s
->sessiontimeout
? "HTTP " : ""), s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2190 astman_send_ack(s
, m
, "Authentication accepted");
2192 } else if (!strcasecmp(action
, "Logoff")) {
2193 astman_send_ack(s
, m
, "See ya");
2196 astman_send_error(s
, m
, "Authentication Required");
2198 if (!strcasecmp(action
, "Login"))
2199 astman_send_ack(s
, m
, "Already logged in");
2201 ast_rwlock_rdlock(&actionlock
);
2202 for (tmp
= first_action
; tmp
; tmp
= tmp
->next
) {
2203 if (strcasecmp(action
, tmp
->action
))
2205 if ((s
->writeperm
& tmp
->authority
) == tmp
->authority
) {
2206 if (tmp
->func(s
, m
))
2209 astman_send_error(s
, m
, "Permission denied");
2212 ast_rwlock_unlock(&actionlock
);
2214 astman_send_error(s
, m
, "Invalid/unknown command");
2219 return process_events(s
);
2222 static int get_input(struct mansession
*s
, char *output
)
2224 /* output must have at least sizeof(s->inbuf) space */
2227 struct pollfd fds
[1];
2228 for (x
= 1; x
< s
->inlen
; x
++) {
2229 if ((s
->inbuf
[x
] == '\n') && (s
->inbuf
[x
-1] == '\r')) {
2230 /* Copy output data up to and including \r\n */
2231 memcpy(output
, s
->inbuf
, x
+ 1);
2232 /* Add trailing \0 */
2234 /* Move remaining data back to the front */
2235 memmove(s
->inbuf
, s
->inbuf
+ x
+ 1, s
->inlen
- x
);
2236 s
->inlen
-= (x
+ 1);
2240 if (s
->inlen
>= sizeof(s
->inbuf
) - 1) {
2241 ast_log(LOG_WARNING
, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s
->sin
.sin_addr
), s
->inbuf
);
2245 fds
[0].events
= POLLIN
;
2247 ast_mutex_lock(&s
->__lock
);
2248 if (s
->pending_event
) {
2249 s
->pending_event
= 0;
2250 ast_mutex_unlock(&s
->__lock
);
2253 s
->waiting_thread
= pthread_self();
2254 ast_mutex_unlock(&s
->__lock
);
2256 res
= poll(fds
, 1, -1);
2258 ast_mutex_lock(&s
->__lock
);
2259 s
->waiting_thread
= AST_PTHREADT_NULL
;
2260 ast_mutex_unlock(&s
->__lock
);
2262 if (errno
== EINTR
|| errno
== EAGAIN
) {
2265 ast_log(LOG_WARNING
, "Select returned error: %s\n", strerror(errno
));
2267 } else if (res
> 0) {
2268 ast_mutex_lock(&s
->__lock
);
2269 res
= read(s
->fd
, s
->inbuf
+ s
->inlen
, sizeof(s
->inbuf
) - 1 - s
->inlen
);
2270 ast_mutex_unlock(&s
->__lock
);
2277 s
->inbuf
[s
->inlen
] = '\0';
2281 static int do_message(struct mansession
*s
)
2283 struct message m
= { 0 };
2284 char header_buf
[sizeof(s
->inbuf
)] = { '\0' };
2288 /* Check if any events are pending and do them if needed */
2289 if (s
->eventq
->next
) {
2290 if (process_events(s
))
2293 res
= get_input(s
, header_buf
);
2296 } else if (res
> 0) {
2297 /* Strip trailing \r\n */
2298 if (strlen(header_buf
) < 2)
2300 header_buf
[strlen(header_buf
) - 2] = '\0';
2301 if (ast_strlen_zero(header_buf
))
2302 return process_message(s
, &m
) ? -1 : 0;
2303 else if (m
.hdrcount
< (AST_MAX_MANHEADERS
- 1))
2304 m
.headers
[m
.hdrcount
++] = ast_strdupa(header_buf
);
2311 static void *session_do(void *data
)
2313 struct mansession
*s
= data
;
2316 astman_append(s
, "Asterisk Call Manager/1.0\r\n");
2318 if ((res
= do_message(s
)) < 0)
2321 if (s
->authenticated
) {
2322 if (option_verbose
> 1) {
2323 if (displayconnects
)
2324 ast_verbose(VERBOSE_PREFIX_2
"Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2326 ast_log(LOG_EVENT
, "Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2328 if (option_verbose
> 1) {
2329 if (displayconnects
)
2330 ast_verbose(VERBOSE_PREFIX_2
"Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2332 ast_log(LOG_EVENT
, "Failed attempt from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2335 /* It is possible under certain circumstances for this session thread
2336 to complete its work and exit *before* the thread that created it
2337 has finished executing the ast_pthread_create_background() function.
2338 If this occurs, some versions of glibc appear to act in a buggy
2339 fashion and attempt to write data into memory that it thinks belongs
2340 to the thread but is in fact not owned by the thread (or may have
2341 been freed completely).
2343 Causing this thread to yield to other threads at least one time
2344 appears to work around this bug.
2352 static void *accept_thread(void *ignore
)
2355 struct sockaddr_in sin
;
2357 struct eventqent
*eqe
;
2358 struct mansession
*s
;
2362 pthread_attr_t attr
;
2364 struct pollfd pfds
[1];
2366 pthread_attr_init(&attr
);
2367 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
2371 AST_LIST_LOCK(&sessions
);
2372 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions
, s
, list
) {
2373 if (s
->sessiontimeout
&& (now
> s
->sessiontimeout
) && !s
->inuse
) {
2374 AST_LIST_REMOVE_CURRENT(&sessions
, list
);
2376 if (s
->authenticated
&& (option_verbose
> 1) && displayconnects
) {
2377 ast_verbose(VERBOSE_PREFIX_2
"HTTP Manager '%s' timed out from %s\n",
2378 s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2384 AST_LIST_TRAVERSE_SAFE_END
2385 /* Purge master event queue of old, unused events, but make sure we
2386 always keep at least one in the queue */
2387 eqe
= master_eventq
;
2388 while (master_eventq
->next
&& !master_eventq
->usecount
) {
2389 eqe
= master_eventq
;
2390 master_eventq
= master_eventq
->next
;
2393 AST_LIST_UNLOCK(&sessions
);
2395 sinlen
= sizeof(sin
);
2397 pfds
[0].events
= POLLIN
;
2398 /* Wait for something to happen, but timeout every few seconds so
2399 we can ditch any old manager sessions */
2400 if (poll(pfds
, 1, 5000) < 1)
2402 as
= accept(asock
, (struct sockaddr
*)&sin
, &sinlen
);
2404 ast_log(LOG_NOTICE
, "Accept returned -1: %s\n", strerror(errno
));
2407 p
= getprotobyname("tcp");
2409 if( setsockopt(as
, p
->p_proto
, TCP_NODELAY
, (char *)&arg
, sizeof(arg
) ) < 0 ) {
2410 ast_log(LOG_WARNING
, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno
));
2413 if (!(s
= ast_calloc(1, sizeof(*s
))))
2416 memcpy(&s
->sin
, &sin
, sizeof(sin
));
2417 s
->writetimeout
= 100;
2418 s
->waiting_thread
= AST_PTHREADT_NULL
;
2420 if (!block_sockets
) {
2421 /* For safety, make sure socket is non-blocking */
2422 flags
= fcntl(as
, F_GETFL
);
2423 fcntl(as
, F_SETFL
, flags
| O_NONBLOCK
);
2425 flags
= fcntl(as
, F_GETFL
);
2426 fcntl(as
, F_SETFL
, flags
& ~O_NONBLOCK
);
2428 ast_mutex_init(&s
->__lock
);
2430 s
->send_events
= -1;
2431 AST_LIST_LOCK(&sessions
);
2432 AST_LIST_INSERT_HEAD(&sessions
, s
, list
);
2434 /* Find the last place in the master event queue and hook ourselves
2436 s
->eventq
= master_eventq
;
2437 while(s
->eventq
->next
)
2438 s
->eventq
= s
->eventq
->next
;
2439 ast_atomic_fetchadd_int(&s
->eventq
->usecount
, 1);
2440 AST_LIST_UNLOCK(&sessions
);
2441 if (ast_pthread_create_background(&s
->t
, &attr
, session_do
, s
))
2444 pthread_attr_destroy(&attr
);
2448 static int append_event(const char *str
, int category
)
2450 struct eventqent
*tmp
, *prev
= NULL
;
2451 tmp
= ast_malloc(sizeof(*tmp
) + strlen(str
));
2457 tmp
->category
= category
;
2458 strcpy(tmp
->eventdata
, str
);
2460 if (master_eventq
) {
2461 prev
= master_eventq
;
2466 master_eventq
= tmp
;
2469 tmp
->usecount
= num_sessions
;
2474 /*! \brief manager_event: Send AMI event to client */
2475 int manager_event(int category
, const char *event
, const char *fmt
, ...)
2477 struct mansession
*s
;
2481 struct ast_dynamic_str
*buf
;
2483 /* Abort if there aren't any manager sessions */
2487 if (!(buf
= ast_dynamic_str_thread_get(&manager_event_buf
, MANAGER_EVENT_BUF_INITSIZE
)))
2490 ast_dynamic_str_thread_set(&buf
, 0, &manager_event_buf
,
2491 "Event: %s\r\nPrivilege: %s\r\n",
2492 event
, authority_to_str(category
, auth
, sizeof(auth
)));
2494 if (timestampevents
) {
2496 ast_dynamic_str_thread_append(&buf
, 0, &manager_event_buf
,
2497 "Timestamp: %ld.%06lu\r\n",
2498 now
.tv_sec
, (unsigned long) now
.tv_usec
);
2502 ast_dynamic_str_thread_append_va(&buf
, 0, &manager_event_buf
, fmt
, ap
);
2505 ast_dynamic_str_thread_append(&buf
, 0, &manager_event_buf
, "\r\n");
2507 /* Append event to master list and wake up any sleeping sessions */
2508 AST_LIST_LOCK(&sessions
);
2509 append_event(buf
->str
, category
);
2510 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2511 ast_mutex_lock(&s
->__lock
);
2512 if (s
->waiting_thread
!= AST_PTHREADT_NULL
)
2513 pthread_kill(s
->waiting_thread
, SIGURG
);
2515 /* We have an event to process, but the mansession is
2516 * not waiting for it. We still need to indicate that there
2517 * is an event waiting so that get_input processes the pending
2518 * event instead of polling.
2520 s
->pending_event
= 1;
2521 ast_mutex_unlock(&s
->__lock
);
2523 AST_LIST_UNLOCK(&sessions
);
2528 int ast_manager_unregister(char *action
)
2530 struct manager_action
*cur
, *prev
;
2532 ast_rwlock_wrlock(&actionlock
);
2533 cur
= prev
= first_action
;
2535 if (!strcasecmp(action
, cur
->action
)) {
2536 prev
->next
= cur
->next
;
2538 if (option_verbose
> 1)
2539 ast_verbose(VERBOSE_PREFIX_2
"Manager unregistered action %s\n", action
);
2540 ast_rwlock_unlock(&actionlock
);
2546 ast_rwlock_unlock(&actionlock
);
2550 static int manager_state_cb(char *context
, char *exten
, int state
, void *data
)
2552 /* Notify managers of change */
2553 manager_event(EVENT_FLAG_CALL
, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten
, context
, state
);
2557 static int ast_manager_register_struct(struct manager_action
*act
)
2559 struct manager_action
*cur
, *prev
= NULL
;
2562 ast_rwlock_wrlock(&actionlock
);
2564 while (cur
) { /* Walk the list of actions */
2565 ret
= strcasecmp(cur
->action
, act
->action
);
2567 ast_log(LOG_WARNING
, "Manager: Action '%s' already registered\n", act
->action
);
2568 ast_rwlock_unlock(&actionlock
);
2570 } else if (ret
> 0) {
2571 /* Insert these alphabetically */
2573 act
->next
= prev
->next
;
2576 act
->next
= first_action
;
2593 if (option_verbose
> 1)
2594 ast_verbose(VERBOSE_PREFIX_2
"Manager registered action %s\n", act
->action
);
2595 ast_rwlock_unlock(&actionlock
);
2599 /*! \brief register a new command with manager, including online help. This is
2600 the preferred way to register a manager command */
2601 int ast_manager_register2(const char *action
, int auth
, int (*func
)(struct mansession
*s
, const struct message
*m
), const char *synopsis
, const char *description
)
2603 struct manager_action
*cur
;
2605 cur
= ast_malloc(sizeof(*cur
));
2609 cur
->action
= action
;
2610 cur
->authority
= auth
;
2612 cur
->synopsis
= synopsis
;
2613 cur
->description
= description
;
2616 ast_manager_register_struct(cur
);
2621 END Doxygen group */
2623 static struct mansession
*find_session(uint32_t ident
)
2625 struct mansession
*s
;
2627 AST_LIST_LOCK(&sessions
);
2628 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2629 ast_mutex_lock(&s
->__lock
);
2630 if (s
->sessiontimeout
&& (s
->managerid
== ident
) && !s
->needdestroy
) {
2634 ast_mutex_unlock(&s
->__lock
);
2636 AST_LIST_UNLOCK(&sessions
);
2641 int astman_verify_session_readpermissions(uint32_t ident
, int perm
)
2644 struct mansession
*s
;
2646 AST_LIST_LOCK(&sessions
);
2647 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2648 ast_mutex_lock(&s
->__lock
);
2649 if ((s
->managerid
== ident
) && (s
->readperm
& perm
)) {
2651 ast_mutex_unlock(&s
->__lock
);
2654 ast_mutex_unlock(&s
->__lock
);
2656 AST_LIST_UNLOCK(&sessions
);
2660 int astman_verify_session_writepermissions(uint32_t ident
, int perm
)
2663 struct mansession
*s
;
2665 AST_LIST_LOCK(&sessions
);
2666 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2667 ast_mutex_lock(&s
->__lock
);
2668 if ((s
->managerid
== ident
) && (s
->writeperm
& perm
)) {
2670 ast_mutex_unlock(&s
->__lock
);
2673 ast_mutex_unlock(&s
->__lock
);
2675 AST_LIST_UNLOCK(&sessions
);
2684 static char *contenttype
[] = { "plain", "html", "xml" };
2686 static char *generic_http_callback(int format
, struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2688 struct mansession
*s
= NULL
;
2690 char workspace
[512];
2692 size_t len
= sizeof(workspace
);
2694 char *c
= workspace
;
2695 char *retval
= NULL
;
2696 struct ast_variable
*v
;
2698 for (v
= params
; v
; v
= v
->next
) {
2699 if (!strcasecmp(v
->name
, "mansession_id")) {
2700 sscanf(v
->value
, "%x", &ident
);
2705 if (!(s
= find_session(ident
))) {
2706 /* Create new session */
2707 if (!(s
= ast_calloc(1, sizeof(*s
)))) {
2709 goto generic_callback_out
;
2711 memcpy(&s
->sin
, requestor
, sizeof(s
->sin
));
2713 s
->waiting_thread
= AST_PTHREADT_NULL
;
2715 ast_mutex_init(&s
->__lock
);
2716 ast_mutex_lock(&s
->__lock
);
2718 /*!\note There is approximately a 1 in 1.8E19 chance that the following
2719 * calculation will produce 0, which is an invalid ID, but due to the
2720 * properties of the rand() function (and the constantcy of s), that
2721 * won't happen twice in a row.
2723 while ((s
->managerid
= rand() ^ (unsigned long) s
) == 0);
2724 AST_LIST_LOCK(&sessions
);
2725 AST_LIST_INSERT_HEAD(&sessions
, s
, list
);
2726 /* Hook into the last spot in the event queue */
2727 s
->eventq
= master_eventq
;
2728 while (s
->eventq
->next
)
2729 s
->eventq
= s
->eventq
->next
;
2730 ast_atomic_fetchadd_int(&s
->eventq
->usecount
, 1);
2731 ast_atomic_fetchadd_int(&num_sessions
, 1);
2732 AST_LIST_UNLOCK(&sessions
);
2735 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2736 time(&s
->sessiontimeout
);
2737 if (!s
->authenticated
&& (httptimeout
> 5))
2738 s
->sessiontimeout
+= 5;
2740 s
->sessiontimeout
+= httptimeout
;
2741 ast_mutex_unlock(&s
->__lock
);
2744 struct message m
= { 0 };
2749 for (x
= 0, v
= params
; v
&& (x
< AST_MAX_MANHEADERS
); x
++, v
= v
->next
) {
2750 hdrlen
= strlen(v
->name
) + strlen(v
->value
) + 3;
2751 m
.headers
[m
.hdrcount
] = alloca(hdrlen
);
2752 snprintf((char *) m
.headers
[m
.hdrcount
], hdrlen
, "%s: %s", v
->name
, v
->value
);
2756 if (process_message(s
, &m
)) {
2757 if (s
->authenticated
) {
2758 if (option_verbose
> 1) {
2759 if (displayconnects
)
2760 ast_verbose(VERBOSE_PREFIX_2
"HTTP Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2762 ast_log(LOG_EVENT
, "HTTP Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2764 if (option_verbose
> 1) {
2765 if (displayconnects
)
2766 ast_verbose(VERBOSE_PREFIX_2
"HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2768 ast_log(LOG_EVENT
, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2772 ast_build_string(&c
, &len
, "Content-type: text/%s\r\n", contenttype
[format
]);
2773 sprintf(tmp
, "%08x", s
->managerid
);
2774 ast_build_string(&c
, &len
, "%s\r\n", ast_http_setcookie("mansession_id", tmp
, httptimeout
, cookie
, sizeof(cookie
)));
2775 if (format
== FORMAT_HTML
)
2776 ast_build_string(&c
, &len
, "<title>Asterisk™ Manager Interface</title>");
2777 if (format
== FORMAT_XML
) {
2778 ast_build_string(&c
, &len
, "<ajax-response>\n");
2779 } else if (format
== FORMAT_HTML
) {
2780 ast_build_string(&c
, &len
, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2781 ast_build_string(&c
, &len
, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1> Manager Tester</h1></td></tr>\r\n");
2783 ast_mutex_lock(&s
->__lock
);
2786 if (format
== FORMAT_XML
)
2787 tmp
= xml_translate(s
->outputstr
->str
, params
);
2788 else if (format
== FORMAT_HTML
)
2789 tmp
= html_translate(s
->outputstr
->str
);
2791 tmp
= s
->outputstr
->str
;
2793 retval
= malloc(strlen(workspace
) + strlen(tmp
) + 128);
2795 strcpy(retval
, workspace
);
2796 strcpy(retval
+ strlen(retval
), tmp
);
2797 c
= retval
+ strlen(retval
);
2801 if (tmp
!= s
->outputstr
->str
)
2804 s
->outputstr
= NULL
;
2806 ast_mutex_unlock(&s
->__lock
);
2807 /* Still okay because c would safely be pointing to workspace even
2808 if retval failed to allocate above */
2809 if (format
== FORMAT_XML
) {
2810 ast_build_string(&c
, &len
, "</ajax-response>\n");
2811 } else if (format
== FORMAT_HTML
)
2812 ast_build_string(&c
, &len
, "</table></body>\r\n");
2815 *title
= strdup("Server Error");
2817 ast_mutex_lock(&s
->__lock
);
2818 if (s
->needdestroy
) {
2819 if (s
->inuse
== 1) {
2820 ast_log(LOG_DEBUG
, "Need destroy, doing it now!\n");
2823 ast_log(LOG_DEBUG
, "Need destroy, but can't do it yet!\n");
2824 if (s
->waiting_thread
!= AST_PTHREADT_NULL
)
2825 pthread_kill(s
->waiting_thread
, SIGURG
);
2830 ast_mutex_unlock(&s
->__lock
);
2834 generic_callback_out
:
2836 return ast_http_error(500, "Server Error", NULL
, "Internal Server Error (out of memory)\n");
2840 static char *manager_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2842 return generic_http_callback(FORMAT_HTML
, requestor
, uri
, params
, status
, title
, contentlength
);
2845 static char *mxml_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2847 return generic_http_callback(FORMAT_XML
, requestor
, uri
, params
, status
, title
, contentlength
);
2850 static char *rawman_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2852 return generic_http_callback(FORMAT_RAW
, requestor
, uri
, params
, status
, title
, contentlength
);
2855 struct ast_http_uri rawmanuri
= {
2856 .description
= "Raw HTTP Manager Event Interface",
2859 .callback
= rawman_http_callback
,
2862 struct ast_http_uri manageruri
= {
2863 .description
= "HTML Manager Event Interface",
2866 .callback
= manager_http_callback
,
2869 struct ast_http_uri managerxmluri
= {
2870 .description
= "XML Manager Event Interface",
2873 .callback
= mxml_http_callback
,
2876 static int registered
= 0;
2877 static int webregged
= 0;
2879 int init_manager(void)
2881 struct ast_config
*cfg
= NULL
, *ucfg
= NULL
;
2884 int oldportno
= portno
;
2885 static struct sockaddr_in ba
;
2889 int newhttptimeout
= 60;
2890 struct ast_manager_user
*user
= NULL
;
2893 /* Register default actions */
2894 ast_manager_register2("Ping", 0, action_ping
, "Keepalive command", mandescr_ping
);
2895 ast_manager_register2("Events", 0, action_events
, "Control Event Flow", mandescr_events
);
2896 ast_manager_register2("Logoff", 0, action_logoff
, "Logoff Manager", mandescr_logoff
);
2897 ast_manager_register2("Hangup", EVENT_FLAG_CALL
, action_hangup
, "Hangup Channel", mandescr_hangup
);
2898 ast_manager_register("Status", EVENT_FLAG_CALL
, action_status
, "Lists channel status" );
2899 ast_manager_register2("Setvar", EVENT_FLAG_CALL
, action_setvar
, "Set Channel Variable", mandescr_setvar
);
2900 ast_manager_register2("Getvar", EVENT_FLAG_CALL
, action_getvar
, "Gets a Channel Variable", mandescr_getvar
);
2901 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG
, action_getconfig
, "Retrieve configuration", mandescr_getconfig
);
2902 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG
, action_updateconfig
, "Update basic configuration", mandescr_updateconfig
);
2903 ast_manager_register2("Redirect", EVENT_FLAG_CALL
, action_redirect
, "Redirect (transfer) a call", mandescr_redirect
);
2904 ast_manager_register2("Originate", EVENT_FLAG_CALL
, action_originate
, "Originate Call", mandescr_originate
);
2905 ast_manager_register2("Command", EVENT_FLAG_COMMAND
, action_command
, "Execute Asterisk CLI Command", mandescr_command
);
2906 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL
, action_extensionstate
, "Check Extension Status", mandescr_extensionstate
);
2907 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL
, action_timeout
, "Set Absolute Timeout", mandescr_timeout
);
2908 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL
, action_mailboxstatus
, "Check Mailbox", mandescr_mailboxstatus
);
2909 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL
, action_mailboxcount
, "Check Mailbox Message Count", mandescr_mailboxcount
);
2910 ast_manager_register2("ListCommands", 0, action_listcommands
, "List available manager commands", mandescr_listcommands
);
2911 ast_manager_register2("UserEvent", EVENT_FLAG_USER
, action_userevent
, "Send an arbitrary event", mandescr_userevent
);
2912 ast_manager_register2("WaitEvent", 0, action_waitevent
, "Wait for an event to occur", mandescr_waitevent
);
2914 ast_cli_register_multiple(cli_manager
, sizeof(cli_manager
) / sizeof(struct ast_cli_entry
));
2915 ast_extension_state_add(NULL
, NULL
, manager_state_cb
, NULL
);
2917 /* Append placeholder event so master_eventq never runs dry */
2918 append_event("Event: Placeholder\r\n\r\n", 0);
2920 portno
= DEFAULT_MANAGER_PORT
;
2921 displayconnects
= 1;
2922 cfg
= ast_config_load("manager.conf");
2924 ast_log(LOG_NOTICE
, "Unable to open management configuration manager.conf. Call management disabled.\n");
2927 val
= ast_variable_retrieve(cfg
, "general", "enabled");
2929 enabled
= ast_true(val
);
2931 val
= ast_variable_retrieve(cfg
, "general", "block-sockets");
2933 block_sockets
= ast_true(val
);
2935 val
= ast_variable_retrieve(cfg
, "general", "webenabled");
2937 webenabled
= ast_true(val
);
2939 if ((val
= ast_variable_retrieve(cfg
, "general", "port"))) {
2940 if (sscanf(val
, "%d", &portno
) != 1) {
2941 ast_log(LOG_WARNING
, "Invalid port number '%s'\n", val
);
2942 portno
= DEFAULT_MANAGER_PORT
;
2946 if ((val
= ast_variable_retrieve(cfg
, "general", "displayconnects")))
2947 displayconnects
= ast_true(val
);
2949 if ((val
= ast_variable_retrieve(cfg
, "general", "timestampevents")))
2950 timestampevents
= ast_true(val
);
2952 if ((val
= ast_variable_retrieve(cfg
, "general", "httptimeout")))
2953 newhttptimeout
= atoi(val
);
2955 memset(&ba
, 0, sizeof(ba
));
2956 ba
.sin_family
= AF_INET
;
2957 ba
.sin_port
= htons(portno
);
2959 if ((val
= ast_variable_retrieve(cfg
, "general", "bindaddr"))) {
2960 if (!inet_aton(val
, &ba
.sin_addr
)) {
2961 ast_log(LOG_WARNING
, "Invalid address '%s' specified, using 0.0.0.0\n", val
);
2962 memset(&ba
.sin_addr
, 0, sizeof(ba
.sin_addr
));
2967 if ((asock
> -1) && ((portno
!= oldportno
) || !enabled
)) {
2969 /* Can't be done yet */
2973 ast_log(LOG_WARNING
, "Unable to change management port / enabled\n");
2977 AST_LIST_LOCK(&users
);
2979 if ((ucfg
= ast_config_load("users.conf"))) {
2980 while ((cat
= ast_category_browse(ucfg
, cat
))) {
2982 struct ast_variable
*var
= NULL
;
2984 if (!strcasecmp(cat
, "general")) {
2988 if (!(hasmanager
= ast_true(ast_variable_retrieve(ucfg
, cat
, "hasmanager")))) {
2992 /* Look for an existing entry, if none found - create one and add it to the list */
2993 if (!(user
= ast_get_manager_by_name_locked(cat
))) {
2994 if (!(user
= ast_calloc(1, sizeof(*user
)))) {
2997 /* Copy name over */
2998 ast_copy_string(user
->username
, cat
, sizeof(user
->username
));
2999 /* Insert into list */
3000 AST_LIST_INSERT_TAIL(&users
, user
, list
);
3003 /* Make sure we keep this user and don't destroy it during cleanup */
3006 for (var
= ast_variable_browse(ucfg
, cat
); var
; var
= var
->next
) {
3007 if (!strcasecmp(var
->name
, "secret")) {
3011 user
->secret
= ast_strdup(var
->value
);
3012 } else if (!strcasecmp(var
->name
, "deny") ) {
3016 user
->deny
= ast_strdup(var
->value
);
3017 } else if (!strcasecmp(var
->name
, "permit") ) {
3021 user
->permit
= ast_strdup(var
->value
);
3022 } else if (!strcasecmp(var
->name
, "read") ) {
3026 user
->read
= ast_strdup(var
->value
);
3027 } else if (!strcasecmp(var
->name
, "write") ) {
3031 user
->write
= ast_strdup(var
->value
);
3032 } else if (!strcasecmp(var
->name
, "displayconnects") ) {
3033 user
->displayconnects
= ast_true(var
->value
);
3034 } else if (!strcasecmp(var
->name
, "hasmanager")) {
3035 /* already handled */
3037 ast_log(LOG_DEBUG
, "%s is an unknown option (to the manager module).\n", var
->name
);
3041 ast_config_destroy(ucfg
);
3044 while ((cat
= ast_category_browse(cfg
, cat
))) {
3045 struct ast_variable
*var
= NULL
;
3047 if (!strcasecmp(cat
, "general"))
3050 /* Look for an existing entry, if none found - create one and add it to the list */
3051 if (!(user
= ast_get_manager_by_name_locked(cat
))) {
3052 if (!(user
= ast_calloc(1, sizeof(*user
))))
3054 /* Copy name over */
3055 ast_copy_string(user
->username
, cat
, sizeof(user
->username
));
3056 /* Insert into list */
3057 AST_LIST_INSERT_TAIL(&users
, user
, list
);
3060 /* Make sure we keep this user and don't destroy it during cleanup */
3063 var
= ast_variable_browse(cfg
, cat
);
3065 if (!strcasecmp(var
->name
, "secret")) {
3068 user
->secret
= ast_strdup(var
->value
);
3069 } else if (!strcasecmp(var
->name
, "deny") ) {
3072 user
->deny
= ast_strdup(var
->value
);
3073 } else if (!strcasecmp(var
->name
, "permit") ) {
3076 user
->permit
= ast_strdup(var
->value
);
3077 } else if (!strcasecmp(var
->name
, "read") ) {
3080 user
->read
= ast_strdup(var
->value
);
3081 } else if (!strcasecmp(var
->name
, "write") ) {
3084 user
->write
= ast_strdup(var
->value
);
3085 } else if (!strcasecmp(var
->name
, "displayconnects") )
3086 user
->displayconnects
= ast_true(var
->value
);
3088 ast_log(LOG_DEBUG
, "%s is an unknown option.\n", var
->name
);
3093 /* Perform cleanup - essentially prune out old users that no longer exist */
3094 AST_LIST_TRAVERSE_SAFE_BEGIN(&users
, user
, list
) {
3099 /* We do not need to keep this user so take them out of the list */
3100 AST_LIST_REMOVE_CURRENT(&users
, list
);
3101 /* Free their memory now */
3114 AST_LIST_TRAVERSE_SAFE_END
3116 AST_LIST_UNLOCK(&users
);
3118 ast_config_destroy(cfg
);
3120 if (webenabled
&& enabled
) {
3122 ast_http_uri_link(&rawmanuri
);
3123 ast_http_uri_link(&manageruri
);
3124 ast_http_uri_link(&managerxmluri
);
3129 ast_http_uri_unlink(&rawmanuri
);
3130 ast_http_uri_unlink(&manageruri
);
3131 ast_http_uri_unlink(&managerxmluri
);
3136 if (newhttptimeout
> 0)
3137 httptimeout
= newhttptimeout
;
3139 /* If not enabled, do nothing */
3144 asock
= socket(AF_INET
, SOCK_STREAM
, 0);
3146 ast_log(LOG_WARNING
, "Unable to create socket: %s\n", strerror(errno
));
3149 setsockopt(asock
, SOL_SOCKET
, SO_REUSEADDR
, &x
, sizeof(x
));
3150 if (bind(asock
, (struct sockaddr
*)&ba
, sizeof(ba
))) {
3151 ast_log(LOG_WARNING
, "Unable to bind socket: %s\n", strerror(errno
));
3156 if (listen(asock
, 2)) {
3157 ast_log(LOG_WARNING
, "Unable to listen on socket: %s\n", strerror(errno
));
3162 flags
= fcntl(asock
, F_GETFL
);
3163 fcntl(asock
, F_SETFL
, flags
| O_NONBLOCK
);
3165 ast_verbose("Asterisk Management interface listening on port %d\n", portno
);
3166 ast_pthread_create_background(&t
, NULL
, accept_thread
, NULL
);
3171 int reload_manager(void)
3173 manager_event(EVENT_FLAG_SYSTEM
, "Reload", "Message: Reload Requested\r\n");
3174 return init_manager();