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 static const char *command_blacklist
[] = {
139 /*! Execution thread */
141 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
143 /*! socket address */
144 struct sockaddr_in sin
;
147 /*! Whether an HTTP manager is in use */
149 /*! Whether an HTTP session should be destroyed */
151 /*! Whether an HTTP session has someone waiting on events */
152 pthread_t waiting_thread
;
153 /*! Unique manager identifer */
154 unsigned long managerid
;
155 /*! Session timeout if HTTP */
156 time_t sessiontimeout
;
157 /*! Output from manager interface */
158 struct ast_dynamic_str
*outputstr
;
159 /*! Logged in username */
161 /*! Authentication challenge */
163 /*! Authentication status */
165 /*! Authorization for reading */
167 /*! Authorization for writing */
173 int displaysystemname
; /*!< Add system name to manager responses and events */
174 /* Queued events that we've not had the ability to send yet */
175 struct eventqent
*eventq
;
176 /* Timeout for ast_carefulwrite() */
178 AST_LIST_ENTRY(mansession
) list
;
181 static AST_LIST_HEAD_STATIC(sessions
, mansession
);
183 struct ast_manager_user
{
190 unsigned int displayconnects
:1;
192 AST_LIST_ENTRY(ast_manager_user
) list
;
195 static AST_LIST_HEAD_STATIC(users
, ast_manager_user
);
197 static struct manager_action
*first_action
;
198 AST_RWLOCK_DEFINE_STATIC(actionlock
);
200 /*! \brief Convert authority code to string with serveral options */
201 static char *authority_to_str(int authority
, char *res
, int reslen
)
203 int running_total
= 0, i
;
205 memset(res
, 0, reslen
);
206 for (i
= 0; i
< (sizeof(perms
) / sizeof(perms
[0])) - 1; i
++) {
207 if (authority
& perms
[i
].num
) {
209 strncat(res
, ",", (reslen
> running_total
) ? reslen
- running_total
: 0);
212 strncat(res
, perms
[i
].label
, (reslen
> running_total
) ? reslen
- running_total
: 0);
213 running_total
+= strlen(perms
[i
].label
);
217 if (ast_strlen_zero(res
))
218 ast_copy_string(res
, "<none>", reslen
);
223 static char *complete_show_mancmd(const char *line
, const char *word
, int pos
, int state
)
225 struct manager_action
*cur
;
229 ast_rwlock_rdlock(&actionlock
);
230 for (cur
= first_action
; cur
; cur
= cur
->next
) { /* Walk the list of actions */
231 if (!strncasecmp(word
, cur
->action
, strlen(word
)) && ++which
> state
) {
232 ret
= ast_strdup(cur
->action
);
233 break; /* make sure we exit even if ast_strdup() returns NULL */
236 ast_rwlock_unlock(&actionlock
);
241 static void xml_copy_escape(char **dst
, size_t *maxlen
, const char *src
, int lower
)
243 while (*src
&& (*maxlen
> 6)) {
246 strcpy(*dst
, "<");
251 strcpy(*dst
, ">");
256 strcpy(*dst
, """);
261 strcpy(*dst
, "'");
266 strcpy(*dst
, "&");
271 *(*dst
)++ = lower
? tolower(*src
) : *src
;
278 struct variable_count
{
283 static int compress_char(char c
)
288 else if (c
>= 'a' && c
<= 'z')
296 static int variable_count_hash_fn(const void *vvc
, const int flags
)
298 const struct variable_count
*vc
= vvc
;
300 for (i
= 0; i
< 5; i
++) {
301 if (vc
->varname
[i
] == '\0')
303 res
+= compress_char(vc
->varname
[i
]) << (i
* 6);
308 static int variable_count_cmp_fn(void *obj
, void *vstr
, int flags
)
310 /* Due to the simplicity of struct variable_count, it makes no difference
311 * if you pass in objects or strings, the same operation applies. This is
312 * due to the fact that the hash occurs on the first element, which means
313 * the address of both the struct and the string are exactly the same. */
314 struct variable_count
*vc
= obj
;
316 return !strcmp(vc
->varname
, str
) ? CMP_MATCH
: 0;
319 static char *xml_translate(char *in
, struct ast_variable
*vars
)
321 struct ast_variable
*v
;
323 char *out
, *tmp
, *var
, *val
;
324 char *objtype
= NULL
;
332 struct variable_count
*vc
= NULL
;
333 struct ao2_container
*vco
= NULL
;
335 for (v
= vars
; v
; v
= v
->next
) {
336 if (!dest
&& !strcasecmp(v
->name
, "ajaxdest"))
338 else if (!objtype
&& !strcasecmp(v
->name
, "ajaxobjtype"))
345 for (x
= 0; in
[x
]; x
++) {
348 else if (in
[x
] == '\n')
350 else if (strchr("&\"<>\'", in
[x
]))
353 len
= (size_t) (strlen(in
) + colons
* 5 + breaks
* (40 + strlen(dest
) + strlen(objtype
)) + escaped
* 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&" */
354 out
= ast_malloc(len
);
360 while (*in
&& (*in
>= 32))
363 if ((count
> 3) && inobj
) {
364 ast_build_string(&tmp
, &len
, " /></response>\n");
367 /* Entity is closed, so close out the name cache */
372 while (*in
&& (*in
< 32)) {
377 val
= strchr(var
, ':');
384 vco
= ao2_container_alloc(37, variable_count_hash_fn
, variable_count_cmp_fn
);
385 ast_build_string(&tmp
, &len
, "<response type='object' id='%s'><%s", dest
, objtype
);
389 /* Check if the var has been used already */
390 if ((vc
= ao2_find(vco
, var
, 0)))
393 /* Create a new entry for this one */
394 vc
= ao2_alloc(sizeof(*vc
), NULL
);
400 ast_build_string(&tmp
, &len
, " ");
401 xml_copy_escape(&tmp
, &len
, var
, 1);
403 ast_build_string(&tmp
, &len
, "-%d", vc
->count
);
404 ast_build_string(&tmp
, &len
, "='");
405 xml_copy_escape(&tmp
, &len
, val
, 0);
406 ast_build_string(&tmp
, &len
, "'");
412 ast_build_string(&tmp
, &len
, " /></response>\n");
418 static char *html_translate(char *in
)
425 char *tmp
, *var
, *val
, *out
;
427 for (x
=0; in
[x
]; x
++) {
433 len
= strlen(in
) + colons
* 40 + breaks
* 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
434 out
= ast_malloc(len
);
440 while (*in
&& (*in
>= 32))
443 if ((count
% 4) == 0){
444 ast_build_string(&tmp
, &len
, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
447 while (*in
&& (*in
< 32)) {
452 val
= strchr(var
, ':');
458 ast_build_string(&tmp
, &len
, "<tr><td>%s</td><td>%s</td></tr>\r\n", var
, val
);
467 static struct ast_manager_user
*ast_get_manager_by_name_locked(const char *name
)
469 struct ast_manager_user
*user
= NULL
;
471 AST_LIST_TRAVERSE(&users
, user
, list
)
472 if (!strcasecmp(user
->username
, name
))
477 void astman_append(struct mansession
*s
, const char *fmt
, ...)
480 struct ast_dynamic_str
*buf
;
482 ast_mutex_lock(&s
->__lock
);
484 if (!(buf
= ast_dynamic_str_thread_get(&astman_append_buf
, ASTMAN_APPEND_BUF_INITSIZE
))) {
485 ast_mutex_unlock(&s
->__lock
);
490 ast_dynamic_str_thread_set_va(&buf
, 0, &astman_append_buf
, fmt
, ap
);
494 ast_carefulwrite(s
->fd
, buf
->str
, strlen(buf
->str
), s
->writetimeout
);
496 if (!s
->outputstr
&& !(s
->outputstr
= ast_calloc(1, sizeof(*s
->outputstr
)))) {
497 ast_mutex_unlock(&s
->__lock
);
501 ast_dynamic_str_append(&s
->outputstr
, 0, "%s", buf
->str
);
504 ast_mutex_unlock(&s
->__lock
);
507 static int handle_showmancmd(int fd
, int argc
, char *argv
[])
509 struct manager_action
*cur
;
514 return RESULT_SHOWUSAGE
;
516 ast_rwlock_rdlock(&actionlock
);
517 for (cur
= first_action
; cur
; cur
= cur
->next
) { /* Walk the list of actions */
518 for (num
= 3; num
< argc
; num
++) {
519 if (!strcasecmp(cur
->action
, argv
[num
])) {
520 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
: "");
524 ast_rwlock_unlock(&actionlock
);
526 return RESULT_SUCCESS
;
529 static int handle_showmanager(int fd
, int argc
, char *argv
[])
531 struct ast_manager_user
*user
= NULL
;
534 return RESULT_SHOWUSAGE
;
536 AST_LIST_LOCK(&users
);
538 if (!(user
= ast_get_manager_by_name_locked(argv
[3]))) {
539 ast_cli(fd
, "There is no manager called %s\n", argv
[3]);
540 AST_LIST_UNLOCK(&users
);
552 "displayconnects: %s\n",
553 (user
->username
? user
->username
: "(N/A)"),
554 (user
->secret
? "<Set>" : "(N/A)"),
555 (user
->deny
? user
->deny
: "(N/A)"),
556 (user
->permit
? user
->permit
: "(N/A)"),
557 (user
->read
? user
->read
: "(N/A)"),
558 (user
->write
? user
->write
: "(N/A)"),
559 (user
->displayconnects
? "yes" : "no"));
561 AST_LIST_UNLOCK(&users
);
563 return RESULT_SUCCESS
;
567 static int handle_showmanagers(int fd
, int argc
, char *argv
[])
569 struct ast_manager_user
*user
= NULL
;
573 return RESULT_SHOWUSAGE
;
575 AST_LIST_LOCK(&users
);
577 /* If there are no users, print out something along those lines */
578 if (AST_LIST_EMPTY(&users
)) {
579 ast_cli(fd
, "There are no manager users.\n");
580 AST_LIST_UNLOCK(&users
);
581 return RESULT_SUCCESS
;
584 ast_cli(fd
, "\nusername\n--------\n");
586 AST_LIST_TRAVERSE(&users
, user
, list
) {
587 ast_cli(fd
, "%s\n", user
->username
);
591 AST_LIST_UNLOCK(&users
);
593 ast_cli(fd
,"-------------------\n");
594 ast_cli(fd
,"%d manager users configured.\n", count_amu
);
596 return RESULT_SUCCESS
;
600 /*! \brief CLI command
601 Should change to "manager show commands" */
602 static int handle_showmancmds(int fd
, int argc
, char *argv
[])
604 struct manager_action
*cur
;
606 char *format
= " %-15.15s %-15.15s %-55.55s\n";
608 ast_cli(fd
, format
, "Action", "Privilege", "Synopsis");
609 ast_cli(fd
, format
, "------", "---------", "--------");
611 ast_rwlock_rdlock(&actionlock
);
612 for (cur
= first_action
; cur
; cur
= cur
->next
) /* Walk the list of actions */
613 ast_cli(fd
, format
, cur
->action
, authority_to_str(cur
->authority
, authority
, sizeof(authority
) -1), cur
->synopsis
);
614 ast_rwlock_unlock(&actionlock
);
616 return RESULT_SUCCESS
;
619 /*! \brief CLI command show manager connected */
620 /* Should change to "manager show connected" */
621 static int handle_showmanconn(int fd
, int argc
, char *argv
[])
623 struct mansession
*s
;
624 char *format
= " %-15.15s %-15.15s\n";
626 ast_cli(fd
, format
, "Username", "IP Address");
628 AST_LIST_LOCK(&sessions
);
629 AST_LIST_TRAVERSE(&sessions
, s
, list
)
630 ast_cli(fd
, format
,s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
631 AST_LIST_UNLOCK(&sessions
);
633 return RESULT_SUCCESS
;
636 /*! \brief CLI command show manager connected */
637 /* Should change to "manager show connected" */
638 static int handle_showmaneventq(int fd
, int argc
, char *argv
[])
642 AST_LIST_LOCK(&sessions
);
643 for (s
= master_eventq
; s
; s
= s
->next
) {
644 ast_cli(fd
, "Usecount: %d\n",s
->usecount
);
645 ast_cli(fd
, "Category: %d\n", s
->category
);
646 ast_cli(fd
, "Event:\n%s", s
->eventdata
);
648 AST_LIST_UNLOCK(&sessions
);
650 return RESULT_SUCCESS
;
653 static char showmancmd_help
[] =
654 "Usage: manager show command <actionname>\n"
655 " Shows the detailed description for a specific Asterisk manager interface command.\n";
657 static char showmancmds_help
[] =
658 "Usage: manager show commands\n"
659 " Prints a listing of all the available Asterisk manager interface commands.\n";
661 static char showmanconn_help
[] =
662 "Usage: manager show connected\n"
663 " Prints a listing of the users that are currently connected to the\n"
664 "Asterisk manager interface.\n";
666 static char showmaneventq_help
[] =
667 "Usage: manager show eventq\n"
668 " Prints a listing of all events pending in the Asterisk manger\n"
671 static char showmanagers_help
[] =
672 "Usage: manager show users\n"
673 " Prints a listing of all managers that are currently configured on that\n"
676 static char showmanager_help
[] =
677 " Usage: manager show user <user>\n"
678 " Display all information related to the manager user specified.\n";
680 static struct ast_cli_entry cli_show_manager_command_deprecated
= {
681 { "show", "manager", "command", NULL
},
682 handle_showmancmd
, NULL
,
683 NULL
, complete_show_mancmd
};
685 static struct ast_cli_entry cli_show_manager_commands_deprecated
= {
686 { "show", "manager", "commands", NULL
},
687 handle_showmancmds
, NULL
,
690 static struct ast_cli_entry cli_show_manager_connected_deprecated
= {
691 { "show", "manager", "connected", NULL
},
692 handle_showmanconn
, NULL
,
695 static struct ast_cli_entry cli_show_manager_eventq_deprecated
= {
696 { "show", "manager", "eventq", NULL
},
697 handle_showmaneventq
, NULL
,
700 static struct ast_cli_entry cli_manager
[] = {
701 { { "manager", "show", "command", NULL
},
702 handle_showmancmd
, "Show a manager interface command",
703 showmancmd_help
, complete_show_mancmd
, &cli_show_manager_command_deprecated
},
705 { { "manager", "show", "commands", NULL
},
706 handle_showmancmds
, "List manager interface commands",
707 showmancmds_help
, NULL
, &cli_show_manager_commands_deprecated
},
709 { { "manager", "show", "connected", NULL
},
710 handle_showmanconn
, "List connected manager interface users",
711 showmanconn_help
, NULL
, &cli_show_manager_connected_deprecated
},
713 { { "manager", "show", "eventq", NULL
},
714 handle_showmaneventq
, "List manager interface queued events",
715 showmaneventq_help
, NULL
, &cli_show_manager_eventq_deprecated
},
717 { { "manager", "show", "users", NULL
},
718 handle_showmanagers
, "List configured manager users",
719 showmanagers_help
, NULL
, NULL
},
721 { { "manager", "show", "user", NULL
},
722 handle_showmanager
, "Display information on a specific manager user",
723 showmanager_help
, NULL
, NULL
},
726 static void unuse_eventqent(struct eventqent
*e
)
728 if (ast_atomic_dec_and_test(&e
->usecount
) && e
->next
)
729 pthread_kill(t
, SIGURG
);
732 static void free_session(struct mansession
*s
)
734 struct eventqent
*eqe
;
739 ast_mutex_destroy(&s
->__lock
);
742 s
->eventq
= s
->eventq
->next
;
743 unuse_eventqent(eqe
);
748 static void destroy_session(struct mansession
*s
)
750 AST_LIST_LOCK(&sessions
);
751 AST_LIST_REMOVE(&sessions
, s
, list
);
754 AST_LIST_UNLOCK(&sessions
);
757 const char *astman_get_header(const struct message
*m
, char *var
)
762 snprintf(cmp
, sizeof(cmp
), "%s: ", var
);
764 for (x
= 0; x
< m
->hdrcount
; x
++) {
765 if (!strncasecmp(cmp
, m
->headers
[x
], strlen(cmp
)))
766 return m
->headers
[x
] + strlen(cmp
);
772 struct ast_variable
*astman_get_variables(const struct message
*m
)
775 struct ast_variable
*head
= NULL
, *cur
;
779 AST_DECLARE_APP_ARGS(args
,
780 AST_APP_ARG(vars
)[32];
783 varlen
= strlen("Variable: ");
785 for (x
= 0; x
< m
->hdrcount
; x
++) {
786 if (strncasecmp("Variable: ", m
->headers
[x
], varlen
))
789 parse
= ast_strdupa(m
->headers
[x
] + varlen
);
791 AST_STANDARD_APP_ARGS(args
, parse
);
793 for (y
= 0; y
< args
.argc
; y
++) {
796 var
= val
= ast_strdupa(args
.vars
[y
]);
798 if (!val
|| ast_strlen_zero(var
))
800 cur
= ast_variable_new(var
, val
);
814 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
815 hold the session lock _or_ be running in an action callback (in which case s->busy will
816 be non-zero). In either of these cases, there is no need to lock-protect the session's
817 fd, since no other output will be sent (events will be queued), and no input will
818 be read until either the current action finishes or get_input() obtains the session
821 void astman_send_error(struct mansession
*s
, const struct message
*m
, char *error
)
823 const char *id
= astman_get_header(m
,"ActionID");
825 astman_append(s
, "Response: Error\r\n");
826 if (!ast_strlen_zero(id
))
827 astman_append(s
, "ActionID: %s\r\n", id
);
828 astman_append(s
, "Message: %s\r\n\r\n", error
);
831 void astman_send_response(struct mansession
*s
, const struct message
*m
, char *resp
, char *msg
)
833 const char *id
= astman_get_header(m
,"ActionID");
835 astman_append(s
, "Response: %s\r\n", resp
);
836 if (!ast_strlen_zero(id
))
837 astman_append(s
, "ActionID: %s\r\n", id
);
839 astman_append(s
, "Message: %s\r\n\r\n", msg
);
841 astman_append(s
, "\r\n");
844 void astman_send_ack(struct mansession
*s
, const struct message
*m
, char *msg
)
846 astman_send_response(s
, m
, "Success", msg
);
849 /*! Tells you if smallstr exists inside bigstr
850 which is delim by delim and uses no buf or stringsep
851 ast_instring("this|that|more","this",',') == 1;
853 feel free to move this to app.c -anthm */
854 static int ast_instring(const char *bigstr
, const char *smallstr
, char delim
)
856 const char *val
= bigstr
, *next
;
859 if ((next
= strchr(val
, delim
))) {
860 if (!strncmp(val
, smallstr
, (next
- val
)))
865 return !strcmp(smallstr
, val
);
867 } while (*(val
= (next
+ 1)));
872 static int get_perm(const char *instr
)
879 for (x
= 0; x
< (sizeof(perms
) / sizeof(perms
[0])); x
++) {
880 if (ast_instring(instr
, perms
[x
].label
, ','))
887 static int ast_is_number(const char *string
)
894 for (x
= 0; x
< strlen(string
); x
++) {
895 if (!(string
[x
] >= 48 && string
[x
] <= 57)) {
901 return ret
? atoi(string
) : 0;
904 static int strings_to_mask(const char *string
)
908 x
= ast_is_number(string
);
912 else if (ast_strlen_zero(string
))
914 else if (ast_false(string
))
916 else if (ast_true(string
)) {
918 for (x
=0; x
<sizeof(perms
) / sizeof(perms
[0]); x
++)
922 for (x
=0; x
<sizeof(perms
) / sizeof(perms
[0]); x
++) {
923 if (ast_instring(string
, perms
[x
].label
, ','))
932 Rather than braindead on,off this now can also accept a specific int mask value
933 or a ',' delim list of mask strings (the same as manager.conf) -anthm
935 static int set_eventmask(struct mansession
*s
, const char *eventmask
)
937 int maskint
= strings_to_mask(eventmask
);
939 ast_mutex_lock(&s
->__lock
);
941 s
->send_events
= maskint
;
942 ast_mutex_unlock(&s
->__lock
);
947 static int authenticate(struct mansession
*s
, const struct message
*m
)
949 struct ast_config
*cfg
;
951 const char *user
= astman_get_header(m
, "Username");
952 const char *pass
= astman_get_header(m
, "Secret");
953 const char *authtype
= astman_get_header(m
, "AuthType");
954 const char *key
= astman_get_header(m
, "Key");
955 const char *events
= astman_get_header(m
, "Events");
957 cfg
= ast_config_load("manager.conf");
960 cat
= ast_category_browse(cfg
, NULL
);
962 if (strcasecmp(cat
, "general")) {
964 if (!strcasecmp(cat
, user
)) {
965 struct ast_variable
*v
;
966 struct ast_ha
*ha
= NULL
;
967 char *password
= NULL
;
969 for (v
= ast_variable_browse(cfg
, cat
); v
; v
= v
->next
) {
970 if (!strcasecmp(v
->name
, "secret")) {
972 } else if (!strcasecmp(v
->name
, "displaysystemname")) {
973 if (ast_true(v
->value
)) {
974 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME
)) {
975 s
->displaysystemname
= 1;
977 ast_log(LOG_ERROR
, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
980 } else if (!strcasecmp(v
->name
, "permit") ||
981 !strcasecmp(v
->name
, "deny")) {
982 ha
= ast_append_ha(v
->name
, v
->value
, ha
);
983 } else if (!strcasecmp(v
->name
, "writetimeout")) {
984 int val
= atoi(v
->value
);
987 ast_log(LOG_WARNING
, "Invalid writetimeout value '%s' at line %d\n", v
->value
, v
->lineno
);
989 s
->writetimeout
= val
;
993 if (ha
&& !ast_apply_ha(ha
, &(s
->sin
))) {
994 ast_log(LOG_NOTICE
, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
996 ast_config_destroy(cfg
);
1000 if (!strcasecmp(authtype
, "MD5")) {
1001 if (!ast_strlen_zero(key
) &&
1002 !ast_strlen_zero(s
->challenge
) && !ast_strlen_zero(password
)) {
1005 char md5key
[256] = "";
1006 struct MD5Context md5
;
1007 unsigned char digest
[16];
1009 MD5Update(&md5
, (unsigned char *) s
->challenge
, strlen(s
->challenge
));
1010 MD5Update(&md5
, (unsigned char *) password
, strlen(password
));
1011 MD5Final(digest
, &md5
);
1012 for (x
=0; x
<16; x
++)
1013 len
+= sprintf(md5key
+ len
, "%2.2x", digest
[x
]);
1014 if (!strcmp(md5key
, key
))
1017 ast_config_destroy(cfg
);
1021 ast_log(LOG_DEBUG
, "MD5 authentication is not possible. challenge: '%s'\n",
1022 S_OR(s
->challenge
, ""));
1023 ast_config_destroy(cfg
);
1026 } else if (password
&& !strcmp(password
, pass
)) {
1029 ast_log(LOG_NOTICE
, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1030 ast_config_destroy(cfg
);
1035 cat
= ast_category_browse(cfg
, cat
);
1038 ast_copy_string(s
->username
, cat
, sizeof(s
->username
));
1039 s
->readperm
= get_perm(ast_variable_retrieve(cfg
, cat
, "read"));
1040 s
->writeperm
= get_perm(ast_variable_retrieve(cfg
, cat
, "write"));
1041 ast_config_destroy(cfg
);
1043 set_eventmask(s
, events
);
1046 ast_config_destroy(cfg
);
1047 cfg
= ast_config_load("users.conf");
1050 cat
= ast_category_browse(cfg
, NULL
);
1052 struct ast_variable
*v
;
1053 const char *password
= NULL
;
1055 const char *readperms
= NULL
;
1056 const char *writeperms
= NULL
;
1058 if (strcasecmp(cat
, user
) || !strcasecmp(cat
, "general")) {
1059 cat
= ast_category_browse(cfg
, cat
);
1062 for (v
= ast_variable_browse(cfg
, cat
); v
; v
= v
->next
) {
1063 if (!strcasecmp(v
->name
, "secret"))
1064 password
= v
->value
;
1065 else if (!strcasecmp(v
->name
, "hasmanager"))
1066 hasmanager
= ast_true(v
->value
);
1067 else if (!strcasecmp(v
->name
, "managerread"))
1068 readperms
= v
->value
;
1069 else if (!strcasecmp(v
->name
, "managerwrite"))
1070 writeperms
= v
->value
;
1074 if (!password
|| strcmp(password
, pass
)) {
1075 ast_log(LOG_NOTICE
, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1076 ast_config_destroy(cfg
);
1079 ast_copy_string(s
->username
, cat
, sizeof(s
->username
));
1080 s
->readperm
= readperms
? get_perm(readperms
) : -1;
1081 s
->writeperm
= writeperms
? get_perm(writeperms
) : -1;
1082 ast_config_destroy(cfg
);
1084 set_eventmask(s
, events
);
1087 ast_log(LOG_NOTICE
, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
1088 ast_config_destroy(cfg
);
1092 /*! \brief Manager PING */
1093 static char mandescr_ping
[] =
1094 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1095 " manager connection open.\n"
1096 "Variables: NONE\n";
1098 static int action_ping(struct mansession
*s
, const struct message
*m
)
1100 astman_send_response(s
, m
, "Pong", NULL
);
1104 static char mandescr_getconfig
[] =
1105 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1106 "file by category and contents.\n"
1108 " Filename: Configuration filename (e.g. foo.conf)\n";
1110 static int action_getconfig(struct mansession
*s
, const struct message
*m
)
1112 struct ast_config
*cfg
;
1113 const char *fn
= astman_get_header(m
, "Filename");
1116 char *category
=NULL
;
1117 struct ast_variable
*v
;
1118 char idText
[256] = "";
1119 const char *id
= astman_get_header(m
, "ActionID");
1121 if (!ast_strlen_zero(id
))
1122 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1124 if (ast_strlen_zero(fn
)) {
1125 astman_send_error(s
, m
, "Filename not specified");
1128 if (!(cfg
= ast_config_load_with_comments(fn
))) {
1129 astman_send_error(s
, m
, "Config file not found");
1132 astman_append(s
, "Response: Success\r\n%s", idText
);
1133 while ((category
= ast_category_browse(cfg
, category
))) {
1135 astman_append(s
, "Category-%06d: %s\r\n", catcount
, category
);
1136 for (v
= ast_variable_browse(cfg
, category
); v
; v
= v
->next
)
1137 astman_append(s
, "Line-%06d-%06d: %s=%s\r\n", catcount
, lineno
++, v
->name
, v
->value
);
1140 ast_config_destroy(cfg
);
1141 astman_append(s
, "\r\n");
1147 static void handle_updates(struct mansession
*s
, const struct message
*m
, struct ast_config
*cfg
)
1151 const char *action
, *cat
, *var
, *value
, *match
;
1152 struct ast_category
*category
;
1153 struct ast_variable
*v
;
1155 for (x
=0;x
<100000;x
++) {
1156 unsigned int object
= 0;
1158 snprintf(hdr
, sizeof(hdr
), "Action-%06d", x
);
1159 action
= astman_get_header(m
, hdr
);
1160 if (ast_strlen_zero(action
))
1162 snprintf(hdr
, sizeof(hdr
), "Cat-%06d", x
);
1163 cat
= astman_get_header(m
, hdr
);
1164 snprintf(hdr
, sizeof(hdr
), "Var-%06d", x
);
1165 var
= astman_get_header(m
, hdr
);
1166 snprintf(hdr
, sizeof(hdr
), "Value-%06d", x
);
1167 value
= astman_get_header(m
, hdr
);
1168 if (!ast_strlen_zero(value
) && *value
== '>') {
1172 snprintf(hdr
, sizeof(hdr
), "Match-%06d", x
);
1173 match
= astman_get_header(m
, hdr
);
1174 if (!strcasecmp(action
, "newcat")) {
1175 if (!ast_strlen_zero(cat
)) {
1176 category
= ast_category_new(cat
);
1178 ast_category_append(cfg
, category
);
1181 } else if (!strcasecmp(action
, "renamecat")) {
1182 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(value
)) {
1183 category
= ast_category_get(cfg
, cat
);
1185 ast_category_rename(category
, value
);
1187 } else if (!strcasecmp(action
, "delcat")) {
1188 if (!ast_strlen_zero(cat
))
1189 ast_category_delete(cfg
, (char *) cat
);
1190 } else if (!strcasecmp(action
, "update")) {
1191 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) && (category
= ast_category_get(cfg
, cat
)))
1192 ast_variable_update(category
, var
, value
, match
, object
);
1193 } else if (!strcasecmp(action
, "delete")) {
1194 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) && (category
= ast_category_get(cfg
, cat
)))
1195 ast_variable_delete(category
, (char *) var
, (char *) match
);
1196 } else if (!strcasecmp(action
, "append")) {
1197 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) &&
1198 (category
= ast_category_get(cfg
, cat
)) &&
1199 (v
= ast_variable_new(var
, value
))){
1200 if (object
|| (match
&& !strcasecmp(match
, "object")))
1202 ast_variable_append(category
, v
);
1208 static char mandescr_updateconfig
[] =
1209 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1210 "configuration elements in Asterisk configuration files.\n"
1211 "Variables (X's represent 6 digit number beginning with 000000):\n"
1212 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1213 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1214 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1215 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1216 " Cat-XXXXXX: Category to operate on\n"
1217 " Var-XXXXXX: Variable to work on\n"
1218 " Value-XXXXXX: Value to work on\n"
1219 " Match-XXXXXX: Extra match required to match line\n";
1221 static int action_updateconfig(struct mansession
*s
, const struct message
*m
)
1223 struct ast_config
*cfg
;
1224 const char *sfn
= astman_get_header(m
, "SrcFilename");
1225 const char *dfn
= astman_get_header(m
, "DstFilename");
1227 char idText
[256] = "";
1228 const char *id
= astman_get_header(m
, "ActionID");
1229 const char *rld
= astman_get_header(m
, "Reload");
1231 if (!ast_strlen_zero(id
))
1232 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1234 if (ast_strlen_zero(sfn
) || ast_strlen_zero(dfn
)) {
1235 astman_send_error(s
, m
, "Filename not specified");
1238 if (!(cfg
= ast_config_load_with_comments(sfn
))) {
1239 astman_send_error(s
, m
, "Config file not found");
1242 handle_updates(s
, m
, cfg
);
1243 res
= config_text_file_save(dfn
, cfg
, "Manager");
1244 ast_config_destroy(cfg
);
1246 astman_send_error(s
, m
, "Save of config failed");
1249 astman_append(s
, "Response: Success\r\n%s\r\n", idText
);
1250 if (!ast_strlen_zero(rld
)) {
1253 ast_module_reload(rld
);
1258 /*! \brief Manager WAITEVENT */
1259 static char mandescr_waitevent
[] =
1260 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1261 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1262 "session, events will be generated and queued.\n"
1264 " Timeout: Maximum time to wait for events\n";
1266 static int action_waitevent(struct mansession
*s
, const struct message
*m
)
1268 const char *timeouts
= astman_get_header(m
, "Timeout");
1269 int timeout
= -1, max
;
1273 struct eventqent
*eqe
;
1274 const char *id
= astman_get_header(m
,"ActionID");
1275 char idText
[256] = "";
1277 if (!ast_strlen_zero(id
))
1278 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1280 if (!ast_strlen_zero(timeouts
)) {
1281 sscanf(timeouts
, "%i", &timeout
);
1284 ast_mutex_lock(&s
->__lock
);
1285 if (s
->waiting_thread
!= AST_PTHREADT_NULL
) {
1286 pthread_kill(s
->waiting_thread
, SIGURG
);
1288 if (s
->sessiontimeout
) {
1290 max
= s
->sessiontimeout
- now
- 10;
1293 if ((timeout
< 0) || (timeout
> max
))
1295 if (!s
->send_events
)
1296 s
->send_events
= -1;
1297 /* Once waitevent is called, always queue events from now on */
1299 ast_mutex_unlock(&s
->__lock
);
1300 s
->waiting_thread
= pthread_self();
1302 ast_log(LOG_DEBUG
, "Starting waiting for an event!\n");
1303 for (x
=0; ((x
< timeout
) || (timeout
< 0)); x
++) {
1304 ast_mutex_lock(&s
->__lock
);
1305 if (s
->eventq
&& s
->eventq
->next
)
1307 if (s
->waiting_thread
!= pthread_self())
1311 ast_mutex_unlock(&s
->__lock
);
1315 if (ast_wait_for_input(s
->fd
, 1000))
1322 ast_log(LOG_DEBUG
, "Finished waiting for an event!\n");
1323 ast_mutex_lock(&s
->__lock
);
1324 if (s
->waiting_thread
== pthread_self()) {
1325 astman_send_response(s
, m
, "Success", "Waiting for Event...");
1326 /* Only show events if we're the most recent waiter */
1327 while(s
->eventq
->next
) {
1328 eqe
= s
->eventq
->next
;
1329 if (((s
->readperm
& eqe
->category
) == eqe
->category
) &&
1330 ((s
->send_events
& eqe
->category
) == eqe
->category
)) {
1331 astman_append(s
, "%s", eqe
->eventdata
);
1333 unuse_eventqent(s
->eventq
);
1337 "Event: WaitEventComplete\r\n"
1340 s
->waiting_thread
= AST_PTHREADT_NULL
;
1342 ast_log(LOG_DEBUG
, "Abandoning event request!\n");
1344 ast_mutex_unlock(&s
->__lock
);
1348 static char mandescr_listcommands
[] =
1349 "Description: Returns the action name and synopsis for every\n"
1350 " action that is available to the user\n"
1351 "Variables: NONE\n";
1353 /*! \note The actionlock is read-locked by the caller of this function */
1354 static int action_listcommands(struct mansession
*s
, const struct message
*m
)
1356 struct manager_action
*cur
;
1357 char idText
[256] = "";
1359 const char *id
= astman_get_header(m
,"ActionID");
1361 if (!ast_strlen_zero(id
))
1362 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1363 astman_append(s
, "Response: Success\r\n%s", idText
);
1364 for (cur
= first_action
; cur
; cur
= cur
->next
) {
1365 if ((s
->writeperm
& cur
->authority
) == cur
->authority
)
1366 astman_append(s
, "%s: %s (Priv: %s)\r\n", cur
->action
, cur
->synopsis
, authority_to_str(cur
->authority
, temp
, sizeof(temp
)));
1368 astman_append(s
, "\r\n");
1373 static char mandescr_events
[] =
1374 "Description: Enable/Disable sending of events to this manager\n"
1377 " EventMask: 'on' if all events should be sent,\n"
1378 " 'off' if no events should be sent,\n"
1379 " 'system,call,log' to select which flags events should have to be sent.\n";
1381 static int action_events(struct mansession
*s
, const struct message
*m
)
1383 const char *mask
= astman_get_header(m
, "EventMask");
1386 res
= set_eventmask(s
, mask
);
1388 astman_send_response(s
, m
, "Events On", NULL
);
1390 astman_send_response(s
, m
, "Events Off", NULL
);
1395 static char mandescr_logoff
[] =
1396 "Description: Logoff this manager session\n"
1397 "Variables: NONE\n";
1399 static int action_logoff(struct mansession
*s
, const struct message
*m
)
1401 astman_send_response(s
, m
, "Goodbye", "Thanks for all the fish.");
1405 static char mandescr_hangup
[] =
1406 "Description: Hangup a channel\n"
1408 " Channel: The channel name to be hungup\n";
1410 static int action_hangup(struct mansession
*s
, const struct message
*m
)
1412 struct ast_channel
*c
= NULL
;
1413 const char *name
= astman_get_header(m
, "Channel");
1414 if (ast_strlen_zero(name
)) {
1415 astman_send_error(s
, m
, "No channel specified");
1418 c
= ast_get_channel_by_name_locked(name
);
1420 astman_send_error(s
, m
, "No such channel");
1423 ast_softhangup(c
, AST_SOFTHANGUP_EXPLICIT
);
1424 ast_channel_unlock(c
);
1425 astman_send_ack(s
, m
, "Channel Hungup");
1429 static char mandescr_setvar
[] =
1430 "Description: Set a global or local channel variable.\n"
1431 "Variables: (Names marked with * are required)\n"
1432 " Channel: Channel to set variable for\n"
1433 " *Variable: Variable name\n"
1436 static int action_setvar(struct mansession
*s
, const struct message
*m
)
1438 struct ast_channel
*c
= NULL
;
1439 const char *name
= astman_get_header(m
, "Channel");
1440 const char *varname
= astman_get_header(m
, "Variable");
1441 const char *varval
= astman_get_header(m
, "Value");
1443 if (ast_strlen_zero(varname
)) {
1444 astman_send_error(s
, m
, "No variable specified");
1448 if (!ast_strlen_zero(name
)) {
1449 c
= ast_get_channel_by_name_locked(name
);
1451 astman_send_error(s
, m
, "No such channel");
1456 pbx_builtin_setvar_helper(c
, varname
, S_OR(varval
, ""));
1459 ast_channel_unlock(c
);
1461 astman_send_ack(s
, m
, "Variable Set");
1466 static char mandescr_getvar
[] =
1467 "Description: Get the value of a global or local channel variable.\n"
1468 "Variables: (Names marked with * are required)\n"
1469 " Channel: Channel to read variable from\n"
1470 " *Variable: Variable name\n"
1471 " ActionID: Optional Action id for message matching.\n";
1473 static int action_getvar(struct mansession
*s
, const struct message
*m
)
1475 struct ast_channel
*c
= NULL
;
1476 const char *name
= astman_get_header(m
, "Channel");
1477 const char *varname
= astman_get_header(m
, "Variable");
1478 const char *id
= astman_get_header(m
,"ActionID");
1480 char workspace
[1024] = "";
1482 if (ast_strlen_zero(varname
)) {
1483 astman_send_error(s
, m
, "No variable specified");
1487 if (!ast_strlen_zero(name
)) {
1488 c
= ast_get_channel_by_name_locked(name
);
1490 astman_send_error(s
, m
, "No such channel");
1495 if (varname
[strlen(varname
) - 1] == ')') {
1496 char *copy
= ast_strdupa(varname
);
1498 ast_func_read(c
, copy
, workspace
, sizeof(workspace
));
1501 pbx_retrieve_variable(c
, varname
, &varval
, workspace
, sizeof(workspace
), NULL
);
1505 ast_channel_unlock(c
);
1506 astman_append(s
, "Response: Success\r\n"
1507 "Variable: %s\r\nValue: %s\r\n", varname
, varval
);
1508 if (!ast_strlen_zero(id
))
1509 astman_append(s
, "ActionID: %s\r\n",id
);
1510 astman_append(s
, "\r\n");
1516 /*! \brief Manager "status" command to show channels */
1517 /* Needs documentation... */
1518 static int action_status(struct mansession
*s
, const struct message
*m
)
1520 const char *id
= astman_get_header(m
,"ActionID");
1521 const char *name
= astman_get_header(m
,"Channel");
1522 char idText
[256] = "";
1523 struct ast_channel
*c
;
1525 struct timeval now
= ast_tvnow();
1526 long elapsed_seconds
= 0;
1527 int all
= ast_strlen_zero(name
); /* set if we want all channels */
1529 if (!ast_strlen_zero(id
))
1530 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1532 c
= ast_channel_walk_locked(NULL
);
1534 c
= ast_get_channel_by_name_locked(name
);
1536 astman_send_error(s
, m
, "No such channel");
1540 astman_send_ack(s
, m
, "Channel status will follow");
1541 /* if we look by name, we break after the first iteration */
1544 snprintf(bridge
, sizeof(bridge
), "Link: %s\r\n", c
->_bridge
->name
);
1549 elapsed_seconds
= now
.tv_sec
- c
->cdr
->start
.tv_sec
;
1553 "Privilege: Call\r\n"
1555 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1556 "CallerIDNum: %s\r\n"
1557 "CallerIDName: %s\r\n"
1569 S_OR(c
->cid
.cid_num
, "<unknown>"),
1570 S_OR(c
->cid
.cid_num
, "<unknown>"),
1571 S_OR(c
->cid
.cid_name
, "<unknown>"),
1573 ast_state2str(c
->_state
), c
->context
,
1574 c
->exten
, c
->priority
, (long)elapsed_seconds
, bridge
, c
->uniqueid
, idText
);
1578 "Privilege: Call\r\n"
1580 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1581 "CallerIDNum: %s\r\n"
1582 "CallerIDName: %s\r\n"
1590 S_OR(c
->cid
.cid_num
, "<unknown>"),
1591 S_OR(c
->cid
.cid_num
, "<unknown>"),
1592 S_OR(c
->cid
.cid_name
, "<unknown>"),
1594 ast_state2str(c
->_state
), bridge
, c
->uniqueid
, idText
);
1596 ast_channel_unlock(c
);
1599 c
= ast_channel_walk_locked(c
);
1602 "Event: StatusComplete\r\n"
1608 static char mandescr_redirect
[] =
1609 "Description: Redirect (transfer) a call.\n"
1610 "Variables: (Names marked with * are required)\n"
1611 " *Channel: Channel to redirect\n"
1612 " ExtraChannel: Second call leg to transfer (optional)\n"
1613 " *Exten: Extension to transfer to\n"
1614 " *Context: Context to transfer to\n"
1615 " *Priority: Priority to transfer to\n"
1616 " ActionID: Optional Action id for message matching.\n";
1618 /*! \brief action_redirect: The redirect manager command */
1619 static int action_redirect(struct mansession
*s
, const struct message
*m
)
1621 const char *name
= astman_get_header(m
, "Channel");
1622 const char *name2
= astman_get_header(m
, "ExtraChannel");
1623 const char *exten
= astman_get_header(m
, "Exten");
1624 const char *context
= astman_get_header(m
, "Context");
1625 const char *priority
= astman_get_header(m
, "Priority");
1626 struct ast_channel
*chan
, *chan2
= NULL
;
1630 if (ast_strlen_zero(name
)) {
1631 astman_send_error(s
, m
, "Channel not specified");
1634 if (!ast_strlen_zero(priority
) && (sscanf(priority
, "%d", &pi
) != 1)) {
1635 if ((pi
= ast_findlabel_extension(NULL
, context
, exten
, priority
, NULL
)) < 1) {
1636 astman_send_error(s
, m
, "Invalid priority\n");
1640 /* XXX watch out, possible deadlock!!! */
1641 chan
= ast_get_channel_by_name_locked(name
);
1644 snprintf(buf
, sizeof(buf
), "Channel does not exist: %s", name
);
1645 astman_send_error(s
, m
, buf
);
1648 if (ast_check_hangup(chan
)) {
1649 astman_send_error(s
, m
, "Redirect failed, channel not up.\n");
1650 ast_channel_unlock(chan
);
1653 if (!ast_strlen_zero(name2
))
1654 chan2
= ast_get_channel_by_name_locked(name2
);
1655 if (chan2
&& ast_check_hangup(chan2
)) {
1656 astman_send_error(s
, m
, "Redirect failed, extra channel not up.\n");
1657 ast_channel_unlock(chan
);
1658 ast_channel_unlock(chan2
);
1661 res
= ast_async_goto(chan
, context
, exten
, pi
);
1663 if (!ast_strlen_zero(name2
)) {
1665 res
= ast_async_goto(chan2
, context
, exten
, pi
);
1669 astman_send_ack(s
, m
, "Dual Redirect successful");
1671 astman_send_error(s
, m
, "Secondary redirect failed");
1673 astman_send_ack(s
, m
, "Redirect successful");
1675 astman_send_error(s
, m
, "Redirect failed");
1677 ast_channel_unlock(chan
);
1679 ast_channel_unlock(chan2
);
1683 static char mandescr_command
[] =
1684 "Description: Run a CLI command.\n"
1685 "Variables: (Names marked with * are required)\n"
1686 " *Command: Asterisk CLI command to run\n"
1687 " ActionID: Optional Action id for message matching.\n";
1689 /*! \brief action_command: Manager command "command" - execute CLI command */
1690 static int action_command(struct mansession
*s
, const struct message
*m
)
1692 const char *cmd
= astman_get_header(m
, "Command");
1693 const char *id
= astman_get_header(m
, "ActionID");
1694 char *buf
, *final_buf
;
1695 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1696 int fd
= mkstemp(template), i
= 0;
1699 for (i
= 0; i
< sizeof(command_blacklist
) / sizeof(command_blacklist
[0]); i
++) {
1700 if (!strncmp(cmd
, command_blacklist
[i
], strlen(command_blacklist
[i
]))) {
1701 astman_send_error(s
, m
, "Command blacklisted");
1706 astman_append(s
, "Response: Follows\r\nPrivilege: Command\r\n");
1707 if (!ast_strlen_zero(id
))
1708 astman_append(s
, "ActionID: %s\r\n", id
);
1709 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1710 ast_cli_command(fd
, cmd
); /* XXX need to change this to use a FILE * */
1711 l
= lseek(fd
, 0, SEEK_END
); /* how many chars available */
1713 /* This has a potential to overflow the stack. Hence, use the heap. */
1714 buf
= ast_calloc(1, l
+ 1);
1715 final_buf
= ast_calloc(1, l
+ 1);
1717 lseek(fd
, 0, SEEK_SET
);
1721 term_strip(final_buf
, buf
, l
);
1722 final_buf
[l
] = '\0';
1724 astman_append(s
, "%s", S_OR(final_buf
, buf
));
1729 astman_append(s
, "--END COMMAND--\r\n\r\n");
1731 ast_free(final_buf
);
1735 static void *fast_originate(void *data
)
1737 struct fast_originate_helper
*in
= data
;
1740 struct ast_channel
*chan
= NULL
;
1741 char requested_channel
[AST_CHANNEL_NAME
];
1743 if (!ast_strlen_zero(in
->app
)) {
1744 res
= ast_pbx_outgoing_app(in
->tech
, AST_FORMAT_SLINEAR
, in
->data
, in
->timeout
, in
->app
, in
->appdata
, &reason
, 1,
1745 S_OR(in
->cid_num
, NULL
),
1746 S_OR(in
->cid_name
, NULL
),
1747 in
->vars
, in
->account
, &chan
);
1749 res
= ast_pbx_outgoing_exten(in
->tech
, AST_FORMAT_SLINEAR
, in
->data
, in
->timeout
, in
->context
, in
->exten
, in
->priority
, &reason
, 1,
1750 S_OR(in
->cid_num
, NULL
),
1751 S_OR(in
->cid_name
, NULL
),
1752 in
->vars
, in
->account
, &chan
);
1756 snprintf(requested_channel
, AST_CHANNEL_NAME
, "%s/%s", in
->tech
, in
->data
);
1757 /* Tell the manager what happened with the channel */
1758 manager_event(EVENT_FLAG_CALL
, "OriginateResponse",
1766 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1767 "CallerIDNum: %s\r\n"
1768 "CallerIDName: %s\r\n",
1769 in
->idtext
, res
? "Failure" : "Success", chan
? chan
->name
: requested_channel
, in
->context
, in
->exten
, reason
,
1770 chan
? chan
->uniqueid
: "<null>",
1771 S_OR(in
->cid_num
, "<unknown>"),
1772 S_OR(in
->cid_num
, "<unknown>"),
1773 S_OR(in
->cid_name
, "<unknown>")
1776 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1778 ast_channel_unlock(chan
);
1783 static char mandescr_originate
[] =
1784 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1785 " Application/Data\n"
1786 "Variables: (Names marked with * are required)\n"
1787 " *Channel: Channel name to call\n"
1788 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1789 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1790 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1791 " Application: Application to use\n"
1792 " Data: Data to use (requires 'Application')\n"
1793 " Timeout: How long to wait for call to be answered (in ms)\n"
1794 " CallerID: Caller ID to be set on the outgoing channel\n"
1795 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1796 " Account: Account code\n"
1797 " Async: Set to 'true' for fast origination\n";
1799 static int action_originate(struct mansession
*s
, const struct message
*m
)
1801 const char *name
= astman_get_header(m
, "Channel");
1802 const char *exten
= astman_get_header(m
, "Exten");
1803 const char *context
= astman_get_header(m
, "Context");
1804 const char *priority
= astman_get_header(m
, "Priority");
1805 const char *timeout
= astman_get_header(m
, "Timeout");
1806 const char *callerid
= astman_get_header(m
, "CallerID");
1807 const char *account
= astman_get_header(m
, "Account");
1808 const char *app
= astman_get_header(m
, "Application");
1809 const char *appdata
= astman_get_header(m
, "Data");
1810 const char *async
= astman_get_header(m
, "Async");
1811 const char *id
= astman_get_header(m
, "ActionID");
1812 struct ast_variable
*vars
= astman_get_variables(m
);
1814 char *l
= NULL
, *n
= NULL
;
1823 pthread_attr_t attr
;
1825 astman_send_error(s
, m
, "Channel not specified");
1828 if (!ast_strlen_zero(priority
) && (sscanf(priority
, "%d", &pi
) != 1)) {
1829 if ((pi
= ast_findlabel_extension(NULL
, context
, exten
, priority
, NULL
)) < 1) {
1830 astman_send_error(s
, m
, "Invalid priority\n");
1834 if (!ast_strlen_zero(timeout
) && (sscanf(timeout
, "%d", &to
) != 1)) {
1835 astman_send_error(s
, m
, "Invalid timeout\n");
1838 ast_copy_string(tmp
, name
, sizeof(tmp
));
1840 data
= strchr(tmp
, '/');
1842 astman_send_error(s
, m
, "Invalid channel\n");
1846 ast_copy_string(tmp2
, callerid
, sizeof(tmp2
));
1847 ast_callerid_parse(tmp2
, &n
, &l
);
1849 if (ast_strlen_zero(n
))
1853 ast_shrink_phone_number(l
);
1854 if (ast_strlen_zero(l
))
1857 if (ast_true(async
)) {
1858 struct fast_originate_helper
*fast
= ast_calloc(1, sizeof(*fast
));
1862 if (!ast_strlen_zero(id
))
1863 snprintf(fast
->idtext
, sizeof(fast
->idtext
), "ActionID: %s\r\n", id
);
1864 ast_copy_string(fast
->tech
, tech
, sizeof(fast
->tech
));
1865 ast_copy_string(fast
->data
, data
, sizeof(fast
->data
));
1866 ast_copy_string(fast
->app
, app
, sizeof(fast
->app
));
1867 ast_copy_string(fast
->appdata
, appdata
, sizeof(fast
->appdata
));
1869 ast_copy_string(fast
->cid_num
, l
, sizeof(fast
->cid_num
));
1871 ast_copy_string(fast
->cid_name
, n
, sizeof(fast
->cid_name
));
1873 ast_copy_string(fast
->context
, context
, sizeof(fast
->context
));
1874 ast_copy_string(fast
->exten
, exten
, sizeof(fast
->exten
));
1875 ast_copy_string(fast
->account
, account
, sizeof(fast
->account
));
1877 fast
->priority
= pi
;
1878 pthread_attr_init(&attr
);
1879 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1880 if (ast_pthread_create(&th
, &attr
, fast_originate
, fast
)) {
1885 pthread_attr_destroy(&attr
);
1887 } else if (!ast_strlen_zero(app
)) {
1888 res
= ast_pbx_outgoing_app(tech
, AST_FORMAT_SLINEAR
, data
, to
, app
, appdata
, &reason
, 1, l
, n
, vars
, account
, NULL
);
1890 if (exten
&& context
&& pi
)
1891 res
= ast_pbx_outgoing_exten(tech
, AST_FORMAT_SLINEAR
, data
, to
, context
, exten
, pi
, &reason
, 1, l
, n
, vars
, account
, NULL
);
1893 astman_send_error(s
, m
, "Originate with 'Exten' requires 'Context' and 'Priority'");
1898 astman_send_ack(s
, m
, "Originate successfully queued");
1900 astman_send_error(s
, m
, "Originate failed");
1904 /*! \brief Help text for manager command mailboxstatus
1906 static char mandescr_mailboxstatus
[] =
1907 "Description: Checks a voicemail account for status.\n"
1908 "Variables: (Names marked with * are required)\n"
1909 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1910 " ActionID: Optional ActionID for message matching.\n"
1911 "Returns number of messages.\n"
1912 " Message: Mailbox Status\n"
1913 " Mailbox: <mailboxid>\n"
1914 " Waiting: <count>\n"
1917 static int action_mailboxstatus(struct mansession
*s
, const struct message
*m
)
1919 const char *mailbox
= astman_get_header(m
, "Mailbox");
1920 const char *id
= astman_get_header(m
,"ActionID");
1921 char idText
[256] = "";
1923 if (ast_strlen_zero(mailbox
)) {
1924 astman_send_error(s
, m
, "Mailbox not specified");
1927 if (!ast_strlen_zero(id
))
1928 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1929 ret
= ast_app_has_voicemail(mailbox
, NULL
);
1930 astman_append(s
, "Response: Success\r\n"
1932 "Message: Mailbox Status\r\n"
1934 "Waiting: %d\r\n\r\n", idText
, mailbox
, ret
);
1938 static char mandescr_mailboxcount
[] =
1939 "Description: Checks a voicemail account for new messages.\n"
1940 "Variables: (Names marked with * are required)\n"
1941 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1942 " ActionID: Optional ActionID for message matching.\n"
1943 "Returns number of new and old messages.\n"
1944 " Message: Mailbox Message Count\n"
1945 " Mailbox: <mailboxid>\n"
1946 " NewMessages: <count>\n"
1947 " OldMessages: <count>\n"
1949 static int action_mailboxcount(struct mansession
*s
, const struct message
*m
)
1951 const char *mailbox
= astman_get_header(m
, "Mailbox");
1952 const char *id
= astman_get_header(m
,"ActionID");
1953 char idText
[256] = "";
1954 int newmsgs
= 0, oldmsgs
= 0;
1955 if (ast_strlen_zero(mailbox
)) {
1956 astman_send_error(s
, m
, "Mailbox not specified");
1959 ast_app_inboxcount(mailbox
, &newmsgs
, &oldmsgs
);
1960 if (!ast_strlen_zero(id
)) {
1961 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n",id
);
1963 astman_append(s
, "Response: Success\r\n"
1965 "Message: Mailbox Message Count\r\n"
1967 "NewMessages: %d\r\n"
1968 "OldMessages: %d\r\n"
1970 idText
,mailbox
, newmsgs
, oldmsgs
);
1974 static char mandescr_extensionstate
[] =
1975 "Description: Report the extension state for given extension.\n"
1976 " If the extension has a hint, will use devicestate to check\n"
1977 " the status of the device connected to the extension.\n"
1978 "Variables: (Names marked with * are required)\n"
1979 " *Exten: Extension to check state on\n"
1980 " *Context: Context for extension\n"
1981 " ActionId: Optional ID for this transaction\n"
1982 "Will return an \"Extension Status\" message.\n"
1983 "The response will include the hint for the extension and the status.\n";
1985 static int action_extensionstate(struct mansession
*s
, const struct message
*m
)
1987 const char *exten
= astman_get_header(m
, "Exten");
1988 const char *context
= astman_get_header(m
, "Context");
1989 const char *id
= astman_get_header(m
,"ActionID");
1990 char idText
[256] = "";
1991 char hint
[256] = "";
1993 if (ast_strlen_zero(exten
)) {
1994 astman_send_error(s
, m
, "Extension not specified");
1997 if (ast_strlen_zero(context
))
1998 context
= "default";
1999 status
= ast_extension_state(NULL
, context
, exten
);
2000 ast_get_hint(hint
, sizeof(hint
) - 1, NULL
, 0, NULL
, context
, exten
);
2001 if (!ast_strlen_zero(id
)) {
2002 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
2004 astman_append(s
, "Response: Success\r\n"
2006 "Message: Extension Status\r\n"
2010 "Status: %d\r\n\r\n",
2011 idText
,exten
, context
, hint
, status
);
2015 static char mandescr_timeout
[] =
2016 "Description: Hangup a channel after a certain time.\n"
2017 "Variables: (Names marked with * are required)\n"
2018 " *Channel: Channel name to hangup\n"
2019 " *Timeout: Maximum duration of the call (sec)\n"
2020 "Acknowledges set time with 'Timeout Set' message\n";
2022 static int action_timeout(struct mansession
*s
, const struct message
*m
)
2024 struct ast_channel
*c
= NULL
;
2025 const char *name
= astman_get_header(m
, "Channel");
2026 int timeout
= atoi(astman_get_header(m
, "Timeout"));
2027 if (ast_strlen_zero(name
)) {
2028 astman_send_error(s
, m
, "No channel specified");
2032 astman_send_error(s
, m
, "No timeout specified");
2035 c
= ast_get_channel_by_name_locked(name
);
2037 astman_send_error(s
, m
, "No such channel");
2040 ast_channel_setwhentohangup(c
, timeout
);
2041 ast_channel_unlock(c
);
2042 astman_send_ack(s
, m
, "Timeout Set");
2046 static int process_events(struct mansession
*s
)
2048 struct eventqent
*eqe
;
2050 ast_mutex_lock(&s
->__lock
);
2052 s
->eventq
= master_eventq
;
2053 while(s
->eventq
->next
) {
2054 eqe
= s
->eventq
->next
;
2055 if ((s
->authenticated
&& (s
->readperm
& eqe
->category
) == eqe
->category
) &&
2056 ((s
->send_events
& eqe
->category
) == eqe
->category
)) {
2058 if (!ret
&& ast_carefulwrite(s
->fd
, eqe
->eventdata
, strlen(eqe
->eventdata
), s
->writetimeout
) < 0)
2060 } else if (!s
->outputstr
&& !(s
->outputstr
= ast_calloc(1, sizeof(*s
->outputstr
))))
2063 ast_dynamic_str_append(&s
->outputstr
, 0, "%s", eqe
->eventdata
);
2065 unuse_eventqent(s
->eventq
);
2068 ast_mutex_unlock(&s
->__lock
);
2072 static char mandescr_userevent
[] =
2073 "Description: Send an event to manager sessions.\n"
2074 "Variables: (Names marked with * are required)\n"
2075 " *UserEvent: EventStringToSend\n"
2076 " Header1: Content1\n"
2077 " HeaderN: ContentN\n";
2079 static int action_userevent(struct mansession
*s
, const struct message
*m
)
2081 const char *event
= astman_get_header(m
, "UserEvent");
2082 char body
[2048] = "";
2084 for (x
= 0; x
< m
->hdrcount
; x
++) {
2085 if (strncasecmp("UserEvent:", m
->headers
[x
], strlen("UserEvent:"))) {
2086 ast_copy_string(body
+ bodylen
, m
->headers
[x
], sizeof(body
) - bodylen
- 3);
2087 bodylen
+= strlen(m
->headers
[x
]);
2088 ast_copy_string(body
+ bodylen
, "\r\n", 3);
2093 manager_event(EVENT_FLAG_USER
, "UserEvent", "UserEvent: %s\r\n%s", event
, body
);
2097 static int process_message(struct mansession
*s
, const struct message
*m
)
2099 char action
[80] = "";
2100 struct manager_action
*tmp
;
2101 const char *id
= astman_get_header(m
,"ActionID");
2102 char idText
[256] = "";
2105 ast_copy_string(action
, astman_get_header(m
, "Action"), sizeof(action
));
2107 ast_log( LOG_DEBUG
, "Manager received command '%s'\n", action
);
2109 if (ast_strlen_zero(action
)) {
2110 astman_send_error(s
, m
, "Missing action in request");
2113 if (!ast_strlen_zero(id
)) {
2114 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
2116 if (!s
->authenticated
) {
2117 if (!strcasecmp(action
, "Challenge")) {
2118 const char *authtype
= astman_get_header(m
, "AuthType");
2120 if (!strcasecmp(authtype
, "MD5")) {
2121 if (ast_strlen_zero(s
->challenge
))
2122 snprintf(s
->challenge
, sizeof(s
->challenge
), "%ld", ast_random());
2123 astman_append(s
, "Response: Success\r\n"
2125 "Challenge: %s\r\n\r\n",
2126 idText
, s
->challenge
);
2129 astman_send_error(s
, m
, "Must specify AuthType");
2132 } else if (!strcasecmp(action
, "Login")) {
2133 if (authenticate(s
, m
)) {
2135 astman_send_error(s
, m
, "Authentication failed");
2138 s
->authenticated
= 1;
2139 if (option_verbose
> 1) {
2140 if (displayconnects
) {
2141 ast_verbose(VERBOSE_PREFIX_2
"%sManager '%s' logged on from %s\n",
2142 (s
->sessiontimeout
? "HTTP " : ""), s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2145 ast_log(LOG_EVENT
, "%sManager '%s' logged on from %s\n",
2146 (s
->sessiontimeout
? "HTTP " : ""), s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2147 astman_send_ack(s
, m
, "Authentication accepted");
2149 } else if (!strcasecmp(action
, "Logoff")) {
2150 astman_send_ack(s
, m
, "See ya");
2153 astman_send_error(s
, m
, "Authentication Required");
2155 if (!strcasecmp(action
, "Login"))
2156 astman_send_ack(s
, m
, "Already logged in");
2158 ast_rwlock_rdlock(&actionlock
);
2159 for (tmp
= first_action
; tmp
; tmp
= tmp
->next
) {
2160 if (strcasecmp(action
, tmp
->action
))
2162 if ((s
->writeperm
& tmp
->authority
) == tmp
->authority
) {
2163 if (tmp
->func(s
, m
))
2166 astman_send_error(s
, m
, "Permission denied");
2169 ast_rwlock_unlock(&actionlock
);
2171 astman_send_error(s
, m
, "Invalid/unknown command");
2176 return process_events(s
);
2179 static int get_input(struct mansession
*s
, char *output
)
2181 /* output must have at least sizeof(s->inbuf) space */
2184 struct pollfd fds
[1];
2185 for (x
= 1; x
< s
->inlen
; x
++) {
2186 if ((s
->inbuf
[x
] == '\n') && (s
->inbuf
[x
-1] == '\r')) {
2187 /* Copy output data up to and including \r\n */
2188 memcpy(output
, s
->inbuf
, x
+ 1);
2189 /* Add trailing \0 */
2191 /* Move remaining data back to the front */
2192 memmove(s
->inbuf
, s
->inbuf
+ x
+ 1, s
->inlen
- x
);
2193 s
->inlen
-= (x
+ 1);
2197 if (s
->inlen
>= sizeof(s
->inbuf
) - 1) {
2198 ast_log(LOG_WARNING
, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s
->sin
.sin_addr
), s
->inbuf
);
2202 fds
[0].events
= POLLIN
;
2204 ast_mutex_lock(&s
->__lock
);
2205 s
->waiting_thread
= pthread_self();
2206 ast_mutex_unlock(&s
->__lock
);
2208 res
= poll(fds
, 1, -1);
2210 ast_mutex_lock(&s
->__lock
);
2211 s
->waiting_thread
= AST_PTHREADT_NULL
;
2212 ast_mutex_unlock(&s
->__lock
);
2214 if (errno
== EINTR
) {
2217 ast_log(LOG_WARNING
, "Select returned error: %s\n", strerror(errno
));
2219 } else if (res
> 0) {
2220 ast_mutex_lock(&s
->__lock
);
2221 res
= read(s
->fd
, s
->inbuf
+ s
->inlen
, sizeof(s
->inbuf
) - 1 - s
->inlen
);
2222 ast_mutex_unlock(&s
->__lock
);
2229 s
->inbuf
[s
->inlen
] = '\0';
2233 static int do_message(struct mansession
*s
)
2235 struct message m
= { 0 };
2236 char header_buf
[sizeof(s
->inbuf
)] = { '\0' };
2240 /* Check if any events are pending and do them if needed */
2241 if (s
->eventq
->next
) {
2242 if (process_events(s
))
2245 res
= get_input(s
, header_buf
);
2248 } else if (res
> 0) {
2249 /* Strip trailing \r\n */
2250 if (strlen(header_buf
) < 2)
2252 header_buf
[strlen(header_buf
) - 2] = '\0';
2253 if (ast_strlen_zero(header_buf
))
2254 return process_message(s
, &m
) ? -1 : 0;
2255 else if (m
.hdrcount
< (AST_MAX_MANHEADERS
- 1))
2256 m
.headers
[m
.hdrcount
++] = ast_strdupa(header_buf
);
2263 static void *session_do(void *data
)
2265 struct mansession
*s
= data
;
2268 astman_append(s
, "Asterisk Call Manager/1.0\r\n");
2270 if ((res
= do_message(s
)) < 0)
2273 if (s
->authenticated
) {
2274 if (option_verbose
> 1) {
2275 if (displayconnects
)
2276 ast_verbose(VERBOSE_PREFIX_2
"Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2278 ast_log(LOG_EVENT
, "Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2280 if (option_verbose
> 1) {
2281 if (displayconnects
)
2282 ast_verbose(VERBOSE_PREFIX_2
"Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2284 ast_log(LOG_EVENT
, "Failed attempt from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2287 /* It is possible under certain circumstances for this session thread
2288 to complete its work and exit *before* the thread that created it
2289 has finished executing the ast_pthread_create_background() function.
2290 If this occurs, some versions of glibc appear to act in a buggy
2291 fashion and attempt to write data into memory that it thinks belongs
2292 to the thread but is in fact not owned by the thread (or may have
2293 been freed completely).
2295 Causing this thread to yield to other threads at least one time
2296 appears to work around this bug.
2304 static void *accept_thread(void *ignore
)
2307 struct sockaddr_in sin
;
2309 struct eventqent
*eqe
;
2310 struct mansession
*s
;
2314 pthread_attr_t attr
;
2316 struct pollfd pfds
[1];
2318 pthread_attr_init(&attr
);
2319 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
2323 AST_LIST_LOCK(&sessions
);
2324 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions
, s
, list
) {
2325 if (s
->sessiontimeout
&& (now
> s
->sessiontimeout
) && !s
->inuse
) {
2326 AST_LIST_REMOVE_CURRENT(&sessions
, list
);
2328 if (s
->authenticated
&& (option_verbose
> 1) && displayconnects
) {
2329 ast_verbose(VERBOSE_PREFIX_2
"HTTP Manager '%s' timed out from %s\n",
2330 s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2336 AST_LIST_TRAVERSE_SAFE_END
2337 /* Purge master event queue of old, unused events, but make sure we
2338 always keep at least one in the queue */
2339 eqe
= master_eventq
;
2340 while (master_eventq
->next
&& !master_eventq
->usecount
) {
2341 eqe
= master_eventq
;
2342 master_eventq
= master_eventq
->next
;
2345 AST_LIST_UNLOCK(&sessions
);
2347 sinlen
= sizeof(sin
);
2349 pfds
[0].events
= POLLIN
;
2350 /* Wait for something to happen, but timeout every few seconds so
2351 we can ditch any old manager sessions */
2352 if (poll(pfds
, 1, 5000) < 1)
2354 as
= accept(asock
, (struct sockaddr
*)&sin
, &sinlen
);
2356 ast_log(LOG_NOTICE
, "Accept returned -1: %s\n", strerror(errno
));
2359 p
= getprotobyname("tcp");
2361 if( setsockopt(as
, p
->p_proto
, TCP_NODELAY
, (char *)&arg
, sizeof(arg
) ) < 0 ) {
2362 ast_log(LOG_WARNING
, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno
));
2365 if (!(s
= ast_calloc(1, sizeof(*s
))))
2368 memcpy(&s
->sin
, &sin
, sizeof(sin
));
2369 s
->writetimeout
= 100;
2370 s
->waiting_thread
= AST_PTHREADT_NULL
;
2372 if (!block_sockets
) {
2373 /* For safety, make sure socket is non-blocking */
2374 flags
= fcntl(as
, F_GETFL
);
2375 fcntl(as
, F_SETFL
, flags
| O_NONBLOCK
);
2377 flags
= fcntl(as
, F_GETFL
);
2378 fcntl(as
, F_SETFL
, flags
& ~O_NONBLOCK
);
2380 ast_mutex_init(&s
->__lock
);
2382 s
->send_events
= -1;
2383 AST_LIST_LOCK(&sessions
);
2384 AST_LIST_INSERT_HEAD(&sessions
, s
, list
);
2386 /* Find the last place in the master event queue and hook ourselves
2388 s
->eventq
= master_eventq
;
2389 while(s
->eventq
->next
)
2390 s
->eventq
= s
->eventq
->next
;
2391 ast_atomic_fetchadd_int(&s
->eventq
->usecount
, 1);
2392 AST_LIST_UNLOCK(&sessions
);
2393 if (ast_pthread_create_background(&s
->t
, &attr
, session_do
, s
))
2396 pthread_attr_destroy(&attr
);
2400 static int append_event(const char *str
, int category
)
2402 struct eventqent
*tmp
, *prev
= NULL
;
2403 tmp
= ast_malloc(sizeof(*tmp
) + strlen(str
));
2409 tmp
->category
= category
;
2410 strcpy(tmp
->eventdata
, str
);
2412 if (master_eventq
) {
2413 prev
= master_eventq
;
2418 master_eventq
= tmp
;
2421 tmp
->usecount
= num_sessions
;
2426 /*! \brief manager_event: Send AMI event to client */
2427 int manager_event(int category
, const char *event
, const char *fmt
, ...)
2429 struct mansession
*s
;
2433 struct ast_dynamic_str
*buf
;
2435 /* Abort if there aren't any manager sessions */
2439 if (!(buf
= ast_dynamic_str_thread_get(&manager_event_buf
, MANAGER_EVENT_BUF_INITSIZE
)))
2442 ast_dynamic_str_thread_set(&buf
, 0, &manager_event_buf
,
2443 "Event: %s\r\nPrivilege: %s\r\n",
2444 event
, authority_to_str(category
, auth
, sizeof(auth
)));
2446 if (timestampevents
) {
2448 ast_dynamic_str_thread_append(&buf
, 0, &manager_event_buf
,
2449 "Timestamp: %ld.%06lu\r\n",
2450 now
.tv_sec
, (unsigned long) now
.tv_usec
);
2454 ast_dynamic_str_thread_append_va(&buf
, 0, &manager_event_buf
, fmt
, ap
);
2457 ast_dynamic_str_thread_append(&buf
, 0, &manager_event_buf
, "\r\n");
2459 /* Append event to master list and wake up any sleeping sessions */
2460 AST_LIST_LOCK(&sessions
);
2461 append_event(buf
->str
, category
);
2462 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2463 ast_mutex_lock(&s
->__lock
);
2464 if (s
->waiting_thread
!= AST_PTHREADT_NULL
)
2465 pthread_kill(s
->waiting_thread
, SIGURG
);
2466 ast_mutex_unlock(&s
->__lock
);
2468 AST_LIST_UNLOCK(&sessions
);
2473 int ast_manager_unregister(char *action
)
2475 struct manager_action
*cur
, *prev
;
2477 ast_rwlock_wrlock(&actionlock
);
2478 cur
= prev
= first_action
;
2480 if (!strcasecmp(action
, cur
->action
)) {
2481 prev
->next
= cur
->next
;
2483 if (option_verbose
> 1)
2484 ast_verbose(VERBOSE_PREFIX_2
"Manager unregistered action %s\n", action
);
2485 ast_rwlock_unlock(&actionlock
);
2491 ast_rwlock_unlock(&actionlock
);
2495 static int manager_state_cb(char *context
, char *exten
, int state
, void *data
)
2497 /* Notify managers of change */
2498 manager_event(EVENT_FLAG_CALL
, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten
, context
, state
);
2502 static int ast_manager_register_struct(struct manager_action
*act
)
2504 struct manager_action
*cur
, *prev
= NULL
;
2507 ast_rwlock_wrlock(&actionlock
);
2509 while (cur
) { /* Walk the list of actions */
2510 ret
= strcasecmp(cur
->action
, act
->action
);
2512 ast_log(LOG_WARNING
, "Manager: Action '%s' already registered\n", act
->action
);
2513 ast_rwlock_unlock(&actionlock
);
2515 } else if (ret
> 0) {
2516 /* Insert these alphabetically */
2518 act
->next
= prev
->next
;
2521 act
->next
= first_action
;
2538 if (option_verbose
> 1)
2539 ast_verbose(VERBOSE_PREFIX_2
"Manager registered action %s\n", act
->action
);
2540 ast_rwlock_unlock(&actionlock
);
2544 /*! \brief register a new command with manager, including online help. This is
2545 the preferred way to register a manager command */
2546 int ast_manager_register2(const char *action
, int auth
, int (*func
)(struct mansession
*s
, const struct message
*m
), const char *synopsis
, const char *description
)
2548 struct manager_action
*cur
;
2550 cur
= ast_malloc(sizeof(*cur
));
2554 cur
->action
= action
;
2555 cur
->authority
= auth
;
2557 cur
->synopsis
= synopsis
;
2558 cur
->description
= description
;
2561 ast_manager_register_struct(cur
);
2566 END Doxygen group */
2568 static struct mansession
*find_session(unsigned long ident
)
2570 struct mansession
*s
;
2572 AST_LIST_LOCK(&sessions
);
2573 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2574 ast_mutex_lock(&s
->__lock
);
2575 if (s
->sessiontimeout
&& (s
->managerid
== ident
) && !s
->needdestroy
) {
2579 ast_mutex_unlock(&s
->__lock
);
2581 AST_LIST_UNLOCK(&sessions
);
2586 int astman_verify_session_readpermissions(unsigned long ident
, int perm
)
2589 struct mansession
*s
;
2591 AST_LIST_LOCK(&sessions
);
2592 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2593 ast_mutex_lock(&s
->__lock
);
2594 if ((s
->managerid
== ident
) && (s
->readperm
& perm
)) {
2596 ast_mutex_unlock(&s
->__lock
);
2599 ast_mutex_unlock(&s
->__lock
);
2601 AST_LIST_UNLOCK(&sessions
);
2605 int astman_verify_session_writepermissions(unsigned long ident
, int perm
)
2608 struct mansession
*s
;
2610 AST_LIST_LOCK(&sessions
);
2611 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2612 ast_mutex_lock(&s
->__lock
);
2613 if ((s
->managerid
== ident
) && (s
->writeperm
& perm
)) {
2615 ast_mutex_unlock(&s
->__lock
);
2618 ast_mutex_unlock(&s
->__lock
);
2620 AST_LIST_UNLOCK(&sessions
);
2629 static char *contenttype
[] = { "plain", "html", "xml" };
2631 static char *generic_http_callback(int format
, struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2633 struct mansession
*s
= NULL
;
2634 unsigned long ident
= 0;
2635 char workspace
[512];
2637 size_t len
= sizeof(workspace
);
2639 char *c
= workspace
;
2640 char *retval
= NULL
;
2641 struct ast_variable
*v
;
2643 for (v
= params
; v
; v
= v
->next
) {
2644 if (!strcasecmp(v
->name
, "mansession_id")) {
2645 sscanf(v
->value
, "%lx", &ident
);
2650 if (!(s
= find_session(ident
))) {
2651 /* Create new session */
2652 if (!(s
= ast_calloc(1, sizeof(*s
)))) {
2654 goto generic_callback_out
;
2656 memcpy(&s
->sin
, requestor
, sizeof(s
->sin
));
2658 s
->waiting_thread
= AST_PTHREADT_NULL
;
2660 ast_mutex_init(&s
->__lock
);
2661 ast_mutex_lock(&s
->__lock
);
2663 s
->managerid
= rand() | (unsigned long)s
;
2664 AST_LIST_LOCK(&sessions
);
2665 AST_LIST_INSERT_HEAD(&sessions
, s
, list
);
2666 /* Hook into the last spot in the event queue */
2667 s
->eventq
= master_eventq
;
2668 while (s
->eventq
->next
)
2669 s
->eventq
= s
->eventq
->next
;
2670 ast_atomic_fetchadd_int(&s
->eventq
->usecount
, 1);
2671 ast_atomic_fetchadd_int(&num_sessions
, 1);
2672 AST_LIST_UNLOCK(&sessions
);
2675 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2676 time(&s
->sessiontimeout
);
2677 if (!s
->authenticated
&& (httptimeout
> 5))
2678 s
->sessiontimeout
+= 5;
2680 s
->sessiontimeout
+= httptimeout
;
2681 ast_mutex_unlock(&s
->__lock
);
2684 struct message m
= { 0 };
2689 for (x
= 0, v
= params
; v
&& (x
< AST_MAX_MANHEADERS
); x
++, v
= v
->next
) {
2690 hdrlen
= strlen(v
->name
) + strlen(v
->value
) + 3;
2691 m
.headers
[m
.hdrcount
] = alloca(hdrlen
);
2692 snprintf((char *) m
.headers
[m
.hdrcount
], hdrlen
, "%s: %s", v
->name
, v
->value
);
2696 if (process_message(s
, &m
)) {
2697 if (s
->authenticated
) {
2698 if (option_verbose
> 1) {
2699 if (displayconnects
)
2700 ast_verbose(VERBOSE_PREFIX_2
"HTTP Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2702 ast_log(LOG_EVENT
, "HTTP Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2704 if (option_verbose
> 1) {
2705 if (displayconnects
)
2706 ast_verbose(VERBOSE_PREFIX_2
"HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2708 ast_log(LOG_EVENT
, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2712 ast_build_string(&c
, &len
, "Content-type: text/%s\r\n", contenttype
[format
]);
2713 sprintf(tmp
, "%08lx", s
->managerid
);
2714 ast_build_string(&c
, &len
, "%s\r\n", ast_http_setcookie("mansession_id", tmp
, httptimeout
, cookie
, sizeof(cookie
)));
2715 if (format
== FORMAT_HTML
)
2716 ast_build_string(&c
, &len
, "<title>Asterisk™ Manager Interface</title>");
2717 if (format
== FORMAT_XML
) {
2718 ast_build_string(&c
, &len
, "<ajax-response>\n");
2719 } else if (format
== FORMAT_HTML
) {
2720 ast_build_string(&c
, &len
, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2721 ast_build_string(&c
, &len
, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1> Manager Tester</h1></td></tr>\r\n");
2723 ast_mutex_lock(&s
->__lock
);
2726 if (format
== FORMAT_XML
)
2727 tmp
= xml_translate(s
->outputstr
->str
, params
);
2728 else if (format
== FORMAT_HTML
)
2729 tmp
= html_translate(s
->outputstr
->str
);
2731 tmp
= s
->outputstr
->str
;
2733 retval
= malloc(strlen(workspace
) + strlen(tmp
) + 128);
2735 strcpy(retval
, workspace
);
2736 strcpy(retval
+ strlen(retval
), tmp
);
2737 c
= retval
+ strlen(retval
);
2741 if (tmp
!= s
->outputstr
->str
)
2744 s
->outputstr
= NULL
;
2746 ast_mutex_unlock(&s
->__lock
);
2747 /* Still okay because c would safely be pointing to workspace even
2748 if retval failed to allocate above */
2749 if (format
== FORMAT_XML
) {
2750 ast_build_string(&c
, &len
, "</ajax-response>\n");
2751 } else if (format
== FORMAT_HTML
)
2752 ast_build_string(&c
, &len
, "</table></body>\r\n");
2755 *title
= strdup("Server Error");
2757 ast_mutex_lock(&s
->__lock
);
2758 if (s
->needdestroy
) {
2759 if (s
->inuse
== 1) {
2760 ast_log(LOG_DEBUG
, "Need destroy, doing it now!\n");
2763 ast_log(LOG_DEBUG
, "Need destroy, but can't do it yet!\n");
2764 if (s
->waiting_thread
!= AST_PTHREADT_NULL
)
2765 pthread_kill(s
->waiting_thread
, SIGURG
);
2770 ast_mutex_unlock(&s
->__lock
);
2774 generic_callback_out
:
2776 return ast_http_error(500, "Server Error", NULL
, "Internal Server Error (out of memory)\n");
2780 static char *manager_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2782 return generic_http_callback(FORMAT_HTML
, requestor
, uri
, params
, status
, title
, contentlength
);
2785 static char *mxml_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2787 return generic_http_callback(FORMAT_XML
, requestor
, uri
, params
, status
, title
, contentlength
);
2790 static char *rawman_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2792 return generic_http_callback(FORMAT_RAW
, requestor
, uri
, params
, status
, title
, contentlength
);
2795 struct ast_http_uri rawmanuri
= {
2796 .description
= "Raw HTTP Manager Event Interface",
2799 .callback
= rawman_http_callback
,
2802 struct ast_http_uri manageruri
= {
2803 .description
= "HTML Manager Event Interface",
2806 .callback
= manager_http_callback
,
2809 struct ast_http_uri managerxmluri
= {
2810 .description
= "XML Manager Event Interface",
2813 .callback
= mxml_http_callback
,
2816 static int registered
= 0;
2817 static int webregged
= 0;
2819 int init_manager(void)
2821 struct ast_config
*cfg
= NULL
;
2824 int oldportno
= portno
;
2825 static struct sockaddr_in ba
;
2829 int newhttptimeout
= 60;
2830 struct ast_manager_user
*user
= NULL
;
2833 /* Register default actions */
2834 ast_manager_register2("Ping", 0, action_ping
, "Keepalive command", mandescr_ping
);
2835 ast_manager_register2("Events", 0, action_events
, "Control Event Flow", mandescr_events
);
2836 ast_manager_register2("Logoff", 0, action_logoff
, "Logoff Manager", mandescr_logoff
);
2837 ast_manager_register2("Hangup", EVENT_FLAG_CALL
, action_hangup
, "Hangup Channel", mandescr_hangup
);
2838 ast_manager_register("Status", EVENT_FLAG_CALL
, action_status
, "Lists channel status" );
2839 ast_manager_register2("Setvar", EVENT_FLAG_CALL
, action_setvar
, "Set Channel Variable", mandescr_setvar
);
2840 ast_manager_register2("Getvar", EVENT_FLAG_CALL
, action_getvar
, "Gets a Channel Variable", mandescr_getvar
);
2841 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG
, action_getconfig
, "Retrieve configuration", mandescr_getconfig
);
2842 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG
, action_updateconfig
, "Update basic configuration", mandescr_updateconfig
);
2843 ast_manager_register2("Redirect", EVENT_FLAG_CALL
, action_redirect
, "Redirect (transfer) a call", mandescr_redirect
);
2844 ast_manager_register2("Originate", EVENT_FLAG_CALL
, action_originate
, "Originate Call", mandescr_originate
);
2845 ast_manager_register2("Command", EVENT_FLAG_COMMAND
, action_command
, "Execute Asterisk CLI Command", mandescr_command
);
2846 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL
, action_extensionstate
, "Check Extension Status", mandescr_extensionstate
);
2847 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL
, action_timeout
, "Set Absolute Timeout", mandescr_timeout
);
2848 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL
, action_mailboxstatus
, "Check Mailbox", mandescr_mailboxstatus
);
2849 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL
, action_mailboxcount
, "Check Mailbox Message Count", mandescr_mailboxcount
);
2850 ast_manager_register2("ListCommands", 0, action_listcommands
, "List available manager commands", mandescr_listcommands
);
2851 ast_manager_register2("UserEvent", EVENT_FLAG_USER
, action_userevent
, "Send an arbitrary event", mandescr_userevent
);
2852 ast_manager_register2("WaitEvent", 0, action_waitevent
, "Wait for an event to occur", mandescr_waitevent
);
2854 ast_cli_register_multiple(cli_manager
, sizeof(cli_manager
) / sizeof(struct ast_cli_entry
));
2855 ast_extension_state_add(NULL
, NULL
, manager_state_cb
, NULL
);
2857 /* Append placeholder event so master_eventq never runs dry */
2858 append_event("Event: Placeholder\r\n\r\n", 0);
2860 portno
= DEFAULT_MANAGER_PORT
;
2861 displayconnects
= 1;
2862 cfg
= ast_config_load("manager.conf");
2864 ast_log(LOG_NOTICE
, "Unable to open management configuration manager.conf. Call management disabled.\n");
2867 val
= ast_variable_retrieve(cfg
, "general", "enabled");
2869 enabled
= ast_true(val
);
2871 val
= ast_variable_retrieve(cfg
, "general", "block-sockets");
2873 block_sockets
= ast_true(val
);
2875 val
= ast_variable_retrieve(cfg
, "general", "webenabled");
2877 webenabled
= ast_true(val
);
2879 if ((val
= ast_variable_retrieve(cfg
, "general", "port"))) {
2880 if (sscanf(val
, "%d", &portno
) != 1) {
2881 ast_log(LOG_WARNING
, "Invalid port number '%s'\n", val
);
2882 portno
= DEFAULT_MANAGER_PORT
;
2886 if ((val
= ast_variable_retrieve(cfg
, "general", "displayconnects")))
2887 displayconnects
= ast_true(val
);
2889 if ((val
= ast_variable_retrieve(cfg
, "general", "timestampevents")))
2890 timestampevents
= ast_true(val
);
2892 if ((val
= ast_variable_retrieve(cfg
, "general", "httptimeout")))
2893 newhttptimeout
= atoi(val
);
2895 memset(&ba
, 0, sizeof(ba
));
2896 ba
.sin_family
= AF_INET
;
2897 ba
.sin_port
= htons(portno
);
2899 if ((val
= ast_variable_retrieve(cfg
, "general", "bindaddr"))) {
2900 if (!inet_aton(val
, &ba
.sin_addr
)) {
2901 ast_log(LOG_WARNING
, "Invalid address '%s' specified, using 0.0.0.0\n", val
);
2902 memset(&ba
.sin_addr
, 0, sizeof(ba
.sin_addr
));
2907 if ((asock
> -1) && ((portno
!= oldportno
) || !enabled
)) {
2909 /* Can't be done yet */
2913 ast_log(LOG_WARNING
, "Unable to change management port / enabled\n");
2917 AST_LIST_LOCK(&users
);
2919 while ((cat
= ast_category_browse(cfg
, cat
))) {
2920 struct ast_variable
*var
= NULL
;
2922 if (!strcasecmp(cat
, "general"))
2925 /* Look for an existing entry, if none found - create one and add it to the list */
2926 if (!(user
= ast_get_manager_by_name_locked(cat
))) {
2927 if (!(user
= ast_calloc(1, sizeof(*user
))))
2929 /* Copy name over */
2930 ast_copy_string(user
->username
, cat
, sizeof(user
->username
));
2931 /* Insert into list */
2932 AST_LIST_INSERT_TAIL(&users
, user
, list
);
2935 /* Make sure we keep this user and don't destroy it during cleanup */
2938 var
= ast_variable_browse(cfg
, cat
);
2940 if (!strcasecmp(var
->name
, "secret")) {
2943 user
->secret
= ast_strdup(var
->value
);
2944 } else if (!strcasecmp(var
->name
, "deny") ) {
2947 user
->deny
= ast_strdup(var
->value
);
2948 } else if (!strcasecmp(var
->name
, "permit") ) {
2951 user
->permit
= ast_strdup(var
->value
);
2952 } else if (!strcasecmp(var
->name
, "read") ) {
2955 user
->read
= ast_strdup(var
->value
);
2956 } else if (!strcasecmp(var
->name
, "write") ) {
2959 user
->write
= ast_strdup(var
->value
);
2960 } else if (!strcasecmp(var
->name
, "displayconnects") )
2961 user
->displayconnects
= ast_true(var
->value
);
2963 ast_log(LOG_DEBUG
, "%s is an unknown option.\n", var
->name
);
2968 /* Perform cleanup - essentially prune out old users that no longer exist */
2969 AST_LIST_TRAVERSE_SAFE_BEGIN(&users
, user
, list
) {
2974 /* We do not need to keep this user so take them out of the list */
2975 AST_LIST_REMOVE_CURRENT(&users
, list
);
2976 /* Free their memory now */
2989 AST_LIST_TRAVERSE_SAFE_END
2991 AST_LIST_UNLOCK(&users
);
2993 ast_config_destroy(cfg
);
2995 if (webenabled
&& enabled
) {
2997 ast_http_uri_link(&rawmanuri
);
2998 ast_http_uri_link(&manageruri
);
2999 ast_http_uri_link(&managerxmluri
);
3004 ast_http_uri_unlink(&rawmanuri
);
3005 ast_http_uri_unlink(&manageruri
);
3006 ast_http_uri_unlink(&managerxmluri
);
3011 if (newhttptimeout
> 0)
3012 httptimeout
= newhttptimeout
;
3014 /* If not enabled, do nothing */
3019 asock
= socket(AF_INET
, SOCK_STREAM
, 0);
3021 ast_log(LOG_WARNING
, "Unable to create socket: %s\n", strerror(errno
));
3024 setsockopt(asock
, SOL_SOCKET
, SO_REUSEADDR
, &x
, sizeof(x
));
3025 if (bind(asock
, (struct sockaddr
*)&ba
, sizeof(ba
))) {
3026 ast_log(LOG_WARNING
, "Unable to bind socket: %s\n", strerror(errno
));
3031 if (listen(asock
, 2)) {
3032 ast_log(LOG_WARNING
, "Unable to listen on socket: %s\n", strerror(errno
));
3037 flags
= fcntl(asock
, F_GETFL
);
3038 fcntl(asock
, F_SETFL
, flags
| O_NONBLOCK
);
3040 ast_verbose("Asterisk Management interface listening on port %d\n", portno
);
3041 ast_pthread_create_background(&t
, NULL
, accept_thread
, NULL
);
3046 int reload_manager(void)
3048 manager_event(EVENT_FLAG_SYSTEM
, "Reload", "Message: Reload Requested\r\n");
3049 return init_manager();