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
];
79 char app
[AST_MAX_APP
];
80 char appdata
[AST_MAX_EXTENSION
];
81 char cid_name
[AST_MAX_EXTENSION
];
82 char cid_num
[AST_MAX_EXTENSION
];
83 char context
[AST_MAX_CONTEXT
];
84 char exten
[AST_MAX_EXTENSION
];
85 char idtext
[AST_MAX_EXTENSION
];
86 char account
[AST_MAX_ACCOUNT_CODE
];
88 struct ast_variable
*vars
;
94 struct eventqent
*next
;
99 static int portno
= DEFAULT_MANAGER_PORT
;
100 static int asock
= -1;
101 static int displayconnects
= 1;
102 static int timestampevents
;
103 static int httptimeout
= 60;
106 static int block_sockets
;
107 static int num_sessions
;
109 /* Protected by the sessions list lock */
110 struct eventqent
*master_eventq
= NULL
;
112 AST_THREADSTORAGE(manager_event_buf
, manager_event_buf_init
);
113 #define MANAGER_EVENT_BUF_INITSIZE 256
115 AST_THREADSTORAGE(astman_append_buf
, astman_append_buf_init
);
116 #define ASTMAN_APPEND_BUF_INITSIZE 256
118 static struct permalias
{
122 { EVENT_FLAG_SYSTEM
, "system" },
123 { EVENT_FLAG_CALL
, "call" },
124 { EVENT_FLAG_LOG
, "log" },
125 { EVENT_FLAG_VERBOSE
, "verbose" },
126 { EVENT_FLAG_COMMAND
, "command" },
127 { EVENT_FLAG_AGENT
, "agent" },
128 { EVENT_FLAG_USER
, "user" },
129 { EVENT_FLAG_CONFIG
, "config" },
134 #define MAX_BLACKLIST_CMD_LEN 2
136 char *words
[AST_MAX_CMD_LEN
];
137 } command_blacklist
[] = {
138 {{ "module", "load", NULL
}},
139 {{ "module", "unload", NULL
}},
143 /*! Execution thread */
145 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
147 /*! socket address */
148 struct sockaddr_in sin
;
151 /*! Whether an HTTP manager is in use */
153 /*! Whether an HTTP session should be destroyed */
155 /*! Whether an HTTP session has someone waiting on events */
156 pthread_t waiting_thread
;
157 /*! Unique manager identifer */
159 /*! Session timeout if HTTP */
160 time_t sessiontimeout
;
161 /*! Output from manager interface */
162 struct ast_dynamic_str
*outputstr
;
163 /*! Logged in username */
165 /*! Authentication challenge */
167 /*! Authentication status */
169 /*! Authorization for reading */
171 /*! Authorization for writing */
177 int displaysystemname
; /*!< Add system name to manager responses and events */
178 /* Queued events that we've not had the ability to send yet */
179 struct eventqent
*eventq
;
180 /* Timeout for ast_carefulwrite() */
182 int pending_event
; /*!< Pending events indicator in case when waiting_thread is NULL */
183 AST_LIST_ENTRY(mansession
) list
;
186 static AST_LIST_HEAD_STATIC(sessions
, mansession
);
188 struct ast_manager_user
{
195 unsigned int displayconnects
:1;
197 AST_LIST_ENTRY(ast_manager_user
) list
;
200 static AST_LIST_HEAD_STATIC(users
, ast_manager_user
);
202 static struct manager_action
*first_action
;
203 AST_RWLOCK_DEFINE_STATIC(actionlock
);
205 /*! \brief Convert authority code to string with serveral options */
206 static char *authority_to_str(int authority
, char *res
, int reslen
)
208 int running_total
= 0, i
;
210 memset(res
, 0, reslen
);
211 for (i
= 0; i
< (sizeof(perms
) / sizeof(perms
[0])) - 1; i
++) {
212 if (authority
& perms
[i
].num
) {
214 strncat(res
, ",", (reslen
> running_total
) ? reslen
- running_total
- 1 : 0);
217 strncat(res
, perms
[i
].label
, (reslen
> running_total
) ? reslen
- running_total
- 1 : 0);
218 running_total
+= strlen(perms
[i
].label
);
222 if (ast_strlen_zero(res
))
223 ast_copy_string(res
, "<none>", reslen
);
228 static char *complete_show_mancmd(const char *line
, const char *word
, int pos
, int state
)
230 struct manager_action
*cur
;
234 ast_rwlock_rdlock(&actionlock
);
235 for (cur
= first_action
; cur
; cur
= cur
->next
) { /* Walk the list of actions */
236 if (!strncasecmp(word
, cur
->action
, strlen(word
)) && ++which
> state
) {
237 ret
= ast_strdup(cur
->action
);
238 break; /* make sure we exit even if ast_strdup() returns NULL */
241 ast_rwlock_unlock(&actionlock
);
246 static void xml_copy_escape(char **dst
, size_t *maxlen
, const char *src
, int lower
)
248 while (*src
&& (*maxlen
> 6)) {
251 strcpy(*dst
, "<");
256 strcpy(*dst
, ">");
261 strcpy(*dst
, """);
266 strcpy(*dst
, "'");
271 strcpy(*dst
, "&");
276 *(*dst
)++ = lower
? tolower(*src
) : *src
;
283 struct variable_count
{
288 static int compress_char(char c
)
293 else if (c
>= 'a' && c
<= 'z')
301 static int variable_count_hash_fn(const void *vvc
, const int flags
)
303 const struct variable_count
*vc
= vvc
;
305 for (i
= 0; i
< 5; i
++) {
306 if (vc
->varname
[i
] == '\0')
308 res
+= compress_char(vc
->varname
[i
]) << (i
* 6);
313 static int variable_count_cmp_fn(void *obj
, void *vstr
, int flags
)
315 /* Due to the simplicity of struct variable_count, it makes no difference
316 * if you pass in objects or strings, the same operation applies. This is
317 * due to the fact that the hash occurs on the first element, which means
318 * the address of both the struct and the string are exactly the same. */
319 struct variable_count
*vc
= obj
;
321 return !strcmp(vc
->varname
, str
) ? CMP_MATCH
| CMP_STOP
: 0;
324 static char *xml_translate(char *in
, struct ast_variable
*vars
)
326 struct ast_variable
*v
;
328 char *out
, *tmp
, *var
, *val
;
329 char *objtype
= NULL
;
337 struct variable_count
*vc
= NULL
;
338 struct ao2_container
*vco
= NULL
;
340 for (v
= vars
; v
; v
= v
->next
) {
341 if (!dest
&& !strcasecmp(v
->name
, "ajaxdest"))
343 else if (!objtype
&& !strcasecmp(v
->name
, "ajaxobjtype"))
350 for (x
= 0; in
[x
]; x
++) {
353 else if (in
[x
] == '\n')
355 else if (strchr("&\"<>\'", in
[x
]))
358 len
= (size_t) (strlen(in
) + colons
* 5 + breaks
* (40 + strlen(dest
) + strlen(objtype
)) + escaped
* 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&" */
359 out
= ast_malloc(len
);
365 while (*in
&& (*in
>= 32))
368 if ((count
> 3) && inobj
) {
369 ast_build_string(&tmp
, &len
, " /></response>\n");
372 /* Entity is closed, so close out the name cache */
377 while (*in
&& (*in
< 32)) {
382 val
= strchr(var
, ':');
389 vco
= ao2_container_alloc(37, variable_count_hash_fn
, variable_count_cmp_fn
);
390 ast_build_string(&tmp
, &len
, "<response type='object' id='%s'><%s", dest
, objtype
);
394 /* Check if the var has been used already */
395 if ((vc
= ao2_find(vco
, var
, 0)))
398 /* Create a new entry for this one */
399 vc
= ao2_alloc(sizeof(*vc
), NULL
);
405 ast_build_string(&tmp
, &len
, " ");
406 xml_copy_escape(&tmp
, &len
, var
, 1);
408 ast_build_string(&tmp
, &len
, "-%d", vc
->count
);
409 ast_build_string(&tmp
, &len
, "='");
410 xml_copy_escape(&tmp
, &len
, val
, 0);
411 ast_build_string(&tmp
, &len
, "'");
417 ast_build_string(&tmp
, &len
, " /></response>\n");
423 static char *html_translate(char *in
)
430 char *tmp
, *var
, *val
, *out
;
432 for (x
=0; in
[x
]; x
++) {
438 len
= strlen(in
) + colons
* 40 + breaks
* 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
439 out
= ast_malloc(len
);
445 while (*in
&& (*in
>= 32))
448 if ((count
% 4) == 0){
449 ast_build_string(&tmp
, &len
, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
452 while (*in
&& (*in
< 32)) {
457 val
= strchr(var
, ':');
463 ast_build_string(&tmp
, &len
, "<tr><td>%s</td><td>%s</td></tr>\r\n", var
, val
);
472 static struct ast_manager_user
*ast_get_manager_by_name_locked(const char *name
)
474 struct ast_manager_user
*user
= NULL
;
476 AST_LIST_TRAVERSE(&users
, user
, list
)
477 if (!strcasecmp(user
->username
, name
))
482 void astman_append(struct mansession
*s
, const char *fmt
, ...)
485 struct ast_dynamic_str
*buf
;
487 ast_mutex_lock(&s
->__lock
);
489 if (!(buf
= ast_dynamic_str_thread_get(&astman_append_buf
, ASTMAN_APPEND_BUF_INITSIZE
))) {
490 ast_mutex_unlock(&s
->__lock
);
495 ast_dynamic_str_thread_set_va(&buf
, 0, &astman_append_buf
, fmt
, ap
);
499 ast_carefulwrite(s
->fd
, buf
->str
, strlen(buf
->str
), s
->writetimeout
);
501 if (!s
->outputstr
&& !(s
->outputstr
= ast_calloc(1, sizeof(*s
->outputstr
)))) {
502 ast_mutex_unlock(&s
->__lock
);
506 ast_dynamic_str_append(&s
->outputstr
, 0, "%s", buf
->str
);
509 ast_mutex_unlock(&s
->__lock
);
512 static int handle_showmancmd(int fd
, int argc
, char *argv
[])
514 struct manager_action
*cur
;
519 return RESULT_SHOWUSAGE
;
521 ast_rwlock_rdlock(&actionlock
);
522 for (cur
= first_action
; cur
; cur
= cur
->next
) { /* Walk the list of actions */
523 for (num
= 3; num
< argc
; num
++) {
524 if (!strcasecmp(cur
->action
, argv
[num
])) {
525 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
: "");
529 ast_rwlock_unlock(&actionlock
);
531 return RESULT_SUCCESS
;
534 static int handle_showmanager(int fd
, int argc
, char *argv
[])
536 struct ast_manager_user
*user
= NULL
;
539 return RESULT_SHOWUSAGE
;
541 AST_LIST_LOCK(&users
);
543 if (!(user
= ast_get_manager_by_name_locked(argv
[3]))) {
544 ast_cli(fd
, "There is no manager called %s\n", argv
[3]);
545 AST_LIST_UNLOCK(&users
);
557 "displayconnects: %s\n",
558 (user
->username
? user
->username
: "(N/A)"),
559 (user
->secret
? "<Set>" : "(N/A)"),
560 (user
->deny
? user
->deny
: "(N/A)"),
561 (user
->permit
? user
->permit
: "(N/A)"),
562 (user
->read
? user
->read
: "(N/A)"),
563 (user
->write
? user
->write
: "(N/A)"),
564 (user
->displayconnects
? "yes" : "no"));
566 AST_LIST_UNLOCK(&users
);
568 return RESULT_SUCCESS
;
572 static int handle_showmanagers(int fd
, int argc
, char *argv
[])
574 struct ast_manager_user
*user
= NULL
;
578 return RESULT_SHOWUSAGE
;
580 AST_LIST_LOCK(&users
);
582 /* If there are no users, print out something along those lines */
583 if (AST_LIST_EMPTY(&users
)) {
584 ast_cli(fd
, "There are no manager users.\n");
585 AST_LIST_UNLOCK(&users
);
586 return RESULT_SUCCESS
;
589 ast_cli(fd
, "\nusername\n--------\n");
591 AST_LIST_TRAVERSE(&users
, user
, list
) {
592 ast_cli(fd
, "%s\n", user
->username
);
596 AST_LIST_UNLOCK(&users
);
598 ast_cli(fd
,"-------------------\n");
599 ast_cli(fd
,"%d manager users configured.\n", count_amu
);
601 return RESULT_SUCCESS
;
605 /*! \brief CLI command
606 Should change to "manager show commands" */
607 static int handle_showmancmds(int fd
, int argc
, char *argv
[])
609 struct manager_action
*cur
;
611 char *format
= " %-15.15s %-15.15s %-55.55s\n";
613 ast_cli(fd
, format
, "Action", "Privilege", "Synopsis");
614 ast_cli(fd
, format
, "------", "---------", "--------");
616 ast_rwlock_rdlock(&actionlock
);
617 for (cur
= first_action
; cur
; cur
= cur
->next
) /* Walk the list of actions */
618 ast_cli(fd
, format
, cur
->action
, authority_to_str(cur
->authority
, authority
, sizeof(authority
) -1), cur
->synopsis
);
619 ast_rwlock_unlock(&actionlock
);
621 return RESULT_SUCCESS
;
624 /*! \brief CLI command show manager connected */
625 /* Should change to "manager show connected" */
626 static int handle_showmanconn(int fd
, int argc
, char *argv
[])
628 struct mansession
*s
;
629 char *format
= " %-15.15s %-15.15s\n";
631 ast_cli(fd
, format
, "Username", "IP Address");
633 AST_LIST_LOCK(&sessions
);
634 AST_LIST_TRAVERSE(&sessions
, s
, list
)
635 ast_cli(fd
, format
,s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
636 AST_LIST_UNLOCK(&sessions
);
638 return RESULT_SUCCESS
;
641 /*! \brief CLI command show manager connected */
642 /* Should change to "manager show connected" */
643 static int handle_showmaneventq(int fd
, int argc
, char *argv
[])
647 AST_LIST_LOCK(&sessions
);
648 for (s
= master_eventq
; s
; s
= s
->next
) {
649 ast_cli(fd
, "Usecount: %d\n",s
->usecount
);
650 ast_cli(fd
, "Category: %d\n", s
->category
);
651 ast_cli(fd
, "Event:\n%s", s
->eventdata
);
653 AST_LIST_UNLOCK(&sessions
);
655 return RESULT_SUCCESS
;
658 static char showmancmd_help
[] =
659 "Usage: manager show command <actionname>\n"
660 " Shows the detailed description for a specific Asterisk manager interface command.\n";
662 static char showmancmds_help
[] =
663 "Usage: manager show commands\n"
664 " Prints a listing of all the available Asterisk manager interface commands.\n";
666 static char showmanconn_help
[] =
667 "Usage: manager show connected\n"
668 " Prints a listing of the users that are currently connected to the\n"
669 "Asterisk manager interface.\n";
671 static char showmaneventq_help
[] =
672 "Usage: manager show eventq\n"
673 " Prints a listing of all events pending in the Asterisk manger\n"
676 static char showmanagers_help
[] =
677 "Usage: manager show users\n"
678 " Prints a listing of all managers that are currently configured on that\n"
681 static char showmanager_help
[] =
682 " Usage: manager show user <user>\n"
683 " Display all information related to the manager user specified.\n";
685 static struct ast_cli_entry cli_show_manager_command_deprecated
= {
686 { "show", "manager", "command", NULL
},
687 handle_showmancmd
, NULL
,
688 NULL
, complete_show_mancmd
};
690 static struct ast_cli_entry cli_show_manager_commands_deprecated
= {
691 { "show", "manager", "commands", NULL
},
692 handle_showmancmds
, NULL
,
695 static struct ast_cli_entry cli_show_manager_connected_deprecated
= {
696 { "show", "manager", "connected", NULL
},
697 handle_showmanconn
, NULL
,
700 static struct ast_cli_entry cli_show_manager_eventq_deprecated
= {
701 { "show", "manager", "eventq", NULL
},
702 handle_showmaneventq
, NULL
,
705 static struct ast_cli_entry cli_manager
[] = {
706 { { "manager", "show", "command", NULL
},
707 handle_showmancmd
, "Show a manager interface command",
708 showmancmd_help
, complete_show_mancmd
, &cli_show_manager_command_deprecated
},
710 { { "manager", "show", "commands", NULL
},
711 handle_showmancmds
, "List manager interface commands",
712 showmancmds_help
, NULL
, &cli_show_manager_commands_deprecated
},
714 { { "manager", "show", "connected", NULL
},
715 handle_showmanconn
, "List connected manager interface users",
716 showmanconn_help
, NULL
, &cli_show_manager_connected_deprecated
},
718 { { "manager", "show", "eventq", NULL
},
719 handle_showmaneventq
, "List manager interface queued events",
720 showmaneventq_help
, NULL
, &cli_show_manager_eventq_deprecated
},
722 { { "manager", "show", "users", NULL
},
723 handle_showmanagers
, "List configured manager users",
724 showmanagers_help
, NULL
, NULL
},
726 { { "manager", "show", "user", NULL
},
727 handle_showmanager
, "Display information on a specific manager user",
728 showmanager_help
, NULL
, NULL
},
731 static void unuse_eventqent(struct eventqent
*e
)
733 if (ast_atomic_dec_and_test(&e
->usecount
) && e
->next
)
734 pthread_kill(t
, SIGURG
);
737 static void free_session(struct mansession
*s
)
739 struct eventqent
*eqe
;
744 ast_mutex_destroy(&s
->__lock
);
747 s
->eventq
= s
->eventq
->next
;
748 unuse_eventqent(eqe
);
753 static void destroy_session(struct mansession
*s
)
755 AST_LIST_LOCK(&sessions
);
756 AST_LIST_REMOVE(&sessions
, s
, list
);
759 AST_LIST_UNLOCK(&sessions
);
762 const char *astman_get_header(const struct message
*m
, char *var
)
767 snprintf(cmp
, sizeof(cmp
), "%s: ", var
);
769 for (x
= 0; x
< m
->hdrcount
; x
++) {
770 if (!strncasecmp(cmp
, m
->headers
[x
], strlen(cmp
)))
771 return m
->headers
[x
] + strlen(cmp
);
777 struct ast_variable
*astman_get_variables(const struct message
*m
)
780 struct ast_variable
*head
= NULL
, *cur
;
784 AST_DECLARE_APP_ARGS(args
,
785 AST_APP_ARG(vars
)[32];
788 varlen
= strlen("Variable: ");
790 for (x
= 0; x
< m
->hdrcount
; x
++) {
791 if (strncasecmp("Variable: ", m
->headers
[x
], varlen
))
794 parse
= ast_strdupa(m
->headers
[x
] + varlen
);
796 AST_STANDARD_APP_ARGS(args
, parse
);
798 for (y
= 0; y
< args
.argc
; y
++) {
801 var
= val
= ast_strdupa(args
.vars
[y
]);
803 if (!val
|| ast_strlen_zero(var
))
805 cur
= ast_variable_new(var
, val
);
819 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
820 hold the session lock _or_ be running in an action callback (in which case s->busy will
821 be non-zero). In either of these cases, there is no need to lock-protect the session's
822 fd, since no other output will be sent (events will be queued), and no input will
823 be read until either the current action finishes or get_input() obtains the session
826 void astman_send_error(struct mansession
*s
, const struct message
*m
, char *error
)
828 const char *id
= astman_get_header(m
,"ActionID");
830 astman_append(s
, "Response: Error\r\n");
831 if (!ast_strlen_zero(id
))
832 astman_append(s
, "ActionID: %s\r\n", id
);
833 astman_append(s
, "Message: %s\r\n\r\n", error
);
836 void astman_send_response(struct mansession
*s
, const struct message
*m
, char *resp
, char *msg
)
838 const char *id
= astman_get_header(m
,"ActionID");
840 astman_append(s
, "Response: %s\r\n", resp
);
841 if (!ast_strlen_zero(id
))
842 astman_append(s
, "ActionID: %s\r\n", id
);
844 astman_append(s
, "Message: %s\r\n\r\n", msg
);
846 astman_append(s
, "\r\n");
849 void astman_send_ack(struct mansession
*s
, const struct message
*m
, char *msg
)
851 astman_send_response(s
, m
, "Success", msg
);
854 /*! Tells you if smallstr exists inside bigstr
855 which is delim by delim and uses no buf or stringsep
856 ast_instring("this|that|more","this",',') == 1;
858 feel free to move this to app.c -anthm */
859 static int ast_instring(const char *bigstr
, const char *smallstr
, char delim
)
861 const char *val
= bigstr
, *next
;
864 if ((next
= strchr(val
, delim
))) {
865 if (!strncmp(val
, smallstr
, (next
- val
)))
870 return !strcmp(smallstr
, val
);
872 } while (*(val
= (next
+ 1)));
877 static int get_perm(const char *instr
)
884 for (x
= 0; x
< (sizeof(perms
) / sizeof(perms
[0])); x
++) {
885 if (ast_instring(instr
, perms
[x
].label
, ','))
892 static int ast_is_number(const char *string
)
899 for (x
= 0; x
< strlen(string
); x
++) {
900 if (!(string
[x
] >= 48 && string
[x
] <= 57)) {
906 return ret
? atoi(string
) : 0;
909 static int strings_to_mask(const char *string
)
913 x
= ast_is_number(string
);
917 else if (ast_strlen_zero(string
))
919 else if (ast_false(string
))
921 else if (ast_true(string
)) {
923 for (x
=0; x
<sizeof(perms
) / sizeof(perms
[0]); x
++)
927 for (x
=0; x
<sizeof(perms
) / sizeof(perms
[0]); x
++) {
928 if (ast_instring(string
, perms
[x
].label
, ','))
937 Rather than braindead on,off this now can also accept a specific int mask value
938 or a ',' delim list of mask strings (the same as manager.conf) -anthm
940 static int set_eventmask(struct mansession
*s
, const char *eventmask
)
942 int maskint
= strings_to_mask(eventmask
);
944 ast_mutex_lock(&s
->__lock
);
946 s
->send_events
= maskint
;
947 ast_mutex_unlock(&s
->__lock
);
952 static int authenticate(struct mansession
*s
, const struct message
*m
)
954 struct ast_config
*cfg
;
956 const char *user
= astman_get_header(m
, "Username");
957 const char *pass
= astman_get_header(m
, "Secret");
958 const char *authtype
= astman_get_header(m
, "AuthType");
959 const char *key
= astman_get_header(m
, "Key");
960 const char *events
= astman_get_header(m
, "Events");
962 cfg
= ast_config_load("manager.conf");
965 cat
= ast_category_browse(cfg
, NULL
);
967 if (strcasecmp(cat
, "general")) {
969 if (!strcasecmp(cat
, user
)) {
970 struct ast_variable
*v
;
971 struct ast_ha
*ha
= NULL
;
972 char *password
= NULL
;
974 for (v
= ast_variable_browse(cfg
, cat
); v
; v
= v
->next
) {
975 if (!strcasecmp(v
->name
, "secret")) {
977 } else if (!strcasecmp(v
->name
, "displaysystemname")) {
978 if (ast_true(v
->value
)) {
979 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME
)) {
980 s
->displaysystemname
= 1;
982 ast_log(LOG_ERROR
, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
985 } else if (!strcasecmp(v
->name
, "permit") ||
986 !strcasecmp(v
->name
, "deny")) {
987 ha
= ast_append_ha(v
->name
, v
->value
, ha
);
988 } else if (!strcasecmp(v
->name
, "writetimeout")) {
989 int val
= atoi(v
->value
);
992 ast_log(LOG_WARNING
, "Invalid writetimeout value '%s' at line %d\n", v
->value
, v
->lineno
);
994 s
->writetimeout
= val
;
998 if (ha
&& !ast_apply_ha(ha
, &(s
->sin
))) {
999 ast_log(LOG_NOTICE
, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1001 ast_config_destroy(cfg
);
1005 if (!strcasecmp(authtype
, "MD5")) {
1006 if (!ast_strlen_zero(key
) &&
1007 !ast_strlen_zero(s
->challenge
) && !ast_strlen_zero(password
)) {
1010 char md5key
[256] = "";
1011 struct MD5Context md5
;
1012 unsigned char digest
[16];
1014 MD5Update(&md5
, (unsigned char *) s
->challenge
, strlen(s
->challenge
));
1015 MD5Update(&md5
, (unsigned char *) password
, strlen(password
));
1016 MD5Final(digest
, &md5
);
1017 for (x
=0; x
<16; x
++)
1018 len
+= sprintf(md5key
+ len
, "%2.2x", digest
[x
]);
1019 if (!strcmp(md5key
, key
))
1022 ast_config_destroy(cfg
);
1026 ast_log(LOG_DEBUG
, "MD5 authentication is not possible. challenge: '%s'\n",
1027 S_OR(s
->challenge
, ""));
1028 ast_config_destroy(cfg
);
1031 } else if (password
&& !strcmp(password
, pass
)) {
1034 ast_log(LOG_NOTICE
, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1035 ast_config_destroy(cfg
);
1040 cat
= ast_category_browse(cfg
, cat
);
1043 ast_copy_string(s
->username
, cat
, sizeof(s
->username
));
1044 s
->readperm
= get_perm(ast_variable_retrieve(cfg
, cat
, "read"));
1045 s
->writeperm
= get_perm(ast_variable_retrieve(cfg
, cat
, "write"));
1046 ast_config_destroy(cfg
);
1048 set_eventmask(s
, events
);
1051 ast_config_destroy(cfg
);
1052 cfg
= ast_config_load("users.conf");
1055 cat
= ast_category_browse(cfg
, NULL
);
1057 struct ast_variable
*v
;
1058 const char *password
= NULL
;
1060 const char *readperms
= NULL
;
1061 const char *writeperms
= NULL
;
1063 if (strcasecmp(cat
, user
) || !strcasecmp(cat
, "general")) {
1064 cat
= ast_category_browse(cfg
, cat
);
1067 for (v
= ast_variable_browse(cfg
, cat
); v
; v
= v
->next
) {
1068 if (!strcasecmp(v
->name
, "secret"))
1069 password
= v
->value
;
1070 else if (!strcasecmp(v
->name
, "hasmanager"))
1071 hasmanager
= ast_true(v
->value
);
1072 else if (!strcasecmp(v
->name
, "managerread"))
1073 readperms
= v
->value
;
1074 else if (!strcasecmp(v
->name
, "managerwrite"))
1075 writeperms
= v
->value
;
1079 if (!password
|| strcmp(password
, pass
)) {
1080 ast_log(LOG_NOTICE
, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1081 ast_config_destroy(cfg
);
1084 ast_copy_string(s
->username
, cat
, sizeof(s
->username
));
1085 s
->readperm
= readperms
? get_perm(readperms
) : -1;
1086 s
->writeperm
= writeperms
? get_perm(writeperms
) : -1;
1087 ast_config_destroy(cfg
);
1089 set_eventmask(s
, events
);
1092 ast_log(LOG_NOTICE
, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1093 ast_config_destroy(cfg
);
1097 /*! \brief Manager PING */
1098 static char mandescr_ping
[] =
1099 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1100 " manager connection open.\n"
1101 "Variables: NONE\n";
1103 static int action_ping(struct mansession
*s
, const struct message
*m
)
1105 astman_send_response(s
, m
, "Pong", NULL
);
1109 static char mandescr_getconfig
[] =
1110 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1111 "file by category and contents.\n"
1113 " Filename: Configuration filename (e.g. foo.conf)\n";
1115 static int action_getconfig(struct mansession
*s
, const struct message
*m
)
1117 struct ast_config
*cfg
;
1118 const char *fn
= astman_get_header(m
, "Filename");
1121 char *category
=NULL
;
1122 struct ast_variable
*v
;
1123 char idText
[256] = "";
1124 const char *id
= astman_get_header(m
, "ActionID");
1126 if (!ast_strlen_zero(id
))
1127 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1129 if (ast_strlen_zero(fn
)) {
1130 astman_send_error(s
, m
, "Filename not specified");
1133 if (!(cfg
= ast_config_load_with_comments(fn
))) {
1134 astman_send_error(s
, m
, "Config file not found");
1137 astman_append(s
, "Response: Success\r\n%s", idText
);
1138 while ((category
= ast_category_browse(cfg
, category
))) {
1140 astman_append(s
, "Category-%06d: %s\r\n", catcount
, category
);
1141 for (v
= ast_variable_browse(cfg
, category
); v
; v
= v
->next
)
1142 astman_append(s
, "Line-%06d-%06d: %s=%s\r\n", catcount
, lineno
++, v
->name
, v
->value
);
1145 ast_config_destroy(cfg
);
1146 astman_append(s
, "\r\n");
1152 static void handle_updates(struct mansession
*s
, const struct message
*m
, struct ast_config
*cfg
)
1156 const char *action
, *cat
, *var
, *value
, *match
;
1157 struct ast_category
*category
;
1158 struct ast_variable
*v
;
1160 for (x
=0;x
<100000;x
++) {
1161 unsigned int object
= 0;
1163 snprintf(hdr
, sizeof(hdr
), "Action-%06d", x
);
1164 action
= astman_get_header(m
, hdr
);
1165 if (ast_strlen_zero(action
))
1167 snprintf(hdr
, sizeof(hdr
), "Cat-%06d", x
);
1168 cat
= astman_get_header(m
, hdr
);
1169 snprintf(hdr
, sizeof(hdr
), "Var-%06d", x
);
1170 var
= astman_get_header(m
, hdr
);
1171 snprintf(hdr
, sizeof(hdr
), "Value-%06d", x
);
1172 value
= astman_get_header(m
, hdr
);
1173 if (!ast_strlen_zero(value
) && *value
== '>') {
1177 snprintf(hdr
, sizeof(hdr
), "Match-%06d", x
);
1178 match
= astman_get_header(m
, hdr
);
1179 if (!strcasecmp(action
, "newcat")) {
1180 if (!ast_strlen_zero(cat
)) {
1181 category
= ast_category_new(cat
);
1183 ast_category_append(cfg
, category
);
1186 } else if (!strcasecmp(action
, "renamecat")) {
1187 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(value
)) {
1188 category
= ast_category_get(cfg
, cat
);
1190 ast_category_rename(category
, value
);
1192 } else if (!strcasecmp(action
, "delcat")) {
1193 if (!ast_strlen_zero(cat
))
1194 ast_category_delete(cfg
, (char *) cat
);
1195 } else if (!strcasecmp(action
, "update")) {
1196 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) && (category
= ast_category_get(cfg
, cat
)))
1197 ast_variable_update(category
, var
, value
, match
, object
);
1198 } else if (!strcasecmp(action
, "delete")) {
1199 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) && (category
= ast_category_get(cfg
, cat
)))
1200 ast_variable_delete(category
, (char *) var
, (char *) match
);
1201 } else if (!strcasecmp(action
, "append")) {
1202 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) &&
1203 (category
= ast_category_get(cfg
, cat
)) &&
1204 (v
= ast_variable_new(var
, value
))){
1205 if (object
|| (match
&& !strcasecmp(match
, "object")))
1207 ast_variable_append(category
, v
);
1213 static char mandescr_updateconfig
[] =
1214 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1215 "configuration elements in Asterisk configuration files.\n"
1216 "Variables (X's represent 6 digit number beginning with 000000):\n"
1217 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1218 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1219 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1220 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1221 " Cat-XXXXXX: Category to operate on\n"
1222 " Var-XXXXXX: Variable to work on\n"
1223 " Value-XXXXXX: Value to work on\n"
1224 " Match-XXXXXX: Extra match required to match line\n";
1226 static int action_updateconfig(struct mansession
*s
, const struct message
*m
)
1228 struct ast_config
*cfg
;
1229 const char *sfn
= astman_get_header(m
, "SrcFilename");
1230 const char *dfn
= astman_get_header(m
, "DstFilename");
1232 char idText
[256] = "";
1233 const char *id
= astman_get_header(m
, "ActionID");
1234 const char *rld
= astman_get_header(m
, "Reload");
1236 if (!ast_strlen_zero(id
))
1237 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1239 if (ast_strlen_zero(sfn
) || ast_strlen_zero(dfn
)) {
1240 astman_send_error(s
, m
, "Filename not specified");
1243 if (!(cfg
= ast_config_load_with_comments(sfn
))) {
1244 astman_send_error(s
, m
, "Config file not found");
1247 handle_updates(s
, m
, cfg
);
1248 res
= config_text_file_save(dfn
, cfg
, "Manager");
1249 ast_config_destroy(cfg
);
1251 astman_send_error(s
, m
, "Save of config failed");
1254 astman_append(s
, "Response: Success\r\n%s\r\n", idText
);
1255 if (!ast_strlen_zero(rld
)) {
1258 ast_module_reload(rld
);
1263 /*! \brief Manager WAITEVENT */
1264 static char mandescr_waitevent
[] =
1265 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1266 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1267 "session, events will be generated and queued.\n"
1269 " Timeout: Maximum time to wait for events\n";
1271 static int action_waitevent(struct mansession
*s
, const struct message
*m
)
1273 const char *timeouts
= astman_get_header(m
, "Timeout");
1274 int timeout
= -1, max
;
1278 struct eventqent
*eqe
;
1279 const char *id
= astman_get_header(m
,"ActionID");
1280 char idText
[256] = "";
1282 if (!ast_strlen_zero(id
))
1283 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1285 if (!ast_strlen_zero(timeouts
)) {
1286 sscanf(timeouts
, "%i", &timeout
);
1289 ast_mutex_lock(&s
->__lock
);
1290 if (s
->waiting_thread
!= AST_PTHREADT_NULL
) {
1291 pthread_kill(s
->waiting_thread
, SIGURG
);
1293 if (s
->sessiontimeout
) {
1295 max
= s
->sessiontimeout
- now
- 10;
1298 if ((timeout
< 0) || (timeout
> max
))
1300 if (!s
->send_events
)
1301 s
->send_events
= -1;
1302 /* Once waitevent is called, always queue events from now on */
1304 ast_mutex_unlock(&s
->__lock
);
1305 s
->waiting_thread
= pthread_self();
1307 ast_log(LOG_DEBUG
, "Starting waiting for an event!\n");
1308 for (x
=0; ((x
< timeout
) || (timeout
< 0)); x
++) {
1309 ast_mutex_lock(&s
->__lock
);
1310 if (s
->eventq
&& s
->eventq
->next
)
1312 if (s
->waiting_thread
!= pthread_self())
1316 ast_mutex_unlock(&s
->__lock
);
1320 if (ast_wait_for_input(s
->fd
, 1000))
1327 ast_log(LOG_DEBUG
, "Finished waiting for an event!\n");
1328 ast_mutex_lock(&s
->__lock
);
1329 if (s
->waiting_thread
== pthread_self()) {
1330 astman_send_response(s
, m
, "Success", "Waiting for Event...");
1331 /* Only show events if we're the most recent waiter */
1332 while(s
->eventq
->next
) {
1333 eqe
= s
->eventq
->next
;
1334 if (((s
->readperm
& eqe
->category
) == eqe
->category
) &&
1335 ((s
->send_events
& eqe
->category
) == eqe
->category
)) {
1336 astman_append(s
, "%s", eqe
->eventdata
);
1338 unuse_eventqent(s
->eventq
);
1342 "Event: WaitEventComplete\r\n"
1345 s
->waiting_thread
= AST_PTHREADT_NULL
;
1347 ast_log(LOG_DEBUG
, "Abandoning event request!\n");
1349 ast_mutex_unlock(&s
->__lock
);
1353 static char mandescr_listcommands
[] =
1354 "Description: Returns the action name and synopsis for every\n"
1355 " action that is available to the user\n"
1356 "Variables: NONE\n";
1358 /*! \note The actionlock is read-locked by the caller of this function */
1359 static int action_listcommands(struct mansession
*s
, const struct message
*m
)
1361 struct manager_action
*cur
;
1362 char idText
[256] = "";
1364 const char *id
= astman_get_header(m
,"ActionID");
1366 if (!ast_strlen_zero(id
))
1367 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1368 astman_append(s
, "Response: Success\r\n%s", idText
);
1369 for (cur
= first_action
; cur
; cur
= cur
->next
) {
1370 if ((s
->writeperm
& cur
->authority
) == cur
->authority
)
1371 astman_append(s
, "%s: %s (Priv: %s)\r\n", cur
->action
, cur
->synopsis
, authority_to_str(cur
->authority
, temp
, sizeof(temp
)));
1373 astman_append(s
, "\r\n");
1378 static char mandescr_events
[] =
1379 "Description: Enable/Disable sending of events to this manager\n"
1382 " EventMask: 'on' if all events should be sent,\n"
1383 " 'off' if no events should be sent,\n"
1384 " 'system,call,log' to select which flags events should have to be sent.\n";
1386 static int action_events(struct mansession
*s
, const struct message
*m
)
1388 const char *mask
= astman_get_header(m
, "EventMask");
1391 res
= set_eventmask(s
, mask
);
1393 astman_send_response(s
, m
, "Events On", NULL
);
1395 astman_send_response(s
, m
, "Events Off", NULL
);
1400 static char mandescr_logoff
[] =
1401 "Description: Logoff this manager session\n"
1402 "Variables: NONE\n";
1404 static int action_logoff(struct mansession
*s
, const struct message
*m
)
1406 astman_send_response(s
, m
, "Goodbye", "Thanks for all the fish.");
1410 static char mandescr_hangup
[] =
1411 "Description: Hangup a channel\n"
1413 " Channel: The channel name to be hungup\n";
1415 static int action_hangup(struct mansession
*s
, const struct message
*m
)
1417 struct ast_channel
*c
= NULL
;
1418 const char *name
= astman_get_header(m
, "Channel");
1419 if (ast_strlen_zero(name
)) {
1420 astman_send_error(s
, m
, "No channel specified");
1423 c
= ast_get_channel_by_name_locked(name
);
1425 astman_send_error(s
, m
, "No such channel");
1428 ast_softhangup(c
, AST_SOFTHANGUP_EXPLICIT
);
1429 ast_channel_unlock(c
);
1430 astman_send_ack(s
, m
, "Channel Hungup");
1434 static char mandescr_setvar
[] =
1435 "Description: Set a global or local channel variable.\n"
1436 "Variables: (Names marked with * are required)\n"
1437 " Channel: Channel to set variable for\n"
1438 " *Variable: Variable name\n"
1441 static int action_setvar(struct mansession
*s
, const struct message
*m
)
1443 struct ast_channel
*c
= NULL
;
1444 const char *name
= astman_get_header(m
, "Channel");
1445 const char *varname
= astman_get_header(m
, "Variable");
1446 const char *varval
= astman_get_header(m
, "Value");
1448 if (ast_strlen_zero(varname
)) {
1449 astman_send_error(s
, m
, "No variable specified");
1453 if (!ast_strlen_zero(name
)) {
1454 c
= ast_get_channel_by_name_locked(name
);
1456 astman_send_error(s
, m
, "No such channel");
1461 pbx_builtin_setvar_helper(c
, varname
, S_OR(varval
, ""));
1464 ast_channel_unlock(c
);
1466 astman_send_ack(s
, m
, "Variable Set");
1471 static char mandescr_getvar
[] =
1472 "Description: Get the value of a global or local channel variable.\n"
1473 "Variables: (Names marked with * are required)\n"
1474 " Channel: Channel to read variable from\n"
1475 " *Variable: Variable name\n"
1476 " ActionID: Optional Action id for message matching.\n";
1478 static int action_getvar(struct mansession
*s
, const struct message
*m
)
1480 struct ast_channel
*c
= NULL
;
1481 const char *name
= astman_get_header(m
, "Channel");
1482 const char *varname
= astman_get_header(m
, "Variable");
1483 const char *id
= astman_get_header(m
,"ActionID");
1485 char workspace
[1024] = "";
1487 if (ast_strlen_zero(varname
)) {
1488 astman_send_error(s
, m
, "No variable specified");
1492 if (!ast_strlen_zero(name
)) {
1493 c
= ast_get_channel_by_name_locked(name
);
1495 astman_send_error(s
, m
, "No such channel");
1500 if (varname
[strlen(varname
) - 1] == ')') {
1501 char *copy
= ast_strdupa(varname
);
1503 ast_func_read(c
, copy
, workspace
, sizeof(workspace
));
1506 pbx_retrieve_variable(c
, varname
, &varval
, workspace
, sizeof(workspace
), NULL
);
1510 ast_channel_unlock(c
);
1511 astman_append(s
, "Response: Success\r\n"
1512 "Variable: %s\r\nValue: %s\r\n", varname
, varval
);
1513 if (!ast_strlen_zero(id
))
1514 astman_append(s
, "ActionID: %s\r\n",id
);
1515 astman_append(s
, "\r\n");
1521 /*! \brief Manager "status" command to show channels */
1522 /* Needs documentation... */
1523 static int action_status(struct mansession
*s
, const struct message
*m
)
1525 const char *id
= astman_get_header(m
,"ActionID");
1526 const char *name
= astman_get_header(m
,"Channel");
1527 char idText
[256] = "";
1528 struct ast_channel
*c
;
1530 struct timeval now
= ast_tvnow();
1531 long elapsed_seconds
= 0;
1532 int all
= ast_strlen_zero(name
); /* set if we want all channels */
1534 if (!ast_strlen_zero(id
))
1535 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1537 c
= ast_channel_walk_locked(NULL
);
1539 c
= ast_get_channel_by_name_locked(name
);
1541 astman_send_error(s
, m
, "No such channel");
1545 astman_send_ack(s
, m
, "Channel status will follow");
1546 /* if we look by name, we break after the first iteration */
1549 snprintf(bridge
, sizeof(bridge
), "Link: %s\r\n", c
->_bridge
->name
);
1554 elapsed_seconds
= now
.tv_sec
- c
->cdr
->start
.tv_sec
;
1558 "Privilege: Call\r\n"
1560 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1561 "CallerIDNum: %s\r\n"
1562 "CallerIDName: %s\r\n"
1574 S_OR(c
->cid
.cid_num
, "<unknown>"),
1575 S_OR(c
->cid
.cid_num
, "<unknown>"),
1576 S_OR(c
->cid
.cid_name
, "<unknown>"),
1578 ast_state2str(c
->_state
), c
->context
,
1579 c
->exten
, c
->priority
, (long)elapsed_seconds
, bridge
, c
->uniqueid
, idText
);
1583 "Privilege: Call\r\n"
1585 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1586 "CallerIDNum: %s\r\n"
1587 "CallerIDName: %s\r\n"
1595 S_OR(c
->cid
.cid_num
, "<unknown>"),
1596 S_OR(c
->cid
.cid_num
, "<unknown>"),
1597 S_OR(c
->cid
.cid_name
, "<unknown>"),
1599 ast_state2str(c
->_state
), bridge
, c
->uniqueid
, idText
);
1601 ast_channel_unlock(c
);
1604 c
= ast_channel_walk_locked(c
);
1607 "Event: StatusComplete\r\n"
1613 static char mandescr_redirect
[] =
1614 "Description: Redirect (transfer) a call.\n"
1615 "Variables: (Names marked with * are required)\n"
1616 " *Channel: Channel to redirect\n"
1617 " ExtraChannel: Second call leg to transfer (optional)\n"
1618 " *Exten: Extension to transfer to\n"
1619 " *Context: Context to transfer to\n"
1620 " *Priority: Priority to transfer to\n"
1621 " ActionID: Optional Action id for message matching.\n";
1623 /*! \brief action_redirect: The redirect manager command */
1624 static int action_redirect(struct mansession
*s
, const struct message
*m
)
1626 const char *name
= astman_get_header(m
, "Channel");
1627 const char *name2
= astman_get_header(m
, "ExtraChannel");
1628 const char *exten
= astman_get_header(m
, "Exten");
1629 const char *context
= astman_get_header(m
, "Context");
1630 const char *priority
= astman_get_header(m
, "Priority");
1631 struct ast_channel
*chan
, *chan2
= NULL
;
1635 if (ast_strlen_zero(name
)) {
1636 astman_send_error(s
, m
, "Channel not specified");
1639 if (!ast_strlen_zero(priority
) && (sscanf(priority
, "%d", &pi
) != 1)) {
1640 if ((pi
= ast_findlabel_extension(NULL
, context
, exten
, priority
, NULL
)) < 1) {
1641 astman_send_error(s
, m
, "Invalid priority");
1645 /* XXX watch out, possible deadlock!!! */
1646 chan
= ast_get_channel_by_name_locked(name
);
1649 snprintf(buf
, sizeof(buf
), "Channel does not exist: %s", name
);
1650 astman_send_error(s
, m
, buf
);
1653 if (ast_check_hangup(chan
)) {
1654 astman_send_error(s
, m
, "Redirect failed, channel not up.");
1655 ast_channel_unlock(chan
);
1658 if (!ast_strlen_zero(name2
))
1659 chan2
= ast_get_channel_by_name_locked(name2
);
1660 if (chan2
&& ast_check_hangup(chan2
)) {
1661 astman_send_error(s
, m
, "Redirect failed, extra channel not up.");
1662 ast_channel_unlock(chan
);
1663 ast_channel_unlock(chan2
);
1666 res
= ast_async_goto(chan
, context
, exten
, pi
);
1668 if (!ast_strlen_zero(name2
)) {
1670 res
= ast_async_goto(chan2
, context
, exten
, pi
);
1674 astman_send_ack(s
, m
, "Dual Redirect successful");
1676 astman_send_error(s
, m
, "Secondary redirect failed");
1678 astman_send_ack(s
, m
, "Redirect successful");
1680 astman_send_error(s
, m
, "Redirect failed");
1682 ast_channel_unlock(chan
);
1684 ast_channel_unlock(chan2
);
1688 static int check_blacklist(const char *cmd
)
1690 char *cmd_copy
, *cur_cmd
;
1691 char *cmd_words
[MAX_BLACKLIST_CMD_LEN
] = { NULL
, };
1694 cmd_copy
= ast_strdupa(cmd
);
1695 for (i
= 0; i
< MAX_BLACKLIST_CMD_LEN
&& (cur_cmd
= strsep(&cmd_copy
, " ")); i
++) {
1696 cur_cmd
= ast_strip(cur_cmd
);
1697 if (ast_strlen_zero(cur_cmd
)) {
1702 cmd_words
[i
] = cur_cmd
;
1705 for (i
= 0; i
< ARRAY_LEN(command_blacklist
); i
++) {
1708 for (j
= 0; command_blacklist
[i
].words
[j
]; j
++) {
1709 if (ast_strlen_zero(cmd_words
[j
]) || strcasecmp(cmd_words
[j
], command_blacklist
[i
].words
[j
])) {
1723 static char mandescr_command
[] =
1724 "Description: Run a CLI command.\n"
1725 "Variables: (Names marked with * are required)\n"
1726 " *Command: Asterisk CLI command to run\n"
1727 " ActionID: Optional Action id for message matching.\n";
1729 /*! \brief action_command: Manager command "command" - execute CLI command */
1730 static int action_command(struct mansession
*s
, const struct message
*m
)
1732 const char *cmd
= astman_get_header(m
, "Command");
1733 const char *id
= astman_get_header(m
, "ActionID");
1734 char *buf
, *final_buf
;
1735 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1736 int fd
= mkstemp(template);
1739 if (ast_strlen_zero(cmd
)) {
1740 astman_send_error(s
, m
, "No command provided");
1744 if (check_blacklist(cmd
)) {
1745 astman_send_error(s
, m
, "Command blacklisted");
1749 astman_append(s
, "Response: Follows\r\nPrivilege: Command\r\n");
1750 if (!ast_strlen_zero(id
))
1751 astman_append(s
, "ActionID: %s\r\n", id
);
1752 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1753 ast_cli_command(fd
, cmd
); /* XXX need to change this to use a FILE * */
1754 l
= lseek(fd
, 0, SEEK_END
); /* how many chars available */
1756 /* This has a potential to overflow the stack. Hence, use the heap. */
1757 buf
= ast_calloc(1, l
+ 1);
1758 final_buf
= ast_calloc(1, l
+ 1);
1760 lseek(fd
, 0, SEEK_SET
);
1764 term_strip(final_buf
, buf
, l
);
1765 final_buf
[l
] = '\0';
1767 astman_append(s
, "%s", S_OR(final_buf
, buf
));
1772 astman_append(s
, "--END COMMAND--\r\n\r\n");
1774 ast_free(final_buf
);
1778 static void *fast_originate(void *data
)
1780 struct fast_originate_helper
*in
= data
;
1783 struct ast_channel
*chan
= NULL
;
1784 char requested_channel
[AST_CHANNEL_NAME
];
1786 if (!ast_strlen_zero(in
->app
)) {
1787 res
= ast_pbx_outgoing_app(in
->tech
, in
->format
, in
->data
, in
->timeout
, in
->app
, in
->appdata
, &reason
, 1,
1788 S_OR(in
->cid_num
, NULL
),
1789 S_OR(in
->cid_name
, NULL
),
1790 in
->vars
, in
->account
, &chan
);
1792 res
= ast_pbx_outgoing_exten(in
->tech
, in
->format
, in
->data
, in
->timeout
, in
->context
, in
->exten
, in
->priority
, &reason
, 1,
1793 S_OR(in
->cid_num
, NULL
),
1794 S_OR(in
->cid_name
, NULL
),
1795 in
->vars
, in
->account
, &chan
);
1799 snprintf(requested_channel
, AST_CHANNEL_NAME
, "%s/%s", in
->tech
, in
->data
);
1800 /* Tell the manager what happened with the channel */
1801 manager_event(EVENT_FLAG_CALL
, "OriginateResponse",
1809 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1810 "CallerIDNum: %s\r\n"
1811 "CallerIDName: %s\r\n",
1812 in
->idtext
, res
? "Failure" : "Success", chan
? chan
->name
: requested_channel
, in
->context
, in
->exten
, reason
,
1813 chan
? chan
->uniqueid
: "<null>",
1814 S_OR(in
->cid_num
, "<unknown>"),
1815 S_OR(in
->cid_num
, "<unknown>"),
1816 S_OR(in
->cid_name
, "<unknown>")
1819 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1821 ast_channel_unlock(chan
);
1826 static char mandescr_originate
[] =
1827 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1828 " Application/Data\n"
1829 "Variables: (Names marked with * are required)\n"
1830 " *Channel: Channel name to call\n"
1831 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1832 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1833 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1834 " Application: Application to use\n"
1835 " Data: Data to use (requires 'Application')\n"
1836 " Timeout: How long to wait for call to be answered (in ms)\n"
1837 " CallerID: Caller ID to be set on the outgoing channel\n"
1838 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1839 " Account: Account code\n"
1840 " Async: Set to 'true' for fast origination\n";
1842 static int action_originate(struct mansession
*s
, const struct message
*m
)
1844 const char *name
= astman_get_header(m
, "Channel");
1845 const char *exten
= astman_get_header(m
, "Exten");
1846 const char *context
= astman_get_header(m
, "Context");
1847 const char *priority
= astman_get_header(m
, "Priority");
1848 const char *timeout
= astman_get_header(m
, "Timeout");
1849 const char *callerid
= astman_get_header(m
, "CallerID");
1850 const char *account
= astman_get_header(m
, "Account");
1851 const char *app
= astman_get_header(m
, "Application");
1852 const char *appdata
= astman_get_header(m
, "Data");
1853 const char *async
= astman_get_header(m
, "Async");
1854 const char *id
= astman_get_header(m
, "ActionID");
1855 const char *codecs
= astman_get_header(m
, "Codecs");
1856 struct ast_variable
*vars
= astman_get_variables(m
);
1858 char *l
= NULL
, *n
= NULL
;
1865 int format
= AST_FORMAT_SLINEAR
;
1868 pthread_attr_t attr
;
1869 if (ast_strlen_zero(name
)) {
1870 astman_send_error(s
, m
, "Channel not specified");
1873 if (!ast_strlen_zero(priority
) && (sscanf(priority
, "%d", &pi
) != 1)) {
1874 if ((pi
= ast_findlabel_extension(NULL
, context
, exten
, priority
, NULL
)) < 1) {
1875 astman_send_error(s
, m
, "Invalid priority");
1879 if (!ast_strlen_zero(timeout
) && (sscanf(timeout
, "%d", &to
) != 1)) {
1880 astman_send_error(s
, m
, "Invalid timeout");
1883 ast_copy_string(tmp
, name
, sizeof(tmp
));
1885 data
= strchr(tmp
, '/');
1887 astman_send_error(s
, m
, "Invalid channel");
1891 ast_copy_string(tmp2
, callerid
, sizeof(tmp2
));
1892 ast_callerid_parse(tmp2
, &n
, &l
);
1894 if (ast_strlen_zero(n
))
1898 ast_shrink_phone_number(l
);
1899 if (ast_strlen_zero(l
))
1902 if (!ast_strlen_zero(codecs
)) {
1904 ast_parse_allow_disallow(NULL
, &format
, codecs
, 1);
1906 if (ast_true(async
)) {
1907 struct fast_originate_helper
*fast
= ast_calloc(1, sizeof(*fast
));
1911 if (!ast_strlen_zero(id
))
1912 snprintf(fast
->idtext
, sizeof(fast
->idtext
), "ActionID: %s\r\n", id
);
1913 ast_copy_string(fast
->tech
, tech
, sizeof(fast
->tech
));
1914 ast_copy_string(fast
->data
, data
, sizeof(fast
->data
));
1915 ast_copy_string(fast
->app
, app
, sizeof(fast
->app
));
1916 ast_copy_string(fast
->appdata
, appdata
, sizeof(fast
->appdata
));
1918 ast_copy_string(fast
->cid_num
, l
, sizeof(fast
->cid_num
));
1920 ast_copy_string(fast
->cid_name
, n
, sizeof(fast
->cid_name
));
1922 ast_copy_string(fast
->context
, context
, sizeof(fast
->context
));
1923 ast_copy_string(fast
->exten
, exten
, sizeof(fast
->exten
));
1924 ast_copy_string(fast
->account
, account
, sizeof(fast
->account
));
1925 fast
->format
= format
;
1927 fast
->priority
= pi
;
1928 pthread_attr_init(&attr
);
1929 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1930 if (ast_pthread_create(&th
, &attr
, fast_originate
, fast
)) {
1936 pthread_attr_destroy(&attr
);
1938 } else if (!ast_strlen_zero(app
)) {
1939 res
= ast_pbx_outgoing_app(tech
, format
, data
, to
, app
, appdata
, &reason
, 1, l
, n
, vars
, account
, NULL
);
1941 if (exten
&& context
&& pi
)
1942 res
= ast_pbx_outgoing_exten(tech
, format
, data
, to
, context
, exten
, pi
, &reason
, 1, l
, n
, vars
, account
, NULL
);
1944 astman_send_error(s
, m
, "Originate with 'Exten' requires 'Context' and 'Priority'");
1949 astman_send_ack(s
, m
, "Originate successfully queued");
1951 astman_send_error(s
, m
, "Originate failed");
1955 /*! \brief Help text for manager command mailboxstatus
1957 static char mandescr_mailboxstatus
[] =
1958 "Description: Checks a voicemail account for status.\n"
1959 "Variables: (Names marked with * are required)\n"
1960 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1961 " ActionID: Optional ActionID for message matching.\n"
1962 "Returns number of messages.\n"
1963 " Message: Mailbox Status\n"
1964 " Mailbox: <mailboxid>\n"
1965 " Waiting: <count>\n"
1968 static int action_mailboxstatus(struct mansession
*s
, const struct message
*m
)
1970 const char *mailbox
= astman_get_header(m
, "Mailbox");
1971 const char *id
= astman_get_header(m
,"ActionID");
1972 char idText
[256] = "";
1974 if (ast_strlen_zero(mailbox
)) {
1975 astman_send_error(s
, m
, "Mailbox not specified");
1978 if (!ast_strlen_zero(id
))
1979 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1980 ret
= ast_app_has_voicemail(mailbox
, NULL
);
1981 astman_append(s
, "Response: Success\r\n"
1983 "Message: Mailbox Status\r\n"
1985 "Waiting: %d\r\n\r\n", idText
, mailbox
, ret
);
1989 static char mandescr_mailboxcount
[] =
1990 "Description: Checks a voicemail account for new messages.\n"
1991 "Variables: (Names marked with * are required)\n"
1992 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1993 " ActionID: Optional ActionID for message matching.\n"
1994 "Returns number of new and old messages.\n"
1995 " Message: Mailbox Message Count\n"
1996 " Mailbox: <mailboxid>\n"
1997 " NewMessages: <count>\n"
1998 " OldMessages: <count>\n"
2000 static int action_mailboxcount(struct mansession
*s
, const struct message
*m
)
2002 const char *mailbox
= astman_get_header(m
, "Mailbox");
2003 const char *id
= astman_get_header(m
,"ActionID");
2004 char idText
[256] = "";
2005 int newmsgs
= 0, oldmsgs
= 0;
2006 if (ast_strlen_zero(mailbox
)) {
2007 astman_send_error(s
, m
, "Mailbox not specified");
2010 ast_app_inboxcount(mailbox
, &newmsgs
, &oldmsgs
);
2011 if (!ast_strlen_zero(id
)) {
2012 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n",id
);
2014 astman_append(s
, "Response: Success\r\n"
2016 "Message: Mailbox Message Count\r\n"
2018 "NewMessages: %d\r\n"
2019 "OldMessages: %d\r\n"
2021 idText
,mailbox
, newmsgs
, oldmsgs
);
2025 static char mandescr_extensionstate
[] =
2026 "Description: Report the extension state for given extension.\n"
2027 " If the extension has a hint, will use devicestate to check\n"
2028 " the status of the device connected to the extension.\n"
2029 "Variables: (Names marked with * are required)\n"
2030 " *Exten: Extension to check state on\n"
2031 " *Context: Context for extension\n"
2032 " ActionId: Optional ID for this transaction\n"
2033 "Will return an \"Extension Status\" message.\n"
2034 "The response will include the hint for the extension and the status.\n";
2036 static int action_extensionstate(struct mansession
*s
, const struct message
*m
)
2038 const char *exten
= astman_get_header(m
, "Exten");
2039 const char *context
= astman_get_header(m
, "Context");
2040 const char *id
= astman_get_header(m
,"ActionID");
2041 char idText
[256] = "";
2042 char hint
[256] = "";
2044 if (ast_strlen_zero(exten
)) {
2045 astman_send_error(s
, m
, "Extension not specified");
2048 if (ast_strlen_zero(context
))
2049 context
= "default";
2050 status
= ast_extension_state(NULL
, context
, exten
);
2051 ast_get_hint(hint
, sizeof(hint
) - 1, NULL
, 0, NULL
, context
, exten
);
2052 if (!ast_strlen_zero(id
)) {
2053 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
2055 astman_append(s
, "Response: Success\r\n"
2057 "Message: Extension Status\r\n"
2061 "Status: %d\r\n\r\n",
2062 idText
,exten
, context
, hint
, status
);
2066 static char mandescr_timeout
[] =
2067 "Description: Hangup a channel after a certain time.\n"
2068 "Variables: (Names marked with * are required)\n"
2069 " *Channel: Channel name to hangup\n"
2070 " *Timeout: Maximum duration of the call (sec)\n"
2071 "Acknowledges set time with 'Timeout Set' message\n";
2073 static int action_timeout(struct mansession
*s
, const struct message
*m
)
2075 struct ast_channel
*c
= NULL
;
2076 const char *name
= astman_get_header(m
, "Channel");
2077 int timeout
= atoi(astman_get_header(m
, "Timeout"));
2078 if (ast_strlen_zero(name
)) {
2079 astman_send_error(s
, m
, "No channel specified");
2083 astman_send_error(s
, m
, "No timeout specified");
2086 c
= ast_get_channel_by_name_locked(name
);
2088 astman_send_error(s
, m
, "No such channel");
2091 ast_channel_setwhentohangup(c
, timeout
);
2092 ast_channel_unlock(c
);
2093 astman_send_ack(s
, m
, "Timeout Set");
2097 static int process_events(struct mansession
*s
)
2099 struct eventqent
*eqe
;
2101 ast_mutex_lock(&s
->__lock
);
2103 s
->eventq
= master_eventq
;
2104 while(s
->eventq
->next
) {
2105 eqe
= s
->eventq
->next
;
2106 if ((s
->authenticated
&& (s
->readperm
& eqe
->category
) == eqe
->category
) &&
2107 ((s
->send_events
& eqe
->category
) == eqe
->category
)) {
2109 if (!ret
&& ast_carefulwrite(s
->fd
, eqe
->eventdata
, strlen(eqe
->eventdata
), s
->writetimeout
) < 0)
2111 } else if (!s
->outputstr
&& !(s
->outputstr
= ast_calloc(1, sizeof(*s
->outputstr
))))
2114 ast_dynamic_str_append(&s
->outputstr
, 0, "%s", eqe
->eventdata
);
2116 unuse_eventqent(s
->eventq
);
2119 ast_mutex_unlock(&s
->__lock
);
2123 static char mandescr_userevent
[] =
2124 "Description: Send an event to manager sessions.\n"
2125 "Variables: (Names marked with * are required)\n"
2126 " *UserEvent: EventStringToSend\n"
2127 " Header1: Content1\n"
2128 " HeaderN: ContentN\n";
2130 static int action_userevent(struct mansession
*s
, const struct message
*m
)
2132 const char *event
= astman_get_header(m
, "UserEvent");
2133 char body
[2048] = "";
2135 for (x
= 0; x
< m
->hdrcount
; x
++) {
2136 if (strncasecmp("UserEvent:", m
->headers
[x
], strlen("UserEvent:"))) {
2137 ast_copy_string(body
+ bodylen
, m
->headers
[x
], sizeof(body
) - bodylen
- 3);
2138 bodylen
+= strlen(m
->headers
[x
]);
2139 ast_copy_string(body
+ bodylen
, "\r\n", 3);
2144 manager_event(EVENT_FLAG_USER
, "UserEvent", "UserEvent: %s\r\n%s", event
, body
);
2148 static int process_message(struct mansession
*s
, const struct message
*m
)
2150 char action
[80] = "";
2151 struct manager_action
*tmp
;
2152 const char *id
= astman_get_header(m
,"ActionID");
2153 char idText
[256] = "";
2156 ast_copy_string(action
, astman_get_header(m
, "Action"), sizeof(action
));
2158 ast_log( LOG_DEBUG
, "Manager received command '%s'\n", action
);
2160 if (ast_strlen_zero(action
)) {
2161 astman_send_error(s
, m
, "Missing action in request");
2164 if (!ast_strlen_zero(id
)) {
2165 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
2167 if (!s
->authenticated
) {
2168 if (!strcasecmp(action
, "Challenge")) {
2169 const char *authtype
= astman_get_header(m
, "AuthType");
2171 if (!strcasecmp(authtype
, "MD5")) {
2172 if (ast_strlen_zero(s
->challenge
))
2173 snprintf(s
->challenge
, sizeof(s
->challenge
), "%ld", ast_random());
2174 astman_append(s
, "Response: Success\r\n"
2176 "Challenge: %s\r\n\r\n",
2177 idText
, s
->challenge
);
2180 astman_send_error(s
, m
, "Must specify AuthType");
2183 } else if (!strcasecmp(action
, "Login")) {
2184 if (authenticate(s
, m
)) {
2186 astman_send_error(s
, m
, "Authentication failed");
2189 s
->authenticated
= 1;
2190 if (option_verbose
> 1) {
2191 if (displayconnects
) {
2192 ast_verbose(VERBOSE_PREFIX_2
"%sManager '%s' logged on from %s\n",
2193 (s
->sessiontimeout
? "HTTP " : ""), s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2196 ast_log(LOG_EVENT
, "%sManager '%s' logged on from %s\n",
2197 (s
->sessiontimeout
? "HTTP " : ""), s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2198 astman_send_ack(s
, m
, "Authentication accepted");
2200 } else if (!strcasecmp(action
, "Logoff")) {
2201 astman_send_ack(s
, m
, "See ya");
2204 astman_send_error(s
, m
, "Authentication Required");
2206 if (!strcasecmp(action
, "Login"))
2207 astman_send_ack(s
, m
, "Already logged in");
2209 ast_rwlock_rdlock(&actionlock
);
2210 for (tmp
= first_action
; tmp
; tmp
= tmp
->next
) {
2211 if (strcasecmp(action
, tmp
->action
))
2213 if ((s
->writeperm
& tmp
->authority
) == tmp
->authority
) {
2214 if (tmp
->func(s
, m
))
2217 astman_send_error(s
, m
, "Permission denied");
2220 ast_rwlock_unlock(&actionlock
);
2222 astman_send_error(s
, m
, "Invalid/unknown command");
2227 return process_events(s
);
2230 static int get_input(struct mansession
*s
, char *output
)
2232 /* output must have at least sizeof(s->inbuf) space */
2235 struct pollfd fds
[1];
2236 for (x
= 1; x
< s
->inlen
; x
++) {
2237 if ((s
->inbuf
[x
] == '\n') && (s
->inbuf
[x
-1] == '\r')) {
2238 /* Copy output data up to and including \r\n */
2239 memcpy(output
, s
->inbuf
, x
+ 1);
2240 /* Add trailing \0 */
2242 /* Move remaining data back to the front */
2243 memmove(s
->inbuf
, s
->inbuf
+ x
+ 1, s
->inlen
- x
);
2244 s
->inlen
-= (x
+ 1);
2248 if (s
->inlen
>= sizeof(s
->inbuf
) - 1) {
2249 ast_log(LOG_WARNING
, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s
->sin
.sin_addr
), s
->inbuf
);
2253 fds
[0].events
= POLLIN
;
2255 ast_mutex_lock(&s
->__lock
);
2256 if (s
->pending_event
) {
2257 s
->pending_event
= 0;
2258 ast_mutex_unlock(&s
->__lock
);
2261 s
->waiting_thread
= pthread_self();
2262 ast_mutex_unlock(&s
->__lock
);
2264 res
= poll(fds
, 1, -1);
2266 ast_mutex_lock(&s
->__lock
);
2267 s
->waiting_thread
= AST_PTHREADT_NULL
;
2268 ast_mutex_unlock(&s
->__lock
);
2270 if (errno
== EINTR
|| errno
== EAGAIN
) {
2273 ast_log(LOG_WARNING
, "Select returned error: %s\n", strerror(errno
));
2275 } else if (res
> 0) {
2276 ast_mutex_lock(&s
->__lock
);
2277 res
= read(s
->fd
, s
->inbuf
+ s
->inlen
, sizeof(s
->inbuf
) - 1 - s
->inlen
);
2278 ast_mutex_unlock(&s
->__lock
);
2285 s
->inbuf
[s
->inlen
] = '\0';
2289 static int do_message(struct mansession
*s
)
2291 struct message m
= { 0 };
2292 char header_buf
[sizeof(s
->inbuf
)] = { '\0' };
2296 /* Check if any events are pending and do them if needed */
2297 if (s
->eventq
->next
) {
2298 if (process_events(s
))
2301 res
= get_input(s
, header_buf
);
2304 } else if (res
> 0) {
2305 /* Strip trailing \r\n */
2306 if (strlen(header_buf
) < 2)
2308 header_buf
[strlen(header_buf
) - 2] = '\0';
2309 if (ast_strlen_zero(header_buf
))
2310 return process_message(s
, &m
) ? -1 : 0;
2311 else if (m
.hdrcount
< (AST_MAX_MANHEADERS
- 1))
2312 m
.headers
[m
.hdrcount
++] = ast_strdupa(header_buf
);
2319 static void *session_do(void *data
)
2321 struct mansession
*s
= data
;
2324 astman_append(s
, "Asterisk Call Manager/1.0\r\n");
2326 if ((res
= do_message(s
)) < 0)
2329 if (s
->authenticated
) {
2330 if (option_verbose
> 1) {
2331 if (displayconnects
)
2332 ast_verbose(VERBOSE_PREFIX_2
"Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2334 ast_log(LOG_EVENT
, "Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2336 if (option_verbose
> 1) {
2337 if (displayconnects
)
2338 ast_verbose(VERBOSE_PREFIX_2
"Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2340 ast_log(LOG_EVENT
, "Failed attempt from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2343 /* It is possible under certain circumstances for this session thread
2344 to complete its work and exit *before* the thread that created it
2345 has finished executing the ast_pthread_create_background() function.
2346 If this occurs, some versions of glibc appear to act in a buggy
2347 fashion and attempt to write data into memory that it thinks belongs
2348 to the thread but is in fact not owned by the thread (or may have
2349 been freed completely).
2351 Causing this thread to yield to other threads at least one time
2352 appears to work around this bug.
2360 static void *accept_thread(void *ignore
)
2363 struct sockaddr_in sin
;
2365 struct eventqent
*eqe
;
2366 struct mansession
*s
;
2370 pthread_attr_t attr
;
2372 struct pollfd pfds
[1];
2374 pthread_attr_init(&attr
);
2375 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
2379 AST_LIST_LOCK(&sessions
);
2380 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions
, s
, list
) {
2381 if (s
->sessiontimeout
&& (now
> s
->sessiontimeout
) && !s
->inuse
) {
2382 AST_LIST_REMOVE_CURRENT(&sessions
, list
);
2384 if (s
->authenticated
&& (option_verbose
> 1) && displayconnects
) {
2385 ast_verbose(VERBOSE_PREFIX_2
"HTTP Manager '%s' timed out from %s\n",
2386 s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2392 AST_LIST_TRAVERSE_SAFE_END
2393 /* Purge master event queue of old, unused events, but make sure we
2394 always keep at least one in the queue */
2395 eqe
= master_eventq
;
2396 while (master_eventq
->next
&& !master_eventq
->usecount
) {
2397 eqe
= master_eventq
;
2398 master_eventq
= master_eventq
->next
;
2401 AST_LIST_UNLOCK(&sessions
);
2403 sinlen
= sizeof(sin
);
2405 pfds
[0].events
= POLLIN
;
2406 /* Wait for something to happen, but timeout every few seconds so
2407 we can ditch any old manager sessions */
2408 if (poll(pfds
, 1, 5000) < 1)
2410 as
= accept(asock
, (struct sockaddr
*)&sin
, &sinlen
);
2412 ast_log(LOG_NOTICE
, "Accept returned -1: %s\n", strerror(errno
));
2415 p
= getprotobyname("tcp");
2417 if( setsockopt(as
, p
->p_proto
, TCP_NODELAY
, (char *)&arg
, sizeof(arg
) ) < 0 ) {
2418 ast_log(LOG_WARNING
, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno
));
2421 if (!(s
= ast_calloc(1, sizeof(*s
))))
2424 memcpy(&s
->sin
, &sin
, sizeof(sin
));
2425 s
->writetimeout
= 100;
2426 s
->waiting_thread
= AST_PTHREADT_NULL
;
2428 if (!block_sockets
) {
2429 /* For safety, make sure socket is non-blocking */
2430 flags
= fcntl(as
, F_GETFL
);
2431 fcntl(as
, F_SETFL
, flags
| O_NONBLOCK
);
2433 flags
= fcntl(as
, F_GETFL
);
2434 fcntl(as
, F_SETFL
, flags
& ~O_NONBLOCK
);
2436 ast_mutex_init(&s
->__lock
);
2438 s
->send_events
= -1;
2439 AST_LIST_LOCK(&sessions
);
2440 AST_LIST_INSERT_HEAD(&sessions
, s
, list
);
2442 /* Find the last place in the master event queue and hook ourselves
2444 s
->eventq
= master_eventq
;
2445 while(s
->eventq
->next
)
2446 s
->eventq
= s
->eventq
->next
;
2447 ast_atomic_fetchadd_int(&s
->eventq
->usecount
, 1);
2448 AST_LIST_UNLOCK(&sessions
);
2449 if (ast_pthread_create_background(&s
->t
, &attr
, session_do
, s
))
2452 pthread_attr_destroy(&attr
);
2456 static int append_event(const char *str
, int category
)
2458 struct eventqent
*tmp
, *prev
= NULL
;
2459 tmp
= ast_malloc(sizeof(*tmp
) + strlen(str
));
2465 tmp
->category
= category
;
2466 strcpy(tmp
->eventdata
, str
);
2468 if (master_eventq
) {
2469 prev
= master_eventq
;
2474 master_eventq
= tmp
;
2477 tmp
->usecount
= num_sessions
;
2482 /*! \brief manager_event: Send AMI event to client */
2483 int manager_event(int category
, const char *event
, const char *fmt
, ...)
2485 struct mansession
*s
;
2489 struct ast_dynamic_str
*buf
;
2491 /* Abort if there aren't any manager sessions */
2495 if (!(buf
= ast_dynamic_str_thread_get(&manager_event_buf
, MANAGER_EVENT_BUF_INITSIZE
)))
2498 ast_dynamic_str_thread_set(&buf
, 0, &manager_event_buf
,
2499 "Event: %s\r\nPrivilege: %s\r\n",
2500 event
, authority_to_str(category
, auth
, sizeof(auth
)));
2502 if (timestampevents
) {
2504 ast_dynamic_str_thread_append(&buf
, 0, &manager_event_buf
,
2505 "Timestamp: %ld.%06lu\r\n",
2506 now
.tv_sec
, (unsigned long) now
.tv_usec
);
2510 ast_dynamic_str_thread_append_va(&buf
, 0, &manager_event_buf
, fmt
, ap
);
2513 ast_dynamic_str_thread_append(&buf
, 0, &manager_event_buf
, "\r\n");
2515 /* Append event to master list and wake up any sleeping sessions */
2516 AST_LIST_LOCK(&sessions
);
2517 append_event(buf
->str
, category
);
2518 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2519 ast_mutex_lock(&s
->__lock
);
2520 if (s
->waiting_thread
!= AST_PTHREADT_NULL
)
2521 pthread_kill(s
->waiting_thread
, SIGURG
);
2523 /* We have an event to process, but the mansession is
2524 * not waiting for it. We still need to indicate that there
2525 * is an event waiting so that get_input processes the pending
2526 * event instead of polling.
2528 s
->pending_event
= 1;
2529 ast_mutex_unlock(&s
->__lock
);
2531 AST_LIST_UNLOCK(&sessions
);
2536 int ast_manager_unregister(char *action
)
2538 struct manager_action
*cur
, *prev
;
2540 ast_rwlock_wrlock(&actionlock
);
2541 cur
= prev
= first_action
;
2543 if (!strcasecmp(action
, cur
->action
)) {
2544 prev
->next
= cur
->next
;
2546 if (option_verbose
> 1)
2547 ast_verbose(VERBOSE_PREFIX_2
"Manager unregistered action %s\n", action
);
2548 ast_rwlock_unlock(&actionlock
);
2554 ast_rwlock_unlock(&actionlock
);
2558 static int manager_state_cb(char *context
, char *exten
, int state
, void *data
)
2560 /* Notify managers of change */
2561 manager_event(EVENT_FLAG_CALL
, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten
, context
, state
);
2565 static int ast_manager_register_struct(struct manager_action
*act
)
2567 struct manager_action
*cur
, *prev
= NULL
;
2570 ast_rwlock_wrlock(&actionlock
);
2572 while (cur
) { /* Walk the list of actions */
2573 ret
= strcasecmp(cur
->action
, act
->action
);
2575 ast_log(LOG_WARNING
, "Manager: Action '%s' already registered\n", act
->action
);
2576 ast_rwlock_unlock(&actionlock
);
2578 } else if (ret
> 0) {
2579 /* Insert these alphabetically */
2581 act
->next
= prev
->next
;
2584 act
->next
= first_action
;
2601 if (option_verbose
> 1)
2602 ast_verbose(VERBOSE_PREFIX_2
"Manager registered action %s\n", act
->action
);
2603 ast_rwlock_unlock(&actionlock
);
2607 /*! \brief register a new command with manager, including online help. This is
2608 the preferred way to register a manager command */
2609 int ast_manager_register2(const char *action
, int auth
, int (*func
)(struct mansession
*s
, const struct message
*m
), const char *synopsis
, const char *description
)
2611 struct manager_action
*cur
;
2613 cur
= ast_malloc(sizeof(*cur
));
2617 cur
->action
= action
;
2618 cur
->authority
= auth
;
2620 cur
->synopsis
= synopsis
;
2621 cur
->description
= description
;
2624 ast_manager_register_struct(cur
);
2629 END Doxygen group */
2631 static struct mansession
*find_session(uint32_t ident
)
2633 struct mansession
*s
;
2635 AST_LIST_LOCK(&sessions
);
2636 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2637 ast_mutex_lock(&s
->__lock
);
2638 if (s
->sessiontimeout
&& (s
->managerid
== ident
) && !s
->needdestroy
) {
2642 ast_mutex_unlock(&s
->__lock
);
2644 AST_LIST_UNLOCK(&sessions
);
2649 int astman_verify_session_readpermissions(uint32_t ident
, int perm
)
2652 struct mansession
*s
;
2654 AST_LIST_LOCK(&sessions
);
2655 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2656 ast_mutex_lock(&s
->__lock
);
2657 if ((s
->managerid
== ident
) && (s
->readperm
& perm
)) {
2659 ast_mutex_unlock(&s
->__lock
);
2662 ast_mutex_unlock(&s
->__lock
);
2664 AST_LIST_UNLOCK(&sessions
);
2668 int astman_verify_session_writepermissions(uint32_t ident
, int perm
)
2671 struct mansession
*s
;
2673 AST_LIST_LOCK(&sessions
);
2674 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2675 ast_mutex_lock(&s
->__lock
);
2676 if ((s
->managerid
== ident
) && (s
->writeperm
& perm
)) {
2678 ast_mutex_unlock(&s
->__lock
);
2681 ast_mutex_unlock(&s
->__lock
);
2683 AST_LIST_UNLOCK(&sessions
);
2692 static char *contenttype
[] = { "plain", "html", "xml" };
2694 static char *generic_http_callback(int format
, struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2696 struct mansession
*s
= NULL
;
2698 char workspace
[512];
2700 size_t len
= sizeof(workspace
);
2702 char *c
= workspace
;
2703 char *retval
= NULL
;
2704 struct ast_variable
*v
;
2706 for (v
= params
; v
; v
= v
->next
) {
2707 if (!strcasecmp(v
->name
, "mansession_id")) {
2708 sscanf(v
->value
, "%x", &ident
);
2713 if (!(s
= find_session(ident
))) {
2714 /* Create new session */
2715 if (!(s
= ast_calloc(1, sizeof(*s
)))) {
2717 goto generic_callback_out
;
2719 memcpy(&s
->sin
, requestor
, sizeof(s
->sin
));
2721 s
->waiting_thread
= AST_PTHREADT_NULL
;
2723 ast_mutex_init(&s
->__lock
);
2724 ast_mutex_lock(&s
->__lock
);
2726 /*!\note There is approximately a 1 in 1.8E19 chance that the following
2727 * calculation will produce 0, which is an invalid ID, but due to the
2728 * properties of the rand() function (and the constantcy of s), that
2729 * won't happen twice in a row.
2731 while ((s
->managerid
= rand() ^ (unsigned long) s
) == 0);
2732 AST_LIST_LOCK(&sessions
);
2733 AST_LIST_INSERT_HEAD(&sessions
, s
, list
);
2734 /* Hook into the last spot in the event queue */
2735 s
->eventq
= master_eventq
;
2736 while (s
->eventq
->next
)
2737 s
->eventq
= s
->eventq
->next
;
2738 ast_atomic_fetchadd_int(&s
->eventq
->usecount
, 1);
2739 ast_atomic_fetchadd_int(&num_sessions
, 1);
2740 AST_LIST_UNLOCK(&sessions
);
2743 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2744 time(&s
->sessiontimeout
);
2745 if (!s
->authenticated
&& (httptimeout
> 5))
2746 s
->sessiontimeout
+= 5;
2748 s
->sessiontimeout
+= httptimeout
;
2749 ast_mutex_unlock(&s
->__lock
);
2752 struct message m
= { 0 };
2757 for (x
= 0, v
= params
; v
&& (x
< AST_MAX_MANHEADERS
); x
++, v
= v
->next
) {
2758 hdrlen
= strlen(v
->name
) + strlen(v
->value
) + 3;
2759 m
.headers
[m
.hdrcount
] = alloca(hdrlen
);
2760 snprintf((char *) m
.headers
[m
.hdrcount
], hdrlen
, "%s: %s", v
->name
, v
->value
);
2764 if (process_message(s
, &m
)) {
2765 if (s
->authenticated
) {
2766 if (option_verbose
> 1) {
2767 if (displayconnects
)
2768 ast_verbose(VERBOSE_PREFIX_2
"HTTP Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2770 ast_log(LOG_EVENT
, "HTTP Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2772 if (option_verbose
> 1) {
2773 if (displayconnects
)
2774 ast_verbose(VERBOSE_PREFIX_2
"HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2776 ast_log(LOG_EVENT
, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2780 ast_build_string(&c
, &len
, "Content-type: text/%s\r\n", contenttype
[format
]);
2781 sprintf(tmp
, "%08x", s
->managerid
);
2782 ast_build_string(&c
, &len
, "%s\r\n", ast_http_setcookie("mansession_id", tmp
, httptimeout
, cookie
, sizeof(cookie
)));
2783 if (format
== FORMAT_HTML
)
2784 ast_build_string(&c
, &len
, "<title>Asterisk™ Manager Interface</title>");
2785 if (format
== FORMAT_XML
) {
2786 ast_build_string(&c
, &len
, "<ajax-response>\n");
2787 } else if (format
== FORMAT_HTML
) {
2788 ast_build_string(&c
, &len
, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2789 ast_build_string(&c
, &len
, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1> Manager Tester</h1></td></tr>\r\n");
2791 ast_mutex_lock(&s
->__lock
);
2794 if (format
== FORMAT_XML
)
2795 tmp
= xml_translate(s
->outputstr
->str
, params
);
2796 else if (format
== FORMAT_HTML
)
2797 tmp
= html_translate(s
->outputstr
->str
);
2799 tmp
= s
->outputstr
->str
;
2801 retval
= malloc(strlen(workspace
) + strlen(tmp
) + 128);
2803 strcpy(retval
, workspace
);
2804 strcpy(retval
+ strlen(retval
), tmp
);
2805 c
= retval
+ strlen(retval
);
2809 if (tmp
!= s
->outputstr
->str
)
2812 s
->outputstr
= NULL
;
2814 ast_mutex_unlock(&s
->__lock
);
2815 /* Still okay because c would safely be pointing to workspace even
2816 if retval failed to allocate above */
2817 if (format
== FORMAT_XML
) {
2818 ast_build_string(&c
, &len
, "</ajax-response>\n");
2819 } else if (format
== FORMAT_HTML
)
2820 ast_build_string(&c
, &len
, "</table></body>\r\n");
2823 *title
= strdup("Server Error");
2825 ast_mutex_lock(&s
->__lock
);
2826 if (s
->needdestroy
) {
2827 if (s
->inuse
== 1) {
2828 ast_log(LOG_DEBUG
, "Need destroy, doing it now!\n");
2831 ast_log(LOG_DEBUG
, "Need destroy, but can't do it yet!\n");
2832 if (s
->waiting_thread
!= AST_PTHREADT_NULL
)
2833 pthread_kill(s
->waiting_thread
, SIGURG
);
2838 ast_mutex_unlock(&s
->__lock
);
2842 generic_callback_out
:
2844 return ast_http_error(500, "Server Error", NULL
, "Internal Server Error (out of memory)\n");
2848 static char *manager_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2850 return generic_http_callback(FORMAT_HTML
, requestor
, uri
, params
, status
, title
, contentlength
);
2853 static char *mxml_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2855 return generic_http_callback(FORMAT_XML
, requestor
, uri
, params
, status
, title
, contentlength
);
2858 static char *rawman_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2860 return generic_http_callback(FORMAT_RAW
, requestor
, uri
, params
, status
, title
, contentlength
);
2863 struct ast_http_uri rawmanuri
= {
2864 .description
= "Raw HTTP Manager Event Interface",
2867 .callback
= rawman_http_callback
,
2870 struct ast_http_uri manageruri
= {
2871 .description
= "HTML Manager Event Interface",
2874 .callback
= manager_http_callback
,
2877 struct ast_http_uri managerxmluri
= {
2878 .description
= "XML Manager Event Interface",
2881 .callback
= mxml_http_callback
,
2884 static int registered
= 0;
2885 static int webregged
= 0;
2887 int init_manager(void)
2889 struct ast_config
*cfg
= NULL
, *ucfg
= NULL
;
2892 int oldportno
= portno
;
2893 static struct sockaddr_in ba
;
2897 int newhttptimeout
= 60;
2898 struct ast_manager_user
*user
= NULL
;
2901 /* Register default actions */
2902 ast_manager_register2("Ping", 0, action_ping
, "Keepalive command", mandescr_ping
);
2903 ast_manager_register2("Events", 0, action_events
, "Control Event Flow", mandescr_events
);
2904 ast_manager_register2("Logoff", 0, action_logoff
, "Logoff Manager", mandescr_logoff
);
2905 ast_manager_register2("Hangup", EVENT_FLAG_CALL
, action_hangup
, "Hangup Channel", mandescr_hangup
);
2906 ast_manager_register("Status", EVENT_FLAG_CALL
, action_status
, "Lists channel status" );
2907 ast_manager_register2("Setvar", EVENT_FLAG_CALL
, action_setvar
, "Set Channel Variable", mandescr_setvar
);
2908 ast_manager_register2("Getvar", EVENT_FLAG_CALL
, action_getvar
, "Gets a Channel Variable", mandescr_getvar
);
2909 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG
, action_getconfig
, "Retrieve configuration", mandescr_getconfig
);
2910 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG
, action_updateconfig
, "Update basic configuration", mandescr_updateconfig
);
2911 ast_manager_register2("Redirect", EVENT_FLAG_CALL
, action_redirect
, "Redirect (transfer) a call", mandescr_redirect
);
2912 ast_manager_register2("Originate", EVENT_FLAG_CALL
, action_originate
, "Originate Call", mandescr_originate
);
2913 ast_manager_register2("Command", EVENT_FLAG_COMMAND
, action_command
, "Execute Asterisk CLI Command", mandescr_command
);
2914 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL
, action_extensionstate
, "Check Extension Status", mandescr_extensionstate
);
2915 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL
, action_timeout
, "Set Absolute Timeout", mandescr_timeout
);
2916 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL
, action_mailboxstatus
, "Check Mailbox", mandescr_mailboxstatus
);
2917 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL
, action_mailboxcount
, "Check Mailbox Message Count", mandescr_mailboxcount
);
2918 ast_manager_register2("ListCommands", 0, action_listcommands
, "List available manager commands", mandescr_listcommands
);
2919 ast_manager_register2("UserEvent", EVENT_FLAG_USER
, action_userevent
, "Send an arbitrary event", mandescr_userevent
);
2920 ast_manager_register2("WaitEvent", 0, action_waitevent
, "Wait for an event to occur", mandescr_waitevent
);
2922 ast_cli_register_multiple(cli_manager
, sizeof(cli_manager
) / sizeof(struct ast_cli_entry
));
2923 ast_extension_state_add(NULL
, NULL
, manager_state_cb
, NULL
);
2925 /* Append placeholder event so master_eventq never runs dry */
2926 append_event("Event: Placeholder\r\n\r\n", 0);
2928 portno
= DEFAULT_MANAGER_PORT
;
2929 displayconnects
= 1;
2930 cfg
= ast_config_load("manager.conf");
2932 ast_log(LOG_NOTICE
, "Unable to open management configuration manager.conf. Call management disabled.\n");
2935 val
= ast_variable_retrieve(cfg
, "general", "enabled");
2937 enabled
= ast_true(val
);
2939 val
= ast_variable_retrieve(cfg
, "general", "block-sockets");
2941 block_sockets
= ast_true(val
);
2943 val
= ast_variable_retrieve(cfg
, "general", "webenabled");
2945 webenabled
= ast_true(val
);
2947 if ((val
= ast_variable_retrieve(cfg
, "general", "port"))) {
2948 if (sscanf(val
, "%d", &portno
) != 1) {
2949 ast_log(LOG_WARNING
, "Invalid port number '%s'\n", val
);
2950 portno
= DEFAULT_MANAGER_PORT
;
2954 if ((val
= ast_variable_retrieve(cfg
, "general", "displayconnects")))
2955 displayconnects
= ast_true(val
);
2957 if ((val
= ast_variable_retrieve(cfg
, "general", "timestampevents")))
2958 timestampevents
= ast_true(val
);
2960 if ((val
= ast_variable_retrieve(cfg
, "general", "httptimeout")))
2961 newhttptimeout
= atoi(val
);
2963 memset(&ba
, 0, sizeof(ba
));
2964 ba
.sin_family
= AF_INET
;
2965 ba
.sin_port
= htons(portno
);
2967 if ((val
= ast_variable_retrieve(cfg
, "general", "bindaddr"))) {
2968 if (!inet_aton(val
, &ba
.sin_addr
)) {
2969 ast_log(LOG_WARNING
, "Invalid address '%s' specified, using 0.0.0.0\n", val
);
2970 memset(&ba
.sin_addr
, 0, sizeof(ba
.sin_addr
));
2975 if ((asock
> -1) && ((portno
!= oldportno
) || !enabled
)) {
2977 /* Can't be done yet */
2981 ast_log(LOG_WARNING
, "Unable to change management port / enabled\n");
2985 AST_LIST_LOCK(&users
);
2987 if ((ucfg
= ast_config_load("users.conf"))) {
2988 while ((cat
= ast_category_browse(ucfg
, cat
))) {
2990 struct ast_variable
*var
= NULL
;
2992 if (!strcasecmp(cat
, "general")) {
2996 if (!(hasmanager
= ast_true(ast_variable_retrieve(ucfg
, cat
, "hasmanager")))) {
3000 /* Look for an existing entry, if none found - create one and add it to the list */
3001 if (!(user
= ast_get_manager_by_name_locked(cat
))) {
3002 if (!(user
= ast_calloc(1, sizeof(*user
)))) {
3005 /* Copy name over */
3006 ast_copy_string(user
->username
, cat
, sizeof(user
->username
));
3007 /* Insert into list */
3008 AST_LIST_INSERT_TAIL(&users
, user
, list
);
3011 /* Make sure we keep this user and don't destroy it during cleanup */
3014 for (var
= ast_variable_browse(ucfg
, cat
); var
; var
= var
->next
) {
3015 if (!strcasecmp(var
->name
, "secret")) {
3019 user
->secret
= ast_strdup(var
->value
);
3020 } else if (!strcasecmp(var
->name
, "deny") ) {
3024 user
->deny
= ast_strdup(var
->value
);
3025 } else if (!strcasecmp(var
->name
, "permit") ) {
3029 user
->permit
= ast_strdup(var
->value
);
3030 } else if (!strcasecmp(var
->name
, "read") ) {
3034 user
->read
= ast_strdup(var
->value
);
3035 } else if (!strcasecmp(var
->name
, "write") ) {
3039 user
->write
= ast_strdup(var
->value
);
3040 } else if (!strcasecmp(var
->name
, "displayconnects") ) {
3041 user
->displayconnects
= ast_true(var
->value
);
3042 } else if (!strcasecmp(var
->name
, "hasmanager")) {
3043 /* already handled */
3045 ast_log(LOG_DEBUG
, "%s is an unknown option (to the manager module).\n", var
->name
);
3049 ast_config_destroy(ucfg
);
3052 while ((cat
= ast_category_browse(cfg
, cat
))) {
3053 struct ast_variable
*var
= NULL
;
3055 if (!strcasecmp(cat
, "general"))
3058 /* Look for an existing entry, if none found - create one and add it to the list */
3059 if (!(user
= ast_get_manager_by_name_locked(cat
))) {
3060 if (!(user
= ast_calloc(1, sizeof(*user
))))
3062 /* Copy name over */
3063 ast_copy_string(user
->username
, cat
, sizeof(user
->username
));
3064 /* Insert into list */
3065 AST_LIST_INSERT_TAIL(&users
, user
, list
);
3068 /* Make sure we keep this user and don't destroy it during cleanup */
3071 var
= ast_variable_browse(cfg
, cat
);
3073 if (!strcasecmp(var
->name
, "secret")) {
3076 user
->secret
= ast_strdup(var
->value
);
3077 } else if (!strcasecmp(var
->name
, "deny") ) {
3080 user
->deny
= ast_strdup(var
->value
);
3081 } else if (!strcasecmp(var
->name
, "permit") ) {
3084 user
->permit
= ast_strdup(var
->value
);
3085 } else if (!strcasecmp(var
->name
, "read") ) {
3088 user
->read
= ast_strdup(var
->value
);
3089 } else if (!strcasecmp(var
->name
, "write") ) {
3092 user
->write
= ast_strdup(var
->value
);
3093 } else if (!strcasecmp(var
->name
, "displayconnects") )
3094 user
->displayconnects
= ast_true(var
->value
);
3096 ast_log(LOG_DEBUG
, "%s is an unknown option.\n", var
->name
);
3101 /* Perform cleanup - essentially prune out old users that no longer exist */
3102 AST_LIST_TRAVERSE_SAFE_BEGIN(&users
, user
, list
) {
3107 /* We do not need to keep this user so take them out of the list */
3108 AST_LIST_REMOVE_CURRENT(&users
, list
);
3109 /* Free their memory now */
3122 AST_LIST_TRAVERSE_SAFE_END
3124 AST_LIST_UNLOCK(&users
);
3126 ast_config_destroy(cfg
);
3128 if (webenabled
&& enabled
) {
3130 ast_http_uri_link(&rawmanuri
);
3131 ast_http_uri_link(&manageruri
);
3132 ast_http_uri_link(&managerxmluri
);
3137 ast_http_uri_unlink(&rawmanuri
);
3138 ast_http_uri_unlink(&manageruri
);
3139 ast_http_uri_unlink(&managerxmluri
);
3144 if (newhttptimeout
> 0)
3145 httptimeout
= newhttptimeout
;
3147 /* If not enabled, do nothing */
3152 asock
= socket(AF_INET
, SOCK_STREAM
, 0);
3154 ast_log(LOG_WARNING
, "Unable to create socket: %s\n", strerror(errno
));
3157 setsockopt(asock
, SOL_SOCKET
, SO_REUSEADDR
, &x
, sizeof(x
));
3158 if (bind(asock
, (struct sockaddr
*)&ba
, sizeof(ba
))) {
3159 ast_log(LOG_WARNING
, "Unable to bind socket: %s\n", strerror(errno
));
3164 if (listen(asock
, 2)) {
3165 ast_log(LOG_WARNING
, "Unable to listen on socket: %s\n", strerror(errno
));
3170 flags
= fcntl(asock
, F_GETFL
);
3171 fcntl(asock
, F_SETFL
, flags
| O_NONBLOCK
);
3173 ast_verbose("Asterisk Management interface listening on port %d\n", portno
);
3174 ast_pthread_create_background(&t
, NULL
, accept_thread
, NULL
);
3179 int reload_manager(void)
3181 manager_event(EVENT_FLAG_SYSTEM
, "Reload", "Message: Reload Requested\r\n");
3182 return init_manager();