Newer
Older
static char mandescr_logoff[] =
"Description: Logoff this manager session\n"
"Variables: NONE\n";
static int action_logoff(struct mansession *s, struct message *m)
{
astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
static char mandescr_hangup[] =
"Description: Hangup a channel\n"
"Variables: \n"
" Channel: The channel name to be hungup\n";
static int action_hangup(struct mansession *s, struct message *m)
{
struct ast_channel *c = NULL;
char *name = astman_get_header(m, "Channel");
astman_send_error(s, m, "No channel specified");
Kevin P. Fleming
committed
c = ast_get_channel_by_name_locked(name);
astman_send_error(s, m, "No such channel");
return 0;
}
ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
ast_channel_unlock(c);
astman_send_ack(s, m, "Channel Hungup");
static char mandescr_setvar[] =
"Description: Set a global or local channel variable.\n"
"Variables: (Names marked with * are required)\n"
" *Variable: Variable name\n"
" *Value: Value\n";
static int action_setvar(struct mansession *s, struct message *m)
{
struct ast_channel *c = NULL;
char *name = astman_get_header(m, "Channel");
char *varname = astman_get_header(m, "Variable");
char *varval = astman_get_header(m, "Value");
astman_send_error(s, m, "No variable specified");
return 0;
}
if (ast_strlen_zero(varval)) {
astman_send_error(s, m, "No value specified");
return 0;
}
if (!ast_strlen_zero(name)) {
c = ast_get_channel_by_name_locked(name);
if (!c) {
astman_send_error(s, m, "No such channel");
return 0;
}
}
pbx_builtin_setvar_helper(c, varname, varval);
ast_channel_unlock(c);
astman_send_ack(s, m, "Variable Set");
return 0;
}
static char mandescr_getvar[] =
"Description: Get the value of a global or local channel variable.\n"
"Variables: (Names marked with * are required)\n"
" Channel: Channel to read variable from\n"
" *Variable: Variable name\n"
" ActionID: Optional Action id for message matching.\n";
static int action_getvar(struct mansession *s, struct message *m)
{
struct ast_channel *c = NULL;
char *name = astman_get_header(m, "Channel");
char *varname = astman_get_header(m, "Variable");
char *id = astman_get_header(m,"ActionID");
char *varval;
char workspace[1024];
if (ast_strlen_zero(varname)) {
astman_send_error(s, m, "No variable specified");
return 0;
}
if (!ast_strlen_zero(name)) {
c = ast_get_channel_by_name_locked(name);
if (!c) {
astman_send_error(s, m, "No such channel");
return 0;
}
}
Tilghman Lesher
committed
if (varname[strlen(varname) - 1] == ')') {
ast_func_read(c, varname, workspace, sizeof(workspace));
Tilghman Lesher
committed
} else {
pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
}
if (c)
ast_channel_unlock(c);
astman_append(s, "Response: Success\r\n"
"Variable: %s\r\nValue: %s\r\n", varname, varval);
if (!ast_strlen_zero(id))
astman_append(s, "ActionID: %s\r\n",id);
astman_append(s, "\r\n");
return 0;
}
/*! \brief action_status: Manager "status" command to show channels */
/* Needs documentation... */
static int action_status(struct mansession *s, struct message *m)
{
char *id = astman_get_header(m,"ActionID");
Mark Spencer
committed
char *name = astman_get_header(m,"Channel");
Kevin P. Fleming
committed
struct timeval now = ast_tvnow();
int all = ast_strlen_zero(name); /* set if we want all channels */
astman_send_ack(s, m, "Channel status will follow");
if (!ast_strlen_zero(id))
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
Kevin P. Fleming
committed
if (all)
c = ast_channel_walk_locked(NULL);
else {
c = ast_get_channel_by_name_locked(name);
Mark Spencer
committed
if (!c) {
astman_send_error(s, m, "No such channel");
return 0;
}
}
Kevin P. Fleming
committed
/* if we look by name, we break after the first iteration */
Mark Spencer
committed
if (c->_bridge)
snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
bridge[0] = '\0';
if (c->cdr) {
elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
}
astman_append(s,
Kevin P. Fleming
committed
"Privilege: Call\r\n"
Tilghman Lesher
committed
"CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
"CallerIDNum: %s\r\n"
Mark Spencer
committed
"Account: %s\r\n"
"State: %s\r\n"
"Context: %s\r\n"
"Extension: %s\r\n"
"Priority: %d\r\n"
S_OR(c->cid.cid_num, "<unknown>"),
S_OR(c->cid.cid_num, "<unknown>"),
S_OR(c->cid.cid_name, "<unknown>"),
Mark Spencer
committed
c->accountcode,
c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
astman_append(s,
Kevin P. Fleming
committed
"Privilege: Call\r\n"
Tilghman Lesher
committed
"CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
"CallerIDNum: %s\r\n"
Mark Spencer
committed
"Account: %s\r\n"
S_OR(c->cid.cid_num, "<unknown>"),
S_OR(c->cid.cid_num, "<unknown>"),
S_OR(c->cid.cid_name, "<unknown>"),
Mark Spencer
committed
c->accountcode,
ast_state2str(c->_state), bridge, c->uniqueid, idText);
ast_channel_unlock(c);
Kevin P. Fleming
committed
if (!all)
Mark Spencer
committed
break;
c = ast_channel_walk_locked(c);
astman_append(s,
"Event: StatusComplete\r\n"
"%s"
"\r\n",idText);
static char mandescr_redirect[] =
"Description: Redirect (transfer) a call.\n"
"Variables: (Names marked with * are required)\n"
" *Channel: Channel to redirect\n"
" ExtraChannel: Second call leg to transfer (optional)\n"
" *Exten: Extension to transfer to\n"
" *Context: Context to transfer to\n"
" *Priority: Priority to transfer to\n"
" ActionID: Optional Action id for message matching.\n";
/*! \brief action_redirect: The redirect manager command */
static int action_redirect(struct mansession *s, struct message *m)
{
char *name = astman_get_header(m, "Channel");
char *name2 = astman_get_header(m, "ExtraChannel");
char *exten = astman_get_header(m, "Exten");
char *context = astman_get_header(m, "Context");
char *priority = astman_get_header(m, "Priority");
struct ast_channel *chan, *chan2 = NULL;
if (ast_strlen_zero(name)) {
astman_send_error(s, m, "Channel not specified");
if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
astman_send_error(s, m, "Invalid priority\n");
return 0;
}
/* XXX watch out, possible deadlock!!! */
chan = ast_get_channel_by_name_locked(name);
char buf[BUFSIZ];
snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
astman_send_error(s, m, buf);
chan2 = ast_get_channel_by_name_locked(name2);
res = ast_async_goto(chan, context, exten, pi);
if (chan2)
res = ast_async_goto(chan2, context, exten, pi);
else
res = -1;
astman_send_ack(s, m, "Dual Redirect successful");
astman_send_error(s, m, "Secondary redirect failed");
astman_send_ack(s, m, "Redirect successful");
astman_send_error(s, m, "Redirect failed");
ast_channel_unlock(chan);
ast_channel_unlock(chan2);
static char mandescr_command[] =
"Description: Run a CLI command.\n"
"Variables: (Names marked with * are required)\n"
" *Command: Asterisk CLI command to run\n"
" ActionID: Optional Action id for message matching.\n";
/*! \brief action_command: Manager command "command" - execute CLI command */
static int action_command(struct mansession *s, struct message *m)
{
char *cmd = astman_get_header(m, "Command");
char *id = astman_get_header(m, "ActionID");
astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
if (!ast_strlen_zero(id))
astman_append(s, "ActionID: %s\r\n", id);
/* FIXME: Wedge a ActionID response in here, waiting for later changes */
astman_append(s, "--END COMMAND--\r\n\r\n");
static void *fast_originate(void *data)
{
struct fast_originate_helper *in = data;
int res;
int reason = 0;
struct ast_channel *chan = NULL;
res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
S_OR(in->cid_num, NULL),
S_OR(in->cid_name, NULL),
in->vars, in->account, &chan);
} else {
res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
S_OR(in->cid_num, NULL),
S_OR(in->cid_name, NULL),
in->vars, in->account, &chan);
Olle Johansson
committed
/* Tell the manager what happened with the channel */
manager_event(EVENT_FLAG_CALL,
res ? "OriginateFailure" : "OriginateSuccess",
Olle Johansson
committed
"%s"
"Channel: %s/%s\r\n"
"Context: %s\r\n"
"Exten: %s\r\n"
"Reason: %d\r\n"
"Uniqueid: %s\r\n"
Tilghman Lesher
committed
"CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
"CallerIDNum: %s\r\n"
Olle Johansson
committed
"CallerIDName: %s\r\n",
in->idtext, in->tech, in->data, in->context, in->exten, reason,
chan ? chan->uniqueid : "<null>",
S_OR(in->cid_num, "<unknown>"),
S_OR(in->cid_num, "<unknown>"),
S_OR(in->cid_name, "<unknown>")
Olle Johansson
committed
);
/* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
if (chan)
ast_channel_unlock(chan);
free(in);
return NULL;
}
static char mandescr_originate[] =
"Description: Generates an outgoing call to a Extension/Context/Priority or\n"
" Application/Data\n"
"Variables: (Names marked with * are required)\n"
" *Channel: Channel name to call\n"
" Exten: Extension to use (requires 'Context' and 'Priority')\n"
" Context: Context to use (requires 'Exten' and 'Priority')\n"
" Priority: Priority to use (requires 'Exten' and 'Context')\n"
" Application: Application to use\n"
" Data: Data to use (requires 'Application')\n"
" Timeout: How long to wait for call to be answered (in ms)\n"
" CallerID: Caller ID to be set on the outgoing channel\n"
" Variable: Channel variable to set, multiple Variable: headers are allowed\n"
" Account: Account code\n"
" Async: Set to 'true' for fast origination\n";
static int action_originate(struct mansession *s, struct message *m)
{
char *name = astman_get_header(m, "Channel");
char *exten = astman_get_header(m, "Exten");
char *context = astman_get_header(m, "Context");
char *priority = astman_get_header(m, "Priority");
char *timeout = astman_get_header(m, "Timeout");
char *callerid = astman_get_header(m, "CallerID");
char *account = astman_get_header(m, "Account");
char *app = astman_get_header(m, "Application");
char *appdata = astman_get_header(m, "Data");
char *async = astman_get_header(m, "Async");
char *id = astman_get_header(m, "ActionID");
struct ast_variable *vars = astman_get_variables(m);
char *l = NULL, *n = NULL;
int pi = 0;
int res;
int to = 30000;
int reason = 0;
char tmp[256];
char tmp2[256];
pthread_t th;
pthread_attr_t attr;
astman_send_error(s, m, "Channel not specified");
if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
astman_send_error(s, m, "Invalid priority\n");
return 0;
}
if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
astman_send_error(s, m, "Invalid timeout\n");
ast_copy_string(tmp, name, sizeof(tmp));
tech = tmp;
data = strchr(tmp, '/');
if (!data) {
astman_send_error(s, m, "Invalid channel\n");
ast_copy_string(tmp2, callerid, sizeof(tmp2));
ast_callerid_parse(tmp2, &n, &l);
if (n) {
if (ast_strlen_zero(n))
n = NULL;
}
if (l) {
ast_shrink_phone_number(l);
if (ast_strlen_zero(l))
l = NULL;
}
James Golovich
committed
if (ast_true(async)) {
struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper));
James Golovich
committed
if (!fast) {
res = -1;
James Golovich
committed
} else {
memset(fast, 0, sizeof(struct fast_originate_helper));
if (!ast_strlen_zero(id))
snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
ast_copy_string(fast->tech, tech, sizeof(fast->tech));
ast_copy_string(fast->data, data, sizeof(fast->data));
ast_copy_string(fast->app, app, sizeof(fast->app));
ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
fast->vars = vars;
ast_copy_string(fast->context, context, sizeof(fast->context));
ast_copy_string(fast->exten, exten, sizeof(fast->exten));
ast_copy_string(fast->account, account, sizeof(fast->account));
fast->timeout = to;
fast->priority = pi;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
James Golovich
committed
if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
res = -1;
James Golovich
committed
} else {
res = 0;
}
}
res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
} else {
if (exten && context && pi)
res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
else {
astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
return 0;
}
astman_send_ack(s, m, "Originate successfully queued");
astman_send_error(s, m, "Originate failed");
/*! \brief Help text for manager command mailboxstatus
*/
static char mandescr_mailboxstatus[] =
"Description: Checks a voicemail account for status.\n"
"Variables: (Names marked with * are required)\n"
" *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
" ActionID: Optional ActionID for message matching.\n"
"Returns number of messages.\n"
" Message: Mailbox Status\n"
" Mailbox: <mailboxid>\n"
" Waiting: <count>\n"
"\n";
static int action_mailboxstatus(struct mansession *s, struct message *m)
{
char *mailbox = astman_get_header(m, "Mailbox");
char *id = astman_get_header(m,"ActionID");
char idText[256] = "";
if (ast_strlen_zero(mailbox)) {
astman_send_error(s, m, "Mailbox not specified");
if (!ast_strlen_zero(id))
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
ret = ast_app_has_voicemail(mailbox, NULL);
astman_append(s, "Response: Success\r\n"
"Message: Mailbox Status\r\n"
"Mailbox: %s\r\n"
"Waiting: %d\r\n\r\n", idText, mailbox, ret);
return 0;
}
static char mandescr_mailboxcount[] =
"Description: Checks a voicemail account for new messages.\n"
"Variables: (Names marked with * are required)\n"
" *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
" ActionID: Optional ActionID for message matching.\n"
"Returns number of new and old messages.\n"
" Message: Mailbox Message Count\n"
" Mailbox: <mailboxid>\n"
" NewMessages: <count>\n"
" OldMessages: <count>\n"
"\n";
static int action_mailboxcount(struct mansession *s, struct message *m)
{
char *mailbox = astman_get_header(m, "Mailbox");
char *id = astman_get_header(m,"ActionID");
char idText[256] = "";
if (ast_strlen_zero(mailbox)) {
astman_send_error(s, m, "Mailbox not specified");
Tilghman Lesher
committed
ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
if (!ast_strlen_zero(id)) {
snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
astman_append(s, "Response: Success\r\n"
"Message: Mailbox Message Count\r\n"
"Mailbox: %s\r\n"
"NewMessages: %d\r\n"
"OldMessages: %d\r\n"
"\r\n",
static char mandescr_extensionstate[] =
"Description: Report the extension state for given extension.\n"
" If the extension has a hint, will use devicestate to check\n"
" the status of the device connected to the extension.\n"
"Variables: (Names marked with * are required)\n"
" *Exten: Extension to check state on\n"
" *Context: Context for extension\n"
" ActionId: Optional ID for this transaction\n"
"Will return an \"Extension Status\" message.\n"
"The response will include the hint for the extension and the status.\n";
static int action_extensionstate(struct mansession *s, struct message *m)
{
char *exten = astman_get_header(m, "Exten");
char *context = astman_get_header(m, "Context");
char *id = astman_get_header(m,"ActionID");
char idText[256] = "";
if (ast_strlen_zero(exten)) {
astman_send_error(s, m, "Extension not specified");
return 0;
}
if (ast_strlen_zero(context))
context = "default";
status = ast_extension_state(NULL, context, exten);
ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
if (!ast_strlen_zero(id)) {
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
astman_append(s, "Response: Success\r\n"
"Message: Extension Status\r\n"
"Exten: %s\r\n"
"Context: %s\r\n"
"Hint: %s\r\n"
"Status: %d\r\n\r\n",
idText,exten, context, hint, status);
static char mandescr_timeout[] =
"Description: Hangup a channel after a certain time.\n"
"Variables: (Names marked with * are required)\n"
" *Channel: Channel name to hangup\n"
" *Timeout: Maximum duration of the call (sec)\n"
"Acknowledges set time with 'Timeout Set' message\n";
static int action_timeout(struct mansession *s, struct message *m)
{
struct ast_channel *c = NULL;
char *name = astman_get_header(m, "Channel");
int timeout = atoi(astman_get_header(m, "Timeout"));
astman_send_error(s, m, "No channel specified");
return 0;
}
if (!timeout) {
astman_send_error(s, m, "No timeout specified");
Kevin P. Fleming
committed
c = ast_get_channel_by_name_locked(name);
astman_send_error(s, m, "No such channel");
return 0;
}
ast_channel_setwhentohangup(c, timeout);
ast_channel_unlock(c);
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
static int process_events(struct mansession *s)
{
struct eventqent *eqe;
int ret = 0;
ast_mutex_lock(&s->__lock);
if (s->fd > -1) {
s->busy--;
if (!s->eventq)
s->eventq = master_eventq;
while(s->eventq->next) {
eqe = s->eventq->next;
if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
((s->send_events & eqe->category) == eqe->category)) {
if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
ret = -1;
}
unuse_eventqent(s->eventq);
s->eventq = eqe;
}
}
ast_mutex_unlock(&s->__lock);
return ret;
}
Russell Bryant
committed
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
static char mandescr_userevent[] =
"Description: Send an event to manager sessions.\n"
"Variables: (Names marked with * are required)\n"
" *UserEvent: EventStringToSend\n"
" Header1: Content1\n"
" HeaderN: ContentN\n";
static int action_userevent(struct mansession *s, struct message *m)
{
char *event = astman_get_header(m, "UserEvent");
char body[2048] = "";
int x, bodylen = 0;
for (x = 0; x < m->hdrcount; x++) {
if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
bodylen += strlen(m->headers[x]);
ast_copy_string(body + bodylen, "\r\n", 3);
bodylen += 2;
}
}
manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
return 0;
}
static int process_message(struct mansession *s, struct message *m)
{
char action[80] = "";
char *id = astman_get_header(m,"ActionID");
char idText[256] = "";
char iabuf[INET_ADDRSTRLEN];
ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
astman_send_error(s, m, "Missing action in request");
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
if (!strcasecmp(action, "Challenge")) {
char *authtype;
authtype = astman_get_header(m, "AuthType");
if (ast_strlen_zero(s->challenge))
Tilghman Lesher
committed
snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
ast_mutex_lock(&s->__lock);
astman_append(s, "Response: Success\r\n"
ast_mutex_unlock(&s->__lock);
astman_send_error(s, m, "Must specify AuthType");
return 0;
}
} else if (!strcasecmp(action, "Login")) {
astman_send_error(s, m, "Authentication failed");
return -1;
} else {
s->authenticated = 1;
if (option_verbose > 1) {
Mark Spencer
committed
ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
}
}
Mark Spencer
committed
ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
astman_send_ack(s, m, "Authentication accepted");
} else if (!strcasecmp(action, "Logoff")) {
astman_send_error(s, m, "Authentication Required");
ast_mutex_lock(&s->__lock);
Mark Spencer
committed
s->busy++;
ast_mutex_unlock(&s->__lock);
if (!strcasecmp(action, tmp->action)) {
if ((s->writeperm & tmp->authority) == tmp->authority) {
if (tmp->func(s, m))
astman_send_error(s, m, "Permission denied");
Kevin P. Fleming
committed
astman_send_error(s, m, "Invalid/unknown command");
if (ret)
return ret;
return process_events(s);
}
static int get_input(struct mansession *s, char *output)
{
/* output must have at least sizeof(s->inbuf) space */
int res;
int x;
struct pollfd fds[1];
char iabuf[INET_ADDRSTRLEN];
for (x = 1; x < s->inlen; x++) {
if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
/* Copy output data up to and including \r\n */
memcpy(output, s->inbuf, x + 1);
/* Add trailing \0 */
output[x+1] = '\0';
/* Move remaining data back to the front */
memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
s->inlen -= (x + 1);
return 1;
}
}
if (s->inlen >= sizeof(s->inbuf) - 1) {
Mark Spencer
committed
ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf);
fds[0].fd = s->fd;
fds[0].events = POLLIN;
ast_mutex_lock(&s->__lock);
s->waiting_thread = pthread_self();
ast_mutex_unlock(&s->__lock);
ast_mutex_lock(&s->__lock);
s->waiting_thread = AST_PTHREADT_NULL;
ast_mutex_unlock(&s->__lock);
if (errno == EINTR) {
if (s->dead)
return -1;
ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
return -1;
} else if (res > 0) {
ast_mutex_lock(&s->__lock);
res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
ast_mutex_unlock(&s->__lock);
if (res < 1)
return -1;
break;
}
} while(1);
s->inlen += res;
s->inbuf[s->inlen] = '\0';
return 0;
}
static void *session_do(void *data)
{
struct mansession *s = data;
struct message m;
char iabuf[INET_ADDRSTRLEN];
ast_mutex_lock(&s->__lock);
astman_append(s, "Asterisk Call Manager/1.0\r\n");
ast_mutex_unlock(&s->__lock);
memset(&m, 0, sizeof(m));
for (;;) {
res = get_input(s, m.headers[m.hdrcount]);
if (res > 0) {
/* Strip trailing \r\n */
if (strlen(m.headers[m.hdrcount]) < 2)
continue;
m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
if (ast_strlen_zero(m.headers[m.hdrcount])) {
memset(&m, 0, sizeof(m));
} else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
} else if (s->eventq->next) {
if (process_events(s))
break;
}
if (option_verbose > 1) {
if (displayconnects)
ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
}
Mark Spencer
committed
ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
if (option_verbose > 1) {
ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
}
Mark Spencer
committed
ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
}
destroy_session(s);
return NULL;
}
static void *accept_thread(void *ignore)
{
int as;
struct sockaddr_in sin;
socklen_t sinlen;
struct mansession *s, *prev = NULL, *next;
Mark Spencer
committed
time_t now;
struct pollfd pfds[1];
char iabuf[INET_ADDRSTRLEN];
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Mark Spencer
committed
time(&now);
ast_mutex_lock(&sessionlock);
prev = NULL;
s = sessions;
Mark Spencer
committed
next = s->next;
if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
Mark Spencer
committed
if (prev)
prev->next = next;
else
sessions = next;
if (s->authenticated && (option_verbose > 1) && displayconnects) {
ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
}
free_session(s);
} else
prev = s;
s = next;
}
/* Purge master event queue of old, unused events, but make sure we
always keep at least one in the queue */
eqe = master_eventq;
while (master_eventq->next && !master_eventq->usecount) {
eqe = master_eventq;
master_eventq = master_eventq->next;
free(eqe);
}
Mark Spencer
committed
ast_mutex_unlock(&sessionlock);
Mark Spencer
committed
pfds[0].fd = asock;
pfds[0].events = POLLIN;
/* Wait for something to happen, but timeout every few seconds so
we can ditch any old manager sessions */
if (poll(pfds, 1, 5000) < 1)
continue;
as = accept(asock, (struct sockaddr *)&sin, &sinlen);
if (as < 0) {
ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
continue;
}
if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
}
}
s = malloc(sizeof(struct mansession));
if (!s) {
ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
continue;
}
memset(s, 0, sizeof(struct mansession));
memcpy(&s->sin, &sin, sizeof(sin));
s->writetimeout = 100;
Mark Spencer
committed
s->waiting_thread = AST_PTHREADT_NULL;
/* For safety, make sure socket is non-blocking */
flags = fcntl(as, F_GETFL);
fcntl(as, F_SETFL, flags | O_NONBLOCK);
}
ast_mutex_init(&s->__lock);
Anthony Minessale II
committed
s->send_events = -1;
ast_mutex_lock(&sessionlock);
/* Find the last place in the master event queue and hook ourselves
in there */
s->eventq = master_eventq;
while(s->eventq->next)
s->eventq = s->eventq->next;
ast_mutex_lock(&s->eventq->lock);
s->eventq->usecount++;
ast_mutex_unlock(&s->eventq->lock);
ast_mutex_unlock(&sessionlock);
if (ast_pthread_create(&s->t, &attr, session_do, s))
static int append_event(const char *str, int category)
struct eventqent *tmp, *prev = NULL;
tmp = malloc(sizeof(struct eventqent) + strlen(str));
if (tmp) {
tmp->next = NULL;
strcpy(tmp->eventdata, str);
if (master_eventq) {
prev = master_eventq;
prev = prev->next;
prev->next = tmp;
} else {
return 0;
}
return -1;
}
/*! \brief manager_event: Send AMI event to client */
int manager_event(int category, const char *event, const char *fmt, ...)
char auth[80];
char tmp[4096] = "";
char *tmp_next = tmp;
size_t tmp_left = sizeof(tmp) - 2;
/* Abort if there aren't any manager sessions */
if (!num_sessions)
return 0;
ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
event, authority_to_str(category, auth, sizeof(auth)));
if (timestampevents) {
now = ast_tvnow();
ast_build_string(&tmp_next, &tmp_left, "Timestamp: %ld.%06lu\r\n",
now.tv_sec, (unsigned long) now.tv_usec);
}
va_start(ap, fmt);
ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
va_end(ap);
*tmp_next++ = '\r';
*tmp_next++ = '\n';