Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2006, Digium, Inc.
* Mark Spencer <markster@digium.com>
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
*
* \brief The Asterisk Management Interface - AMI
*
* \author Mark Spencer <markster@digium.com>
*
* Channel Management and more
*
* \ref amiconf
/*! \addtogroup Group_AMI AMI functions
*/
/*! @{
Doxygen group */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Mark Spencer
committed
#include <ctype.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
Kevin P. Fleming
committed
Kevin P. Fleming
committed
#include "asterisk/channel.h"
#include "asterisk/file.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
#include "asterisk/callerid.h"
#include "asterisk/lock.h"
#include "asterisk/logger.h"
#include "asterisk/options.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/pbx.h"
#include "asterisk/md5.h"
#include "asterisk/acl.h"
#include "asterisk/utils.h"
Mark Spencer
committed
#include "asterisk/http.h"
struct fast_originate_helper {
char tech[AST_MAX_MANHEADER_LEN];
char data[AST_MAX_MANHEADER_LEN];
int timeout;
char app[AST_MAX_APP];
char appdata[AST_MAX_MANHEADER_LEN];
char cid_name[AST_MAX_MANHEADER_LEN];
char cid_num[AST_MAX_MANHEADER_LEN];
char context[AST_MAX_CONTEXT];
char exten[AST_MAX_EXTENSION];
char idtext[AST_MAX_MANHEADER_LEN];
char account[AST_MAX_ACCOUNT_CODE];
int priority;
struct ast_variable *vars;
struct eventqent {
int usecount;
int category;
ast_mutex_t lock;
struct eventqent *next;
char eventdata[1];
};
static int enabled = 0;
static int portno = DEFAULT_MANAGER_PORT;
static int asock = -1;
static int displayconnects = 1;
static int timestampevents = 0;
Mark Spencer
committed
static int httptimeout = 60;
AST_MUTEX_DEFINE_STATIC(sessionlock);
static int block_sockets = 0;
static int num_sessions = 0;
struct eventqent *master_eventq = NULL;
static struct permalias {
int num;
char *label;
} perms[] = {
{ EVENT_FLAG_SYSTEM, "system" },
{ EVENT_FLAG_CALL, "call" },
{ EVENT_FLAG_LOG, "log" },
{ EVENT_FLAG_VERBOSE, "verbose" },
{ EVENT_FLAG_COMMAND, "command" },
{ EVENT_FLAG_AGENT, "agent" },
{ EVENT_FLAG_USER, "user" },
Kevin P. Fleming
committed
{ 0, "none" },
static struct mansession {
/*! Execution thread */
pthread_t t;
/*! Thread lock -- don't use in action callbacks, it's already taken care of */
ast_mutex_t __lock;
/*! socket address */
struct sockaddr_in sin;
/*! TCP socket */
int fd;
/*! Whether or not we're busy doing an action */
int busy;
/*! Whether or not we're "dead" */
int dead;
Mark Spencer
committed
/*! Whether an HTTP manager is in use */
int inuse;
/*! Whether an HTTP session should be destroyed */
int needdestroy;
/*! Whether an HTTP session has someone waiting on events */
pthread_t waiting_thread;
/*! Unique manager identifer */
unsigned long managerid;
/*! Session timeout if HTTP */
time_t sessiontimeout;
/*! Output from manager interface */
char *outputstr;
/*! Logged in username */
char username[80];
/*! Authentication challenge */
char challenge[10];
/*! Authentication status */
int authenticated;
/*! Authorization for reading */
int readperm;
/*! Authorization for writing */
int writeperm;
/*! Buffer */
char inbuf[AST_MAX_MANHEADER_LEN];
int inlen;
int send_events;
int displaysystemname; /*!< Add system name to manager responses and events */
/* Queued events that we've not had the ability to send yet */
struct eventqent *eventq;
/* Timeout for ast_carefulwrite() */
int writetimeout;
struct mansession *next;
} *sessions = NULL;
static struct manager_action *first_action = NULL;
AST_MUTEX_DEFINE_STATIC(actionlock);
/*! authority_to_str: Convert authority code to string with serveral options */
Malcolm Davenport
committed
static char *authority_to_str(int authority, char *res, int reslen)
{
int running_total = 0, i;
memset(res, 0, reslen);
for (i=0; i<sizeof(perms) / sizeof(perms[0]) - 1; i++) {
if (authority & perms[i].num) {
if (*res) {
strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
running_total++;
}
strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
running_total += strlen(perms[i].label);
}
}
ast_copy_string(res, "<none>", reslen);
Malcolm Davenport
committed
return res;
}
Russell Bryant
committed
static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
int which = 0;
ast_mutex_lock(&actionlock);
for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
ret = ast_strdup(cur->action);
break; /* make sure we exit even if ast_strdup() returns NULL */
}
}
ast_mutex_unlock(&actionlock);
}
static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
Mark Spencer
committed
{
while (*src && (*maxlen > 6)) {
Mark Spencer
committed
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
case '<':
strcpy(*dst, "<");
(*dst) += 4;
*maxlen -= 4;
break;
case '>':
strcpy(*dst, ">");
(*dst) += 4;
*maxlen -= 4;
break;
case '\"':
strcpy(*dst, """);
(*dst) += 6;
*maxlen -= 6;
break;
case '\'':
strcpy(*dst, "'");
(*dst) += 6;
*maxlen -= 6;
break;
case '&':
strcpy(*dst, "&");
(*dst) += 4;
*maxlen -= 4;
break;
default:
*(*dst)++ = lower ? tolower(*src) : *src;
(*maxlen)--;
}
src++;
}
}
Mark Spencer
committed
static char *xml_translate(char *in, struct ast_variable *vars)
{
struct ast_variable *v;
Mark Spencer
committed
char *out, *tmp, *var, *val;
Mark Spencer
committed
int colons = 0;
int breaks = 0;
Mark Spencer
committed
int count = 1;
int escaped = 0;
int inobj = 0;
int x;
v = vars;
Mark Spencer
committed
if (!dest && !strcasecmp(v->name, "ajaxdest"))
dest = v->value;
else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
objtype = v->value;
v = v->next;
}
if (!dest)
dest = "unknown";
if (!objtype)
objtype = "generic";
Mark Spencer
committed
if (in[x] == ':')
colons++;
else if (in[x] == '\n')
breaks++;
else if (strchr("&\"<>", in[x]))
escaped++;
}
len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&" */
Mark Spencer
committed
out = malloc(len);
if (!out)
return 0;
tmp = out;
Mark Spencer
committed
var = in;
Mark Spencer
committed
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
if (*in) {
if ((count > 3) && inobj) {
ast_build_string(&tmp, &len, " /></response>\n");
inobj = 0;
}
count = 0;
while (*in && (*in < 32)) {
*in = '\0';
in++;
count++;
}
val = strchr(var, ':');
if (val) {
*val = '\0';
val++;
if (*val == ' ')
val++;
if (!inobj) {
ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
inobj = 1;
}
ast_build_string(&tmp, &len, " ");
xml_copy_escape(&tmp, &len, var, 1);
ast_build_string(&tmp, &len, "='");
xml_copy_escape(&tmp, &len, val, 0);
ast_build_string(&tmp, &len, "'");
}
}
}
if (inobj)
ast_build_string(&tmp, &len, " /></response>\n");
return out;
}
static char *html_translate(char *in)
{
int x;
int colons = 0;
int breaks = 0;
Mark Spencer
committed
char *tmp, *var, *val, *out;
Mark Spencer
committed
if (in[x] == ':')
colons++;
if (in[x] == '\n')
breaks++;
}
len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
out = malloc(len);
if (!out)
return 0;
tmp = out;
Mark Spencer
committed
var = in;
Mark Spencer
committed
if (*in) {
if ((count % 4) == 0){
ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
}
count = 0;
while (*in && (*in < 32)) {
*in = '\0';
in++;
count++;
}
val = strchr(var, ':');
if (val) {
*val = '\0';
val++;
if (*val == ' ')
val++;
ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
}
}
}
return out;
}
void astman_append(struct mansession *s, const char *fmt, ...)
{
char *stuff;
int res;
va_list ap;
Mark Spencer
committed
char *tmp;
va_start(ap, fmt);
res = vasprintf(&stuff, fmt, ap);
va_end(ap);
if (res == -1) {
ast_log(LOG_ERROR, "Memory allocation failure\n");
return;
}
if (s->fd > -1)
ast_carefulwrite(s->fd, stuff, strlen(stuff), s->writetimeout);
else {
tmp = realloc(s->outputstr, (s->outputstr ? strlen(s->outputstr) : 0) + strlen(stuff) + 1);
if (tmp) {
if (!s->outputstr)
tmp[0] = '\0';
s->outputstr = tmp;
strcat(s->outputstr, stuff);
Mark Spencer
committed
}
}
}
static int handle_showmancmd(int fd, int argc, char *argv[])
{
struct manager_action *cur = first_action;
Malcolm Davenport
committed
char authority[80];
int num;
if (argc != 4)
return RESULT_SHOWUSAGE;
ast_mutex_lock(&actionlock);
while (cur) { /* Walk the list of actions */
for (num = 3; num < argc; num++) {
if (!strcasecmp(cur->action, argv[num])) {
Malcolm Davenport
committed
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 : "");
}
}
cur = cur->next;
}
ast_mutex_unlock(&actionlock);
return RESULT_SUCCESS;
}
/*! \brief handle_showmancmds: CLI command */
Kevin P. Fleming
committed
/* Should change to "manager show commands" */
static int handle_showmancmds(int fd, int argc, char *argv[])
{
struct manager_action *cur = first_action;
Malcolm Davenport
committed
char authority[80];
Kevin P. Fleming
committed
char *format = " %-15.15s %-15.15s %-55.55s\n";
ast_mutex_lock(&actionlock);
Malcolm Davenport
committed
ast_cli(fd, format, "Action", "Privilege", "Synopsis");
Kevin P. Fleming
committed
ast_cli(fd, format, "------", "---------", "--------");
while (cur) { /* Walk the list of actions */
Malcolm Davenport
committed
ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
ast_mutex_unlock(&actionlock);
/*! \brief handle_showmanconn: CLI command show manager connected */
Kevin P. Fleming
committed
/* Should change to "manager show connected" */
static int handle_showmanconn(int fd, int argc, char *argv[])
{
struct mansession *s;
char iabuf[INET_ADDRSTRLEN];
ast_mutex_lock(&sessionlock);
ast_cli(fd, format, "Username", "IP Address");
while (s) {
Mark Spencer
committed
ast_cli(fd, format,s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
ast_mutex_unlock(&sessionlock);
/*! \brief handle_showmanconn: CLI command show manager connected */
/* Should change to "manager show connected" */
static int handle_showmaneventq(int fd, int argc, char *argv[])
{
struct eventqent *s;
ast_mutex_lock(&sessionlock);
s = master_eventq;
while (s) {
ast_cli(fd, "Usecount: %d\n",s->usecount);
ast_cli(fd, "Category: %d\n", s->category);
ast_cli(fd, "Event:\n%s", s->eventdata);
s = s->next;
}
ast_mutex_unlock(&sessionlock);
return RESULT_SUCCESS;
}
static char showmancmd_help[] =
"Usage: show manager command <actionname>\n"
" Shows the detailed description for a specific Asterisk manager interface command.\n";
static char showmancmds_help[] =
"Usage: show manager commands\n"
" Prints a listing of all the available Asterisk manager interface commands.\n";
static char showmanconn_help[] =
"Usage: show manager connected\n"
" Prints a listing of the users that are currently connected to the\n"
"Asterisk manager interface.\n";
static char showmaneventq_help[] =
"Usage: show manager eventq\n"
" Prints a listing of all events pending in the Asterisk manger\n"
"event queue.\n";
static struct ast_cli_entry show_mancmd_cli =
{ { "show", "manager", "command", NULL },
handle_showmancmd, "Show a manager interface command", showmancmd_help, complete_show_mancmd };
static struct ast_cli_entry show_mancmds_cli =
{ { "show", "manager", "commands", NULL },
handle_showmancmds, "List manager interface commands", showmancmds_help };
static struct ast_cli_entry show_manconn_cli =
{ { "show", "manager", "connected", NULL },
handle_showmanconn, "Show connected manager interface users", showmanconn_help };
static struct ast_cli_entry show_maneventq_cli =
{ { "show", "manager", "eventq", NULL },
handle_showmaneventq, "Show manager interface queued events", showmaneventq_help };
static void unuse_eventqent(struct eventqent *e)
{
/* XXX Need to atomically decrement the users. Change this to atomic_dec
one day when we have such a beast XXX */
int val;
ast_mutex_lock(&e->lock);
e->usecount--;
ast_mutex_unlock(&e->lock);
/* Wake up sleeping beauty */
if (val)
pthread_kill(t, SIGURG);
}
static void free_session(struct mansession *s)
{
struct eventqent *eqe;
if (s->fd > -1)
close(s->fd);
Mark Spencer
committed
if (s->outputstr)
free(s->outputstr);
ast_mutex_destroy(&s->__lock);
eqe = s->eventq;
s->eventq = s->eventq->next;
}
free(s);
}
static void destroy_session(struct mansession *s)
{
struct mansession *cur, *prev = NULL;
ast_mutex_lock(&sessionlock);
if (cur == s)
break;
prev = cur;
cur = cur->next;
}
if (cur) {
if (prev)
prev->next = cur->next;
else
sessions = cur->next;
free_session(s);
ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
ast_mutex_unlock(&sessionlock);
char *astman_get_header(struct message *m, char *var)
{
char cmp[80];
int x;
snprintf(cmp, sizeof(cmp), "%s: ", var);
for (x=0; x<m->hdrcount; x++)
if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
return m->headers[x] + strlen(cmp);
return "";
}
struct ast_variable *astman_get_variables(struct message *m)
{
struct ast_variable *head = NULL, *cur;
char *var, *val;
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(vars)[32];
);
varlen = strlen("Variable: ");
for (x = 0; x < m->hdrcount; x++) {
if (strncasecmp("Variable: ", m->headers[x], varlen))
continue;
Russell Bryant
committed
parse = ast_strdupa(m->headers[x] + varlen);
AST_STANDARD_APP_ARGS(args, parse);
if (args.argc) {
for (y = 0; y < args.argc; y++) {
if (!args.vars[y])
var = val = ast_strdupa(args.vars[y]);
strsep(&val, "=");
if (!val || ast_strlen_zero(var))
continue;
cur = ast_variable_new(var, val);
if (head) {
cur->next = head;
head = cur;
} else
head = cur;
}
}
}
return head;
}
Kevin P. Fleming
committed
Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
hold the session lock _or_ be running in an action callback (in which case s->busy will
be non-zero). In either of these cases, there is no need to lock-protect the session's
fd, since no other output will be sent (events will be queued), and no input will
be read until either the current action finishes or get_input() obtains the session
lock.
*/
void astman_send_error(struct mansession *s, struct message *m, char *error)
char *id = astman_get_header(m,"ActionID");
Kevin P. Fleming
committed
astman_append(s, "Response: Error\r\n");
if (!ast_strlen_zero(id))
astman_append(s, "ActionID: %s\r\n", id);
astman_append(s, "Message: %s\r\n\r\n", error);
void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
char *id = astman_get_header(m,"ActionID");
Kevin P. Fleming
committed
astman_append(s, "Response: %s\r\n", resp);
if (!ast_strlen_zero(id))
astman_append(s, "ActionID: %s\r\n", id);
astman_append(s, "Message: %s\r\n\r\n", msg);
astman_append(s, "\r\n");
void astman_send_ack(struct mansession *s, struct message *m, char *msg)
astman_send_response(s, m, "Success", msg);
/*! Tells you if smallstr exists inside bigstr
Anthony Minessale II
committed
which is delim by delim and uses no buf or stringsep
ast_instring("this|that|more","this",',') == 1;
feel free to move this to app.c -anthm */
static int ast_instring(char *bigstr, char *smallstr, char delim)
{
char *val = bigstr, *next;
Anthony Minessale II
committed
do {
if ((next = strchr(val, delim))) {
if (!strncmp(val, smallstr, (next - val)))
return 1;
else
continue;
} else
return !strcmp(smallstr, val);
Anthony Minessale II
committed
} while (*(val = (next + 1)));
Anthony Minessale II
committed
return 0;
Anthony Minessale II
committed
}
Anthony Minessale II
committed
int x = 0, ret = 0;
Anthony Minessale II
committed
for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
if (ast_instring(instr, perms[x].label, ','))
ret |= perms[x].num;
return ret;
}
static int ast_is_number(char *string)
{
Anthony Minessale II
committed
int ret = 1, x = 0;
if (!string)
Anthony Minessale II
committed
return 0;
for (x=0; x < strlen(string); x++) {
if (!(string[x] >= 48 && string[x] <= 57)) {
Anthony Minessale II
committed
ret = 0;
break;
Anthony Minessale II
committed
return ret ? atoi(string) : 0;
}
static int ast_strings_to_mask(char *string)
{
int x, ret = -1;
Anthony Minessale II
committed
x = ast_is_number(string);
if (x) {
Anthony Minessale II
committed
ret = x;
} else if (ast_strlen_zero(string)) {
Anthony Minessale II
committed
ret = -1;
} else if (ast_false(string)) {
Anthony Minessale II
committed
ret = 0;
} else if (ast_true(string)) {
ret = 0;
for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
ret |= perms[x].num;
} else {
Anthony Minessale II
committed
ret = 0;
for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
Anthony Minessale II
committed
if (ast_instring(string, perms[x].label, ','))
ret |= perms[x].num;
}
Anthony Minessale II
committed
}
Anthony Minessale II
committed
Rather than braindead on,off this now can also accept a specific int mask value
or a ',' delim list of mask strings (the same as manager.conf) -anthm
*/
static int set_eventmask(struct mansession *s, char *eventmask)
{
Anthony Minessale II
committed
int maskint = ast_strings_to_mask(eventmask);
ast_mutex_lock(&s->__lock);
if (maskint >= 0)
s->send_events = maskint;
ast_mutex_unlock(&s->__lock);
Anthony Minessale II
committed
return maskint;
}
static int authenticate(struct mansession *s, struct message *m)
{
struct ast_config *cfg;
char iabuf[INET_ADDRSTRLEN];
char *user = astman_get_header(m, "Username");
char *pass = astman_get_header(m, "Secret");
char *authtype = astman_get_header(m, "AuthType");
char *key = astman_get_header(m, "Key");
char *events = astman_get_header(m, "Events");
if (!cfg)
return -1;
cat = ast_category_browse(cfg, NULL);
if (strcasecmp(cat, "general")) {
/* This is a user */
if (!strcasecmp(cat, user)) {
struct ast_variable *v;
struct ast_ha *ha = NULL;
char *password = NULL;
v = ast_variable_browse(cfg, cat);
while (v) {
if (!strcasecmp(v->name, "secret")) {
password = v->value;
} else if (!strcasecmp(v->name, "displaysystemname")) {
if (ast_true(v->value)) {
if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
s->displaysystemname = 1;
} else {
ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
}
}
} else if (!strcasecmp(v->name, "permit") ||
!strcasecmp(v->name, "deny")) {
ha = ast_append_ha(v->name, v->value, ha);
} else if (!strcasecmp(v->name, "writetimeout")) {
int val = atoi(v->value);
if (val < 100)
ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
else
s->writetimeout = val;
}
v = v->next;
}
if (ha && !ast_apply_ha(ha, &(s->sin))) {
Mark Spencer
committed
ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
return -1;
} else if (ha)
ast_free_ha(ha);
if (!ast_strlen_zero(key) && s->challenge) {
char md5key[256] = "";
struct MD5Context md5;
unsigned char digest[16];
MD5Init(&md5);
MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
MD5Update(&md5, (unsigned char *) password, strlen(password));
len += sprintf(md5key + len, "%2.2x", digest[x]);
if (!strcmp(md5key, key))
break;
else {
Mark Spencer
committed
} else if (password && !strcmp(password, pass)) {
Mark Spencer
committed
ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
return -1;
}
}
}
cat = ast_category_browse(cfg, cat);
}
if (cat) {
ast_copy_string(s->username, cat, sizeof(s->username));
s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
if (events)
set_eventmask(s, events);
ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
/*! \brief PING: Manager PING */
static char mandescr_ping[] =
Mark Spencer
committed
"Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
" manager connection open.\n"
"Variables: NONE\n";
static int action_ping(struct mansession *s, struct message *m)
{
astman_send_response(s, m, "Pong", NULL);
Mark Spencer
committed
/*! \brief WAITEVENT: Manager WAITEVENT */
static char mandescr_waitevent[] =
"Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
"a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
"session, events will be generated and queued.\n"
"Variables: \n"
" Timeout: Maximum time to wait for events\n";
static int action_waitevent(struct mansession *s, struct message *m)
{
char *timeouts = astman_get_header(m, "Timeout");
int timeout = -1, max;
int x;
int needexit = 0;
time_t now;
struct eventqent *eqe;
char *id = astman_get_header(m,"ActionID");
Mark Spencer
committed
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
if (!ast_strlen_zero(id))
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
if (!ast_strlen_zero(timeouts)) {
sscanf(timeouts, "%i", &timeout);
}
ast_mutex_lock(&s->__lock);
if (s->waiting_thread != AST_PTHREADT_NULL) {
pthread_kill(s->waiting_thread, SIGURG);
}
if (s->sessiontimeout) {
time(&now);
max = s->sessiontimeout - now - 10;
if (max < 0)
max = 0;
if ((timeout < 0) || (timeout > max))
timeout = max;
if (!s->send_events)
s->send_events = -1;
/* Once waitevent is called, always queue events from now on */
if (s->busy == 1)
s->busy = 2;
}
ast_mutex_unlock(&s->__lock);
s->waiting_thread = pthread_self();
if (option_debug)
ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
for (x=0; ((x < timeout) || (timeout < 0)); x++) {
Mark Spencer
committed
ast_mutex_lock(&s->__lock);
if (s->eventq && s->eventq->next)
Mark Spencer
committed
needexit = 1;
if (s->waiting_thread != pthread_self())
needexit = 1;
if (s->needdestroy)
needexit = 1;
ast_mutex_unlock(&s->__lock);
if (needexit)
break;
if (s->fd > 0) {
if (ast_wait_for_input(s->fd, 1000))
break;
} else {
sleep(1);
}
}
if (option_debug)
ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
Mark Spencer
committed
ast_mutex_lock(&s->__lock);
if (s->waiting_thread == pthread_self()) {
astman_send_response(s, m, "Success", "Waiting for Event...");
/* Only show events if we're the most recent waiter */
while(s->eventq->next) {
eqe = s->eventq->next;
if (((s->readperm & eqe->category) == eqe->category) &&
((s->send_events & eqe->category) == eqe->category)) {
astman_append(s, "%s", eqe->eventdata);
}
unuse_eventqent(s->eventq);
s->eventq = eqe;
Mark Spencer
committed
}
astman_append(s,
"Event: WaitEventComplete\r\n"
"%s"
Mark Spencer
committed
s->waiting_thread = AST_PTHREADT_NULL;
} else {
ast_log(LOG_DEBUG, "Abandoning event request!\n");
}
ast_mutex_unlock(&s->__lock);
return 0;
}
static char mandescr_listcommands[] =
"Description: Returns the action name and synopsis for every\n"
" action that is available to the user\n"
"Variables: NONE\n";
static int action_listcommands(struct mansession *s, struct message *m)
{
struct manager_action *cur = first_action;
char idText[256] = "";
Kevin P. Fleming
committed
char temp[BUFSIZ];
char *id = astman_get_header(m,"ActionID");
if (!ast_strlen_zero(id))
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
astman_append(s, "Response: Success\r\n%s", idText);
ast_mutex_lock(&actionlock);
while (cur) { /* Walk the list of actions */
if ((s->writeperm & cur->authority) == cur->authority)
astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
cur = cur->next;
}
ast_mutex_unlock(&actionlock);
astman_append(s, "\r\n");
static char mandescr_events[] =
"Description: Enable/Disable sending of events to this manager\n"
" client.\n"
"Variables:\n"
Anthony Minessale II
committed
" EventMask: 'on' if all events should be sent,\n"
" 'off' if no events should be sent,\n"
" 'system,call,log' to select which flags events should have to be sent.\n";
static int action_events(struct mansession *s, struct message *m)
{
char *mask = astman_get_header(m, "EventMask");
int res;
res = set_eventmask(s, mask);
if (res > 0)
astman_send_response(s, m, "Events On", NULL);
else if (res == 0)
astman_send_response(s, m, "Events Off", NULL);
Anthony Minessale II
committed