2002-09-12 18:32:42 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk -- An open source telephony toolkit.
2002-09-12 18:32:42 +00:00
*
2006-01-03 22:16:23 +00:00
* Copyright (C) 1999 - 2006, Digium, Inc.
2002-09-12 18:32:42 +00:00
*
2004-08-31 00:08:52 +00:00
* Mark Spencer <markster@digium.com>
2002-09-12 18:32:42 +00:00
*
2005-09-14 20:46:50 +00:00
* 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.
*
2002-09-12 18:32:42 +00:00
* This program is free software, distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \brief The Asterisk Management Interface - AMI
2005-09-14 20:46:50 +00:00
*
2005-12-30 21:18:06 +00:00
* \author Mark Spencer <markster@digium.com>
*
2005-09-14 20:46:50 +00:00
* Channel Management and more
*
2005-10-26 23:11:36 +00:00
* \ref amiconf
2002-09-12 18:32:42 +00:00
*/
2005-11-14 19:00:38 +00:00
/*! \addtogroup Group_AMI AMI functions
*/
/*! @{
Doxygen group */
2002-09-12 18:32:42 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2006-04-01 08:49:54 +00:00
#include <ctype.h>
2002-09-12 18:32:42 +00:00
#include <sys/time.h>
2003-03-20 17:21:54 +00:00
#include <sys/types.h>
#include <netdb.h>
2002-09-12 18:32:42 +00:00
#include <sys/socket.h>
#include <netinet/in.h>
2003-03-20 17:21:54 +00:00
#include <netinet/tcp.h>
2002-09-12 18:32:42 +00:00
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
2005-04-21 06:02:45 +00:00
2005-06-06 20:27:51 +00:00
#include "asterisk.h"
2005-06-06 22:12:19 +00:00
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
2005-06-06 20:27:51 +00:00
2005-04-21 06:02:45 +00:00
#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"
2006-04-01 08:49:54 +00:00
#include "asterisk/http.h"
2002-09-12 18:32:42 +00:00
2005-07-15 23:24:51 +00:00
struct fast_originate_helper {
2006-02-11 18:31:59 +00:00
char tech [ AST_MAX_MANHEADER_LEN ];
char data [ AST_MAX_MANHEADER_LEN ];
2004-05-01 23:52:27 +00:00
int timeout ;
2006-02-11 18:31:59 +00:00
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 ];
2004-05-01 23:52:27 +00:00
int priority ;
2005-07-15 23:24:51 +00:00
struct ast_variable * vars ;
2004-05-01 23:52:27 +00:00
};
2006-04-02 19:59:55 +00:00
struct eventqent {
int usecount ;
int category ;
ast_mutex_t lock ;
struct eventqent * next ;
char eventdata [ 1 ];
};
2002-09-12 18:32:42 +00:00
static int enabled = 0 ;
static int portno = DEFAULT_MANAGER_PORT ;
static int asock = - 1 ;
2005-01-05 06:24:59 +00:00
static int displayconnects = 1 ;
2006-01-03 22:07:12 +00:00
static int timestampevents = 0 ;
2006-04-01 08:49:54 +00:00
static int httptimeout = 60 ;
2005-01-05 06:24:59 +00:00
2002-09-12 18:32:42 +00:00
static pthread_t t ;
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( sessionlock );
2004-03-01 21:12:32 +00:00
static int block_sockets = 0 ;
2006-04-02 19:59:55 +00:00
static int num_sessions = 0 ;
struct eventqent * master_eventq = NULL ;
2002-09-12 18:32:42 +00:00
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" },
2003-04-14 18:47:36 +00:00
{ EVENT_FLAG_USER , "user" },
2002-09-12 18:32:42 +00:00
{ - 1 , "all" },
2005-04-04 16:18:11 +00:00
{ 0 , "none" },
2002-09-12 18:32:42 +00:00
};
2006-03-25 23:50:09 +00:00
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 ;
2006-04-01 08:49:54 +00:00
/*! 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 ;
2006-03-25 23:50:09 +00:00
/*! 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 ;
/* 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 ;
2003-01-28 22:33:41 +00:00
static struct manager_action * first_action = NULL ;
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( actionlock );
2002-09-12 18:32:42 +00:00
2005-10-24 20:12:06 +00:00
/*! If you are calling ast_carefulwrite, it is assumed that you are calling
2005-05-17 18:30:44 +00:00
it on a file descriptor that _DOES_ have NONBLOCK set. This way,
there is only one system call made to do a write, unless we actually
have a need to wait. This way, we get better performance. */
2004-03-01 21:12:32 +00:00
int ast_carefulwrite ( int fd , char * s , int len , int timeoutms )
{
/* Try to write string, but wait no more than ms milliseconds
before timing out */
2006-04-04 19:03:12 +00:00
int res = 0 ;
2004-04-25 20:42:45 +00:00
struct pollfd fds [ 1 ];
2006-04-04 19:03:12 +00:00
while ( len ) {
2004-03-01 21:12:32 +00:00
res = write ( fd , s , len );
if (( res < 0 ) && ( errno != EAGAIN )) {
return - 1 ;
}
2006-04-04 19:03:12 +00:00
if ( res < 0 )
res = 0 ;
2004-03-01 21:12:32 +00:00
len -= res ;
s += res ;
2005-10-14 20:37:40 +00:00
res = 0 ;
if ( len ) {
fds [ 0 ]. fd = fd ;
fds [ 0 ]. events = POLLOUT ;
/* Wait until writable again */
res = poll ( fds , 1 , timeoutms );
if ( res < 1 )
return - 1 ;
}
2004-03-01 21:12:32 +00:00
}
return res ;
}
2005-10-24 20:12:06 +00:00
/*! authority_to_str: Convert authority code to string with serveral options */
2004-07-30 20:27:42 +00:00
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 );
}
}
2005-02-26 07:15:18 +00:00
if ( ast_strlen_zero ( res )) {
2005-07-10 22:56:21 +00:00
ast_copy_string ( res , "<none>" , reslen );
2005-02-26 07:15:18 +00:00
}
2004-07-30 20:27:42 +00:00
return res ;
}
2006-01-18 22:17:31 +00:00
static char * complete_show_mancmd ( const char * line , const char * word , int pos , int state )
2004-06-02 20:08:08 +00:00
{
2006-03-28 23:52:21 +00:00
struct manager_action * cur ;
2004-06-02 20:08:08 +00:00
int which = 0 ;
2006-03-28 23:52:21 +00:00
char * ret = NULL ;
2004-06-02 20:08:08 +00:00
ast_mutex_lock ( & actionlock );
2006-03-28 23:52:21 +00:00
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 */
2004-06-02 20:08:08 +00:00
}
}
ast_mutex_unlock ( & actionlock );
2006-03-28 23:52:21 +00:00
return ret ;
2004-06-02 20:08:08 +00:00
}
2006-04-03 18:38:28 +00:00
static void xml_copy_escape ( char ** dst , size_t * maxlen , const char * src , int lower )
2006-04-01 08:49:54 +00:00
{
while ( * src && ( * maxlen > 6 )) {
2006-04-04 19:03:12 +00:00
switch ( * src ) {
2006-04-01 08:49:54 +00:00
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 ++ ;
}
}
2006-04-03 18:38:28 +00:00
2006-04-01 08:49:54 +00:00
static char * xml_translate ( char * in , struct ast_variable * vars )
{
struct ast_variable * v ;
2006-04-04 19:03:12 +00:00
char * dest = NULL ;
2006-04-01 08:49:54 +00:00
char * out , * tmp , * var , * val ;
2006-04-04 19:03:12 +00:00
char * objtype = NULL ;
2006-04-01 08:49:54 +00:00
int colons = 0 ;
int breaks = 0 ;
2006-04-03 18:38:28 +00:00
size_t len ;
2006-04-01 08:49:54 +00:00
int count = 1 ;
int escaped = 0 ;
int inobj = 0 ;
int x ;
v = vars ;
2006-04-03 18:38:28 +00:00
2006-04-04 19:03:12 +00:00
while ( v ) {
2006-04-01 08:49:54 +00:00
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" ;
2006-04-03 18:38:28 +00:00
for ( x = 0 ; in [ x ]; x ++ ) {
2006-04-01 08:49:54 +00:00
if ( in [ x ] == ':' )
colons ++ ;
else if ( in [ x ] == '\n' )
breaks ++ ;
else if ( strchr ( "& \" <>" , in [ x ]))
escaped ++ ;
}
2006-04-03 18:38:28 +00:00
len = ( size_t ) ( strlen ( in ) + colons * 5 + breaks * ( 40 + strlen ( dest ) + strlen ( objtype )) + escaped * 10 ); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&" */
2006-04-01 08:49:54 +00:00
out = malloc ( len );
if ( ! out )
return 0 ;
tmp = out ;
2006-04-04 19:03:12 +00:00
while ( * in ) {
2006-04-01 08:49:54 +00:00
var = in ;
2006-04-03 18:38:28 +00:00
while ( * in && ( * in >= 32 ))
in ++ ;
2006-04-01 08:49:54 +00:00
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 ;
2006-04-03 18:38:28 +00:00
size_t len ;
2006-04-04 19:03:12 +00:00
int count = 1 ;
2006-04-01 08:49:54 +00:00
char * tmp , * var , * val , * out ;
2006-04-03 18:38:28 +00:00
for ( x = 0 ; in [ x ]; x ++ ) {
2006-04-01 08:49:54 +00:00
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 ;
2006-04-04 19:03:12 +00:00
while ( * in ) {
2006-04-01 08:49:54 +00:00
var = in ;
2006-04-03 18:38:28 +00:00
while ( * in && ( * in >= 32 ))
in ++ ;
2006-04-01 08:49:54 +00:00
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 ;
}
2006-03-25 23:50:09 +00:00
void astman_append ( struct mansession * s , const char * fmt , ...)
{
char * stuff ;
int res ;
va_list ap ;
2006-04-01 08:49:54 +00:00
char * tmp ;
2006-03-25 23:50:09 +00:00
va_start ( ap , fmt );
res = vasprintf ( & stuff , fmt , ap );
va_end ( ap );
if ( res == - 1 ) {
ast_log ( LOG_ERROR , "Memory allocation failure \n " );
2006-04-03 18:38:28 +00:00
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 );
2006-04-01 08:49:54 +00:00
}
2006-03-25 23:50:09 +00:00
}
2006-04-03 18:38:28 +00:00
free ( stuff );
2006-03-25 23:50:09 +00:00
}
2004-06-02 20:08:08 +00:00
static int handle_showmancmd ( int fd , int argc , char * argv [])
{
struct manager_action * cur = first_action ;
2004-07-30 20:27:42 +00:00
char authority [ 80 ];
2004-06-02 20:08:08 +00:00
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 ])) {
2004-07-30 20:27:42 +00:00
ast_cli ( fd , "Action: %s \n Synopsis: %s \n Privilege: %s \n %s \n " , cur -> action , cur -> synopsis , authority_to_str ( cur -> authority , authority , sizeof ( authority ) - 1 ), cur -> description ? cur -> description : "" );
2004-06-02 20:08:08 +00:00
}
}
cur = cur -> next ;
}
ast_mutex_unlock ( & actionlock );
return RESULT_SUCCESS ;
}
2005-10-26 13:03:17 +00:00
/*! \brief handle_showmancmds: CLI command */
2005-04-04 16:18:11 +00:00
/* Should change to "manager show commands" */
2003-01-28 22:33:41 +00:00
static int handle_showmancmds ( int fd , int argc , char * argv [])
{
struct manager_action * cur = first_action ;
2004-07-30 20:27:42 +00:00
char authority [ 80 ];
2005-04-04 16:18:11 +00:00
char * format = " %-15.15s %-15.15s %-55.55s \n " ;
2002-09-12 18:32:42 +00:00
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & actionlock );
2004-07-30 20:27:42 +00:00
ast_cli ( fd , format , "Action" , "Privilege" , "Synopsis" );
2005-04-04 16:18:11 +00:00
ast_cli ( fd , format , "------" , "---------" , "--------" );
2004-06-02 20:08:08 +00:00
while ( cur ) { /* Walk the list of actions */
2004-07-30 20:27:42 +00:00
ast_cli ( fd , format , cur -> action , authority_to_str ( cur -> authority , authority , sizeof ( authority ) - 1 ), cur -> synopsis );
2003-01-28 22:33:41 +00:00
cur = cur -> next ;
}
2002-09-12 18:32:42 +00:00
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & actionlock );
2003-01-28 22:33:41 +00:00
return RESULT_SUCCESS ;
}
2005-10-26 13:03:17 +00:00
/*! \brief handle_showmanconn: CLI command show manager connected */
2005-04-04 16:18:11 +00:00
/* Should change to "manager show connected" */
2003-02-21 06:00:08 +00:00
static int handle_showmanconn ( int fd , int argc , char * argv [])
{
struct mansession * s ;
2004-06-30 16:56:51 +00:00
char iabuf [ INET_ADDRSTRLEN ];
2004-01-22 21:44:47 +00:00
char * format = " %-15.15s %-15.15s \n " ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & sessionlock );
2003-02-21 06:00:08 +00:00
s = sessions ;
2004-01-22 21:44:47 +00:00
ast_cli ( fd , format , "Username" , "IP Address" );
2004-06-02 20:08:08 +00:00
while ( s ) {
2004-06-29 12:56:46 +00:00
ast_cli ( fd , format , s -> username , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ));
2003-02-21 06:00:08 +00:00
s = s -> next ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & sessionlock );
2003-02-21 06:00:08 +00:00
return RESULT_SUCCESS ;
}
2006-04-02 19:59:55 +00:00
/*! \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 ;
}
2004-06-02 20:08:08 +00:00
static char showmancmd_help [] =
"Usage: show manager command <actionname> \n "
2005-02-26 07:15:18 +00:00
" Shows the detailed description for a specific Asterisk manager interface command. \n " ;
2004-06-02 20:08:08 +00:00
2003-01-28 22:33:41 +00:00
static char showmancmds_help [] =
"Usage: show manager commands \n "
2005-02-26 07:15:18 +00:00
" Prints a listing of all the available Asterisk manager interface commands. \n " ;
2003-01-28 22:33:41 +00:00
2003-02-21 06:00:08 +00:00
static char showmanconn_help [] =
"Usage: show manager connected \n "
2005-02-26 07:15:18 +00:00
" Prints a listing of the users that are currently connected to the \n "
"Asterisk manager interface. \n " ;
2003-02-21 06:00:08 +00:00
2006-04-02 19:59:55 +00:00
static char showmaneventq_help [] =
"Usage: show manager eventq \n "
" Prints a listing of all events pending in the Asterisk manger \n "
"event queue. \n " ;
2004-06-02 20:08:08 +00:00
static struct ast_cli_entry show_mancmd_cli =
{ { "show" , "manager" , "command" , NULL },
2005-02-26 07:15:18 +00:00
handle_showmancmd , "Show a manager interface command" , showmancmd_help , complete_show_mancmd };
2004-06-02 20:08:08 +00:00
2003-01-28 22:33:41 +00:00
static struct ast_cli_entry show_mancmds_cli =
{ { "show" , "manager" , "commands" , NULL },
2005-02-26 07:15:18 +00:00
handle_showmancmds , "List manager interface commands" , showmancmds_help };
2002-09-12 18:32:42 +00:00
2003-02-21 06:00:08 +00:00
static struct ast_cli_entry show_manconn_cli =
{ { "show" , "manager" , "connected" , NULL },
2005-02-26 07:15:18 +00:00
handle_showmanconn , "Show connected manager interface users" , showmanconn_help };
2003-02-21 06:00:08 +00:00
2006-04-02 19:59:55 +00:00
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 -- ;
2006-04-02 20:11:55 +00:00
val = ! e -> usecount && e -> next ;
2006-04-02 19:59:55 +00:00
ast_mutex_unlock ( & e -> lock );
/* Wake up sleeping beauty */
if ( val )
pthread_kill ( t , SIGURG );
}
2005-09-28 23:10:14 +00:00
static void free_session ( struct mansession * s )
{
struct eventqent * eqe ;
if ( s -> fd > - 1 )
close ( s -> fd );
2006-04-01 08:49:54 +00:00
if ( s -> outputstr )
free ( s -> outputstr );
2005-09-28 23:10:14 +00:00
ast_mutex_destroy ( & s -> __lock );
2006-04-04 19:03:12 +00:00
while ( s -> eventq ) {
2005-09-28 23:10:14 +00:00
eqe = s -> eventq ;
s -> eventq = s -> eventq -> next ;
2006-04-02 19:59:55 +00:00
unuse_eventqent ( eqe );
2005-09-28 23:10:14 +00:00
}
free ( s );
}
2002-09-12 18:32:42 +00:00
static void destroy_session ( struct mansession * s )
{
struct mansession * cur , * prev = NULL ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & sessionlock );
2002-09-12 18:32:42 +00:00
cur = sessions ;
2006-04-04 19:03:12 +00:00
while ( cur ) {
2002-09-12 18:32:42 +00:00
if ( cur == s )
break ;
prev = cur ;
cur = cur -> next ;
}
if ( cur ) {
if ( prev )
prev -> next = cur -> next ;
else
sessions = cur -> next ;
2005-09-28 23:10:14 +00:00
free_session ( s );
2006-04-02 19:59:55 +00:00
num_sessions -- ;
2002-09-12 18:32:42 +00:00
} else
2005-04-13 23:33:47 +00:00
ast_log ( LOG_WARNING , "Trying to delete nonexistent session %p? \n " , s );
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & sessionlock );
2002-09-12 18:32:42 +00:00
}
2003-03-31 17:40:18 +00:00
char * astman_get_header ( struct message * m , char * var )
2002-09-12 18:32:42 +00:00
{
char cmp [ 80 ];
int x ;
snprintf ( cmp , sizeof ( cmp ), "%s: " , var );
2006-04-04 19:03:12 +00:00
for ( x = 0 ; x < m -> hdrcount ; x ++ )
2002-09-12 18:32:42 +00:00
if ( ! strncasecmp ( cmp , m -> headers [ x ], strlen ( cmp )))
return m -> headers [ x ] + strlen ( cmp );
return "" ;
}
2005-07-15 23:24:51 +00:00
struct ast_variable * astman_get_variables ( struct message * m )
{
2005-11-08 02:11:42 +00:00
int varlen , x , y ;
2005-07-15 23:24:51 +00:00
struct ast_variable * head = NULL , * cur ;
char * var , * val ;
2006-01-23 18:07:12 +00:00
char * parse ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( vars )[ 32 ];
);
2005-07-15 23:24:51 +00:00
varlen = strlen ( "Variable: " );
for ( x = 0 ; x < m -> hdrcount ; x ++ ) {
2005-11-08 02:11:42 +00:00
if ( strncasecmp ( "Variable: " , m -> headers [ x ], varlen ))
continue ;
2006-01-23 18:07:12 +00:00
if ( ! ( parse = ast_strdupa ( m -> headers [ x ] + varlen )))
2005-11-08 02:11:42 +00:00
return head ;
2006-01-23 18:07:12 +00:00
AST_STANDARD_APP_ARGS ( args , parse );
if ( args . argc ) {
for ( y = 0 ; y < args . argc ; y ++ ) {
if ( ! args . vars [ y ])
2005-11-15 01:33:31 +00:00
continue ;
2006-01-23 18:07:12 +00:00
var = val = ast_strdupa ( args . vars [ y ]);
2005-11-08 02:11:42 +00:00
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 ;
}
2005-07-15 23:24:51 +00:00
}
}
return head ;
}
2005-10-24 20:12:06 +00:00
/*! NOTE:
2005-09-30 23:52:04 +00:00
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.
*/
2003-09-08 16:44:36 +00:00
void astman_send_error ( struct mansession * s , struct message * m , char * error )
2002-09-12 18:32:42 +00:00
{
2003-09-08 16:44:36 +00:00
char * id = astman_get_header ( m , "ActionID" );
2005-09-30 23:52:04 +00:00
2006-03-25 23:50:09 +00:00
astman_append ( s , "Response: Error \r\n " );
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( id ))
2006-04-04 19:03:12 +00:00
astman_append ( s , "ActionID: %s \r\n " , id );
2006-03-25 23:50:09 +00:00
astman_append ( s , "Message: %s \r\n\r\n " , error );
2002-09-12 18:32:42 +00:00
}
2003-09-08 16:44:36 +00:00
void astman_send_response ( struct mansession * s , struct message * m , char * resp , char * msg )
2002-09-12 18:32:42 +00:00
{
2003-09-08 16:44:36 +00:00
char * id = astman_get_header ( m , "ActionID" );
2005-09-30 23:52:04 +00:00
2006-03-25 23:50:09 +00:00
astman_append ( s , "Response: %s \r\n " , resp );
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( id ))
2006-04-04 19:03:12 +00:00
astman_append ( s , "ActionID: %s \r\n " , id );
2002-09-12 18:32:42 +00:00
if ( msg )
2006-03-25 23:50:09 +00:00
astman_append ( s , "Message: %s \r\n\r\n " , msg );
2002-09-12 18:32:42 +00:00
else
2006-03-25 23:50:09 +00:00
astman_append ( s , " \r\n " );
2002-09-12 18:32:42 +00:00
}
2003-09-08 16:44:36 +00:00
void astman_send_ack ( struct mansession * s , struct message * m , char * msg )
2002-09-12 18:32:42 +00:00
{
2003-09-08 16:44:36 +00:00
astman_send_response ( s , m , "Success" , msg );
2002-09-12 18:32:42 +00:00
}
2005-10-24 20:12:06 +00:00
/*! Tells you if smallstr exists inside bigstr
2004-07-08 15:39:35 +00:00
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 */
2004-07-08 17:08:38 +00:00
static int ast_instring ( char * bigstr , char * smallstr , char delim )
{
char * val = bigstr , * next ;
2004-07-08 15:39:35 +00:00
2004-07-08 17:08:38 +00:00
do {
if (( next = strchr ( val , delim ))) {
if ( ! strncmp ( val , smallstr , ( next - val )))
return 1 ;
else
continue ;
} else
return ! strcmp ( smallstr , val );
2004-07-08 15:39:35 +00:00
2004-07-08 17:08:38 +00:00
} while ( * ( val = ( next + 1 )));
2004-07-08 15:39:35 +00:00
2004-07-08 17:08:38 +00:00
return 0 ;
2004-07-08 15:39:35 +00:00
}
2002-09-12 18:32:42 +00:00
static int get_perm ( char * instr )
{
2004-07-08 15:39:35 +00:00
int x = 0 , ret = 0 ;
2003-01-28 22:33:41 +00:00
if ( ! instr )
return 0 ;
2004-07-08 15:39:35 +00:00
for ( x = 0 ; x < sizeof ( perms ) / sizeof ( perms [ 0 ]); x ++ )
if ( ast_instring ( instr , perms [ x ]. label , ',' ))
ret |= perms [ x ]. num ;
return ret ;
}
2004-07-08 17:08:38 +00:00
static int ast_is_number ( char * string )
{
2004-07-08 15:39:35 +00:00
int ret = 1 , x = 0 ;
2004-07-08 17:08:38 +00:00
if ( ! string )
2004-07-08 15:39:35 +00:00
return 0 ;
2004-07-08 17:08:38 +00:00
for ( x = 0 ; x < strlen ( string ); x ++ ) {
if ( ! ( string [ x ] >= 48 && string [ x ] <= 57 )) {
2004-07-08 15:39:35 +00:00
ret = 0 ;
break ;
2002-09-12 18:32:42 +00:00
}
}
2004-07-08 15:39:35 +00:00
return ret ? atoi ( string ) : 0 ;
}
2004-07-08 17:08:38 +00:00
static int ast_strings_to_mask ( char * string )
{
2005-08-26 23:04:20 +00:00
int x , ret = - 1 ;
2004-07-08 15:39:35 +00:00
x = ast_is_number ( string );
2005-08-26 23:04:20 +00:00
if ( x ) {
2004-07-08 15:39:35 +00:00
ret = x ;
2005-10-26 18:54:24 +00:00
} else if ( ast_strlen_zero ( string )) {
2004-07-08 15:39:35 +00:00
ret = - 1 ;
2005-08-26 23:04:20 +00:00
} else if ( ast_false ( string )) {
2004-07-08 15:39:35 +00:00
ret = 0 ;
2005-08-26 23:04:20 +00:00
} else if ( ast_true ( string )) {
ret = 0 ;
for ( x = 0 ; x < sizeof ( perms ) / sizeof ( perms [ 0 ]); x ++ )
ret |= perms [ x ]. num ;
} else {
2004-07-08 15:39:35 +00:00
ret = 0 ;
2004-07-08 17:08:38 +00:00
for ( x = 0 ; x < sizeof ( perms ) / sizeof ( perms [ 0 ]); x ++ ) {
2004-07-08 15:39:35 +00:00
if ( ast_instring ( string , perms [ x ]. label , ',' ))
ret |= perms [ x ]. num ;
2004-07-08 17:08:38 +00:00
}
2004-07-08 15:39:35 +00:00
}
2002-09-12 18:32:42 +00:00
return ret ;
}
2005-10-24 20:12:06 +00:00
/*!
2004-07-08 15:39:35 +00:00
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
*/
2004-03-02 08:45:51 +00:00
static int set_eventmask ( struct mansession * s , char * eventmask )
{
2004-07-08 15:39:35 +00:00
int maskint = ast_strings_to_mask ( eventmask );
2005-09-28 23:10:14 +00:00
ast_mutex_lock ( & s -> __lock );
2005-07-25 23:01:39 +00:00
if ( maskint >= 0 )
s -> send_events = maskint ;
2005-09-28 23:10:14 +00:00
ast_mutex_unlock ( & s -> __lock );
2004-07-08 15:39:35 +00:00
2005-07-25 23:01:39 +00:00
return maskint ;
2004-03-02 08:45:51 +00:00
}
2002-09-12 18:32:42 +00:00
static int authenticate ( struct mansession * s , struct message * m )
{
struct ast_config * cfg ;
2004-06-30 16:56:51 +00:00
char iabuf [ INET_ADDRSTRLEN ];
2002-09-12 18:32:42 +00:00
char * cat ;
2003-03-31 17:40:18 +00:00
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" );
2004-03-01 21:12:32 +00:00
char * events = astman_get_header ( m , "Events" );
2005-01-25 06:10:20 +00:00
cfg = ast_config_load ( "manager.conf" );
2002-09-12 18:32:42 +00:00
if ( ! cfg )
return - 1 ;
cat = ast_category_browse ( cfg , NULL );
2006-04-04 19:03:12 +00:00
while ( cat ) {
2002-09-12 18:32:42 +00:00
if ( strcasecmp ( cat , "general" )) {
/* This is a user */
if ( ! strcasecmp ( cat , user )) {
2003-05-05 06:14:25 +00:00
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 , "permit" ) ||
! strcasecmp ( v -> name , "deny" )) {
2005-01-05 06:24:59 +00:00
ha = ast_append_ha ( v -> name , v -> value , ha );
2005-10-04 22:25:15 +00:00
} 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 ;
}
2005-01-05 06:24:59 +00:00
2003-05-05 06:14:25 +00:00
v = v -> next ;
}
if ( ha && ! ast_apply_ha ( ha , & ( s -> sin ))) {
2004-06-29 12:56:46 +00:00
ast_log ( LOG_NOTICE , "%s failed to pass IP ACL as '%s' \n " , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ), user );
2003-05-05 06:14:25 +00:00
ast_free_ha ( ha );
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg );
2003-05-05 06:14:25 +00:00
return - 1 ;
} else if ( ha )
ast_free_ha ( ha );
2003-03-10 06:00:17 +00:00
if ( ! strcasecmp ( authtype , "MD5" )) {
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( key ) && s -> challenge ) {
2003-03-10 06:00:17 +00:00
int x ;
2006-04-04 19:03:12 +00:00
int len = 0 ;
2003-03-10 06:00:17 +00:00
char md5key [ 256 ] = "" ;
struct MD5Context md5 ;
unsigned char digest [ 16 ];
MD5Init ( & md5 );
2005-08-05 16:29:30 +00:00
MD5Update ( & md5 , ( unsigned char * ) s -> challenge , strlen ( s -> challenge ));
MD5Update ( & md5 , ( unsigned char * ) password , strlen ( password ));
2003-03-10 06:00:17 +00:00
MD5Final ( digest , & md5 );
2006-04-04 19:03:12 +00:00
for ( x = 0 ; x < 16 ; x ++ )
2003-03-10 06:00:17 +00:00
len += sprintf ( md5key + len , "%2.2x" , digest [ x ]);
if ( ! strcmp ( md5key , key ))
break ;
else {
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg );
2003-03-10 06:00:17 +00:00
return - 1 ;
}
}
2006-04-01 08:49:54 +00:00
} else if ( password && ! strcmp ( password , pass )) {
2002-09-12 18:32:42 +00:00
break ;
} else {
2004-06-29 12:56:46 +00:00
ast_log ( LOG_NOTICE , "%s failed to authenticate as '%s' \n " , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ), user );
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg );
2002-09-12 18:32:42 +00:00
return - 1 ;
}
}
}
cat = ast_category_browse ( cfg , cat );
}
if ( cat ) {
2005-07-10 22:56:21 +00:00
ast_copy_string ( s -> username , cat , sizeof ( s -> username ));
2002-09-12 18:32:42 +00:00
s -> readperm = get_perm ( ast_variable_retrieve ( cfg , cat , "read" ));
s -> writeperm = get_perm ( ast_variable_retrieve ( cfg , cat , "write" ));
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg );
2004-03-02 08:45:51 +00:00
if ( events )
set_eventmask ( s , events );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2005-04-13 23:33:47 +00:00
ast_log ( LOG_NOTICE , "%s tried to authenticate with nonexistent user '%s' \n " , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ), user );
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg );
2002-09-12 18:32:42 +00:00
return - 1 ;
}
2005-11-14 19:00:38 +00:00
/*! \brief PING: Manager PING */
2004-06-02 20:08:08 +00:00
static char mandescr_ping [] =
2006-04-01 08:49:54 +00:00
"Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the \n "
2004-06-02 20:08:08 +00:00
" manager connection open. \n "
"Variables: NONE \n " ;
2002-09-12 18:32:42 +00:00
static int action_ping ( struct mansession * s , struct message * m )
{
2003-09-08 16:44:36 +00:00
astman_send_response ( s , m , "Pong" , NULL );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2006-04-01 08:49:54 +00:00
/*! \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" );
2006-04-04 19:03:12 +00:00
char idText [ 256 ] = "" ;
2006-04-01 08:49:54 +00:00
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 ();
2006-04-01 20:39:33 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , "Starting waiting for an event! \n " );
2006-04-04 19:03:12 +00:00
for ( x = 0 ; (( x < timeout ) || ( timeout < 0 )); x ++ ) {
2006-04-01 08:49:54 +00:00
ast_mutex_lock ( & s -> __lock );
2006-04-02 19:59:55 +00:00
if ( s -> eventq && s -> eventq -> next )
2006-04-01 08:49:54 +00:00
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 );
}
}
2006-04-01 20:39:33 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , "Finished waiting for an event! \n " );
2006-04-01 08:49:54 +00:00
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 */
2006-04-02 19:59:55 +00:00
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 ;
2006-04-01 08:49:54 +00:00
}
astman_append ( s ,
"Event: WaitEventComplete \r\n "
"%s"
2006-04-04 19:03:12 +00:00
" \r\n " , idText );
2006-04-01 08:49:54 +00:00
s -> waiting_thread = AST_PTHREADT_NULL ;
} else {
ast_log ( LOG_DEBUG , "Abandoning event request! \n " );
}
ast_mutex_unlock ( & s -> __lock );
return 0 ;
}
2004-06-02 22:30:42 +00:00
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 ] = "" ;
2005-04-04 16:18:11 +00:00
char temp [ BUFSIZ ];
2004-06-02 22:30:42 +00:00
char * id = astman_get_header ( m , "ActionID" );
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( id ))
2006-03-28 23:52:21 +00:00
snprintf ( idText , sizeof ( idText ), "ActionID: %s \r\n " , id );
2006-03-25 23:50:09 +00:00
astman_append ( s , "Response: Success \r\n %s" , idText );
2004-06-02 22:30:42 +00:00
ast_mutex_lock ( & actionlock );
while ( cur ) { /* Walk the list of actions */
if (( s -> writeperm & cur -> authority ) == cur -> authority )
2006-04-04 19:03:12 +00:00
astman_append ( s , "%s: %s (Priv: %s) \r\n " , cur -> action , cur -> synopsis , authority_to_str ( cur -> authority , temp , sizeof ( temp )));
2004-06-02 22:30:42 +00:00
cur = cur -> next ;
}
ast_mutex_unlock ( & actionlock );
2006-03-25 23:50:09 +00:00
astman_append ( s , " \r\n " );
2004-06-02 22:30:42 +00:00
return 0 ;
}
2004-06-29 05:04:48 +00:00
static char mandescr_events [] =
"Description: Enable/Disable sending of events to this manager \n "
" client. \n "
"Variables: \n "
2004-07-08 15:39:35 +00:00
" EventMask: 'on' if all events should be sent, \n "
2004-07-08 17:08:38 +00:00
" 'off' if no events should be sent, \n "
" 'system,call,log' to select which flags events should have to be sent. \n " ;
2004-06-29 05:04:48 +00:00
2004-03-01 21:12:32 +00:00
static int action_events ( struct mansession * s , struct message * m )
{
char * mask = astman_get_header ( m , "EventMask" );
int res ;
2004-03-02 08:45:51 +00:00
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 );
2004-07-08 15:39:35 +00:00
2004-03-01 21:12:32 +00:00
return 0 ;
}
2004-06-29 05:04:48 +00:00
static char mandescr_logoff [] =
"Description: Logoff this manager session \n "
"Variables: NONE \n " ;
2002-09-12 18:32:42 +00:00
static int action_logoff ( struct mansession * s , struct message * m )
{
2003-09-08 16:44:36 +00:00
astman_send_response ( s , m , "Goodbye" , "Thanks for all the fish." );
2002-09-12 18:32:42 +00:00
return - 1 ;
}
2004-06-29 05:04:48 +00:00
static char mandescr_hangup [] =
"Description: Hangup a channel \n "
"Variables: \n "
" Channel: The channel name to be hungup \n " ;
2002-09-12 18:32:42 +00:00
static int action_hangup ( struct mansession * s , struct message * m )
{
struct ast_channel * c = NULL ;
2003-03-31 17:40:18 +00:00
char * name = astman_get_header ( m , "Channel" );
2004-05-26 19:30:12 +00:00
if ( ast_strlen_zero ( name )) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "No channel specified" );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2005-06-06 02:29:18 +00:00
c = ast_get_channel_by_name_locked ( name );
2002-09-12 18:32:42 +00:00
if ( ! c ) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "No such channel" );
2002-09-12 18:32:42 +00:00
return 0 ;
}
ast_softhangup ( c , AST_SOFTHANGUP_EXPLICIT );
2004-05-20 16:30:10 +00:00
ast_mutex_unlock ( & c -> lock );
2003-09-08 16:44:36 +00:00
astman_send_ack ( s , m , "Channel Hungup" );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2005-02-26 07:15:18 +00:00
static char mandescr_setvar [] =
2005-11-08 03:35:40 +00:00
"Description: Set a global or local channel variable. \n "
2005-02-26 07:15:18 +00:00
"Variables: (Names marked with * are required) \n "
2005-11-08 03:35:40 +00:00
" Channel: Channel to set variable for \n "
2005-02-26 07:15:18 +00:00
" *Variable: Variable name \n "
" *Value: Value \n " ;
2004-06-03 15:42:36 +00:00
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" );
2005-11-08 03:35:40 +00:00
if ( ast_strlen_zero ( varname )) {
2004-06-03 15:42:36 +00:00
astman_send_error ( s , m , "No variable specified" );
return 0 ;
}
2005-11-15 18:35:30 +00:00
if ( ast_strlen_zero ( varval )) {
astman_send_error ( s , m , "No value specified" );
return 0 ;
}
2004-06-03 15:42:36 +00:00
2005-11-08 03:35:40 +00:00
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 ;
}
2004-06-03 15:42:36 +00:00
}
2005-11-15 18:35:30 +00:00
pbx_builtin_setvar_helper ( c , varname , varval );
2004-06-03 15:42:36 +00:00
2005-11-15 18:35:30 +00:00
if ( c )
ast_mutex_unlock ( & c -> lock );
astman_send_ack ( s , m , "Variable Set" );
2005-11-08 03:35:40 +00:00
2004-06-03 15:42:36 +00:00
return 0 ;
}
2005-02-26 07:15:18 +00:00
static char mandescr_getvar [] =
2005-10-13 20:30:39 +00:00
"Description: Get the value of a global or local channel variable. \n "
2005-02-26 07:15:18 +00:00
"Variables: (Names marked with * are required) \n "
2005-10-13 20:30:39 +00:00
" Channel: Channel to read variable from \n "
2005-02-26 07:15:18 +00:00
" *Variable: Variable name \n "
" ActionID: Optional Action id for message matching. \n " ;
2004-06-03 15:42:36 +00:00
static int action_getvar ( struct mansession * s , struct message * m )
{
2005-12-27 19:13:13 +00:00
struct ast_channel * c = NULL ;
char * name = astman_get_header ( m , "Channel" );
char * varname = astman_get_header ( m , "Variable" );
2004-09-09 12:50:56 +00:00
char * id = astman_get_header ( m , "ActionID" );
2005-12-27 19:13:13 +00:00
char * varval ;
char workspace [ 1024 ];
2004-06-03 15:42:36 +00:00
2005-12-27 19:13:13 +00:00
if ( ast_strlen_zero ( varname )) {
2004-06-03 15:42:36 +00:00
astman_send_error ( s , m , "No variable specified" );
return 0 ;
}
2005-12-27 19:13:13 +00:00
if ( ! ast_strlen_zero ( name )) {
2005-10-13 20:30:39 +00:00
c = ast_get_channel_by_name_locked ( name );
if ( ! c ) {
astman_send_error ( s , m , "No such channel" );
return 0 ;
}
2004-06-03 15:42:36 +00:00
}
2005-12-27 19:13:13 +00:00
2006-01-15 05:50:19 +00:00
if ( varname [ strlen ( varname ) - 1 ] == ')' ) {
2006-02-12 04:28:58 +00:00
ast_func_read ( c , varname , workspace , sizeof ( workspace ));
2006-01-15 05:50:19 +00:00
} else {
pbx_retrieve_variable ( c , varname , & varval , workspace , sizeof ( workspace ), NULL );
}
2005-12-27 19:13:13 +00:00
2005-10-13 20:30:39 +00:00
if ( c )
ast_mutex_unlock ( & c -> lock );
2006-03-25 23:50:09 +00:00
astman_append ( s , "Response: Success \r\n "
2005-12-27 19:13:13 +00:00
"Variable: %s \r\n Value: %s \r\n " , varname , varval );
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( id ))
2006-03-25 23:50:09 +00:00
astman_append ( s , "ActionID: %s \r\n " , id );
astman_append ( s , " \r\n " );
2004-06-03 15:42:36 +00:00
return 0 ;
}
2005-10-26 13:03:17 +00:00
/*! \brief action_status: Manager "status" command to show channels */
2005-04-13 05:45:53 +00:00
/* Needs documentation... */
2002-09-12 18:32:42 +00:00
static int action_status ( struct mansession * s , struct message * m )
{
2003-09-08 16:44:36 +00:00
char * id = astman_get_header ( m , "ActionID" );
2004-06-26 14:31:09 +00:00
char * name = astman_get_header ( m , "Channel" );
2003-09-08 16:44:36 +00:00
char idText [ 256 ] = "" ;
2002-09-12 18:32:42 +00:00
struct ast_channel * c ;
char bridge [ 256 ];
2005-07-15 23:00:47 +00:00
struct timeval now = ast_tvnow ();
2006-04-04 19:03:12 +00:00
long elapsed_seconds = 0 ;
2005-10-26 18:54:24 +00:00
int all = ast_strlen_zero ( name ); /* set if we want all channels */
2004-08-03 17:48:18 +00:00
2003-09-08 16:44:36 +00:00
astman_send_ack ( s , m , "Channel status will follow" );
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( id ))
2006-03-28 23:52:21 +00:00
snprintf ( idText , sizeof ( idText ), "ActionID: %s \r\n " , id );
2005-06-06 02:29:18 +00:00
if ( all )
c = ast_channel_walk_locked ( NULL );
else {
c = ast_get_channel_by_name_locked ( name );
2004-06-26 14:31:09 +00:00
if ( ! c ) {
astman_send_error ( s , m , "No such channel" );
return 0 ;
}
}
2005-06-06 02:29:18 +00:00
/* if we look by name, we break after the first iteration */
2006-04-04 19:03:12 +00:00
while ( c ) {
2004-10-23 12:19:47 +00:00
if ( c -> _bridge )
snprintf ( bridge , sizeof ( bridge ), "Link: %s \r\n " , c -> _bridge -> name );
2002-09-12 18:32:42 +00:00
else
2004-07-14 07:44:19 +00:00
bridge [ 0 ] = '\0' ;
2002-09-12 18:32:42 +00:00
if ( c -> pbx ) {
2004-08-03 17:48:18 +00:00
if ( c -> cdr ) {
elapsed_seconds = now . tv_sec - c -> cdr -> start . tv_sec ;
}
2006-03-25 23:50:09 +00:00
astman_append ( s ,
2002-09-12 18:32:42 +00:00
"Event: Status \r\n "
2005-04-04 16:18:11 +00:00
"Privilege: Call \r\n "
2002-09-12 18:32:42 +00:00
"Channel: %s \r\n "
"CallerID: %s \r\n "
2004-10-02 00:58:31 +00:00
"CallerIDName: %s \r\n "
2004-06-26 14:31:09 +00:00
"Account: %s \r\n "
2002-09-12 18:32:42 +00:00
"State: %s \r\n "
"Context: %s \r\n "
"Extension: %s \r\n "
"Priority: %d \r\n "
2004-08-03 17:48:18 +00:00
"Seconds: %ld \r\n "
2002-09-12 18:32:42 +00:00
"%s"
2003-06-14 14:57:35 +00:00
"Uniqueid: %s \r\n "
2003-09-08 16:44:36 +00:00
"%s"
2003-06-14 14:57:35 +00:00
" \r\n " ,
2004-10-02 00:58:31 +00:00
c -> name ,
c -> cid . cid_num ? c -> cid . cid_num : "<unknown>" ,
c -> cid . cid_name ? c -> cid . cid_name : "<unknown>" ,
2004-06-26 14:31:09 +00:00
c -> accountcode ,
2002-09-12 18:32:42 +00:00
ast_state2str ( c -> _state ), c -> context ,
2004-08-03 17:48:18 +00:00
c -> exten , c -> priority , ( long ) elapsed_seconds , bridge , c -> uniqueid , idText );
2002-09-12 18:32:42 +00:00
} else {
2006-03-25 23:50:09 +00:00
astman_append ( s ,
2002-09-12 18:32:42 +00:00
"Event: Status \r\n "
2005-04-04 16:18:11 +00:00
"Privilege: Call \r\n "
2002-09-12 18:32:42 +00:00
"Channel: %s \r\n "
"CallerID: %s \r\n "
2004-10-02 00:58:31 +00:00
"CallerIDName: %s \r\n "
2004-06-26 14:31:09 +00:00
"Account: %s \r\n "
2002-09-12 18:32:42 +00:00
"State: %s \r\n "
"%s"
2003-08-12 23:08:49 +00:00
"Uniqueid: %s \r\n "
2003-09-08 16:44:36 +00:00
"%s"
2003-08-12 23:08:49 +00:00
" \r\n " ,
2004-10-02 00:58:31 +00:00
c -> name ,
c -> cid . cid_num ? c -> cid . cid_num : "<unknown>" ,
c -> cid . cid_name ? c -> cid . cid_name : "<unknown>" ,
2004-06-26 14:31:09 +00:00
c -> accountcode ,
2003-09-08 16:44:36 +00:00
ast_state2str ( c -> _state ), bridge , c -> uniqueid , idText );
2002-09-12 18:32:42 +00:00
}
2005-06-09 19:27:19 +00:00
ast_mutex_unlock ( & c -> lock );
2005-06-06 02:29:18 +00:00
if ( ! all )
2004-06-26 14:31:09 +00:00
break ;
2004-05-20 16:30:10 +00:00
c = ast_channel_walk_locked ( c );
2002-09-12 18:32:42 +00:00
}
2006-03-25 23:50:09 +00:00
astman_append ( s ,
2004-01-29 00:28:51 +00:00
"Event: StatusComplete \r\n "
"%s"
" \r\n " , idText );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2005-04-13 05:45:53 +00:00
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 " ;
2005-10-26 13:03:17 +00:00
/*! \brief action_redirect: The redirect manager command */
2002-09-12 18:32:42 +00:00
static int action_redirect ( struct mansession * s , struct message * m )
{
2003-03-31 17:40:18 +00:00
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" );
2004-05-22 04:11:22 +00:00
struct ast_channel * chan , * chan2 = NULL ;
2002-09-12 18:32:42 +00:00
int pi = 0 ;
int res ;
2005-04-13 05:45:53 +00:00
2005-10-26 18:54:24 +00:00
if ( ast_strlen_zero ( name )) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Channel not specified" );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2004-05-26 19:30:12 +00:00
if ( ! ast_strlen_zero ( priority ) && ( sscanf ( priority , "%d" , & pi ) != 1 )) {
2005-12-27 19:13:13 +00:00
if (( pi = ast_findlabel_extension ( NULL , context , exten , priority , NULL )) < 1 ) {
astman_send_error ( s , m , "Invalid priority \n " );
return 0 ;
}
2002-09-12 18:32:42 +00:00
}
2006-03-28 23:52:21 +00:00
/* XXX watch out, possible deadlock!!! */
2004-05-22 04:11:22 +00:00
chan = ast_get_channel_by_name_locked ( name );
2004-05-24 15:28:36 +00:00
if ( ! chan ) {
2005-04-13 05:45:53 +00:00
char buf [ BUFSIZ ];
snprintf ( buf , sizeof ( buf ), "Channel does not exist: %s" , name );
astman_send_error ( s , m , buf );
2004-05-24 15:28:36 +00:00
return 0 ;
}
2004-05-26 19:30:12 +00:00
if ( ! ast_strlen_zero ( name2 ))
2004-05-22 04:11:22 +00:00
chan2 = ast_get_channel_by_name_locked ( name2 );
res = ast_async_goto ( chan , context , exten , pi );
2002-09-12 18:32:42 +00:00
if ( ! res ) {
2004-05-26 19:30:12 +00:00
if ( ! ast_strlen_zero ( name2 )) {
2004-05-22 04:11:22 +00:00
if ( chan2 )
res = ast_async_goto ( chan2 , context , exten , pi );
else
res = - 1 ;
2002-09-12 18:32:42 +00:00
if ( ! res )
2003-09-08 16:44:36 +00:00
astman_send_ack ( s , m , "Dual Redirect successful" );
2002-09-12 18:32:42 +00:00
else
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Secondary redirect failed" );
2002-09-12 18:32:42 +00:00
} else
2003-09-08 16:44:36 +00:00
astman_send_ack ( s , m , "Redirect successful" );
2002-09-12 18:32:42 +00:00
} else
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Redirect failed" );
2004-05-22 04:11:22 +00:00
if ( chan )
ast_mutex_unlock ( & chan -> lock );
if ( chan2 )
ast_mutex_unlock ( & chan2 -> lock );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2005-02-26 07:15:18 +00:00
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 " ;
2005-04-13 05:45:53 +00:00
2005-10-26 13:03:17 +00:00
/*! \brief action_command: Manager command "command" - execute CLI command */
2003-01-28 22:33:41 +00:00
static int action_command ( struct mansession * s , struct message * m )
{
2003-03-31 17:40:18 +00:00
char * cmd = astman_get_header ( m , "Command" );
2004-04-29 02:59:29 +00:00
char * id = astman_get_header ( m , "ActionID" );
2006-03-25 23:50:09 +00:00
astman_append ( s , "Response: Follows \r\n Privilege: Command \r\n " );
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( id ))
2006-03-25 23:50:09 +00:00
astman_append ( s , "ActionID: %s \r\n " , id );
2003-09-08 16:44:36 +00:00
/* FIXME: Wedge a ActionID response in here, waiting for later changes */
2003-01-28 22:33:41 +00:00
ast_cli_command ( s -> fd , cmd );
2006-03-25 23:50:09 +00:00
astman_append ( s , "--END COMMAND-- \r\n\r\n " );
2003-01-28 22:33:41 +00:00
return 0 ;
}
2004-05-01 23:52:27 +00:00
static void * fast_originate ( void * data )
{
struct fast_originate_helper * in = data ;
int res ;
int reason = 0 ;
2005-02-02 02:54:15 +00:00
struct ast_channel * chan = NULL ;
2004-05-26 19:30:12 +00:00
if ( ! ast_strlen_zero ( in -> app )) {
2004-10-02 00:58:31 +00:00
res = ast_pbx_outgoing_app ( in -> tech , AST_FORMAT_SLINEAR , in -> data , in -> timeout , in -> app , in -> appdata , & reason , 1 ,
2006-03-26 16:08:42 +00:00
S_OR ( in -> cid_num , NULL ),
S_OR ( in -> cid_name , NULL ),
2006-02-11 18:31:59 +00:00
in -> vars , in -> account , & chan );
2004-05-01 23:52:27 +00:00
} else {
2004-10-02 00:58:31 +00:00
res = ast_pbx_outgoing_exten ( in -> tech , AST_FORMAT_SLINEAR , in -> data , in -> timeout , in -> context , in -> exten , in -> priority , & reason , 1 ,
2006-03-26 16:08:42 +00:00
S_OR ( in -> cid_num , NULL ),
S_OR ( in -> cid_name , NULL ),
2006-02-11 18:31:59 +00:00
in -> vars , in -> account , & chan );
2004-05-01 23:52:27 +00:00
}
2006-01-03 11:31:56 +00:00
/* Tell the manager what happened with the channel */
manager_event ( EVENT_FLAG_CALL ,
res ? "OriginateSuccess" : "OriginateFailure" ,
"%s"
"Channel: %s/%s \r\n "
"Context: %s \r\n "
"Exten: %s \r\n "
"Reason: %d \r\n "
"Uniqueid: %s \r\n "
"CallerID: %s \r\n "
"CallerIDName: %s \r\n " ,
in -> idtext , in -> tech , in -> data , in -> context , in -> exten , reason ,
chan ? chan -> uniqueid : "<null>" ,
in -> cid_num ? in -> cid_num : "<unknown>" ,
in -> cid_name ? in -> cid_name : "<unknown>"
);
2004-09-14 22:15:50 +00:00
2005-02-02 02:54:15 +00:00
/* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
if ( chan )
ast_mutex_unlock ( & chan -> lock );
2004-05-01 23:52:27 +00:00
free ( in );
return NULL ;
}
2004-06-02 20:08:08 +00:00
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 "
2005-10-05 21:12:57 +00:00
" Variable: Channel variable to set, multiple Variable: headers are allowed \n "
2004-06-02 20:08:08 +00:00
" Account: Account code \n "
" Async: Set to 'true' for fast origination \n " ;
2002-09-12 18:32:42 +00:00
static int action_originate ( struct mansession * s , struct message * m )
{
2003-03-31 17:40:18 +00:00
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" );
2005-01-27 16:33:12 +00:00
char * account = astman_get_header ( m , "Account" );
2003-06-26 20:32:52 +00:00
char * app = astman_get_header ( m , "Application" );
char * appdata = astman_get_header ( m , "Data" );
2004-05-01 23:52:27 +00:00
char * async = astman_get_header ( m , "Async" );
2004-09-14 22:15:50 +00:00
char * id = astman_get_header ( m , "ActionID" );
2005-07-15 23:24:51 +00:00
struct ast_variable * vars = astman_get_variables ( m );
2002-09-12 18:32:42 +00:00
char * tech , * data ;
2006-04-04 19:03:12 +00:00
char * l = NULL , * n = NULL ;
2002-09-12 18:32:42 +00:00
int pi = 0 ;
int res ;
int to = 30000 ;
int reason = 0 ;
char tmp [ 256 ];
2005-07-15 23:24:51 +00:00
char tmp2 [ 256 ];
2004-10-02 00:58:31 +00:00
2004-05-01 23:52:27 +00:00
pthread_t th ;
pthread_attr_t attr ;
2002-09-12 18:32:42 +00:00
if ( ! name ) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Channel not specified" );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2004-05-26 19:30:12 +00:00
if ( ! ast_strlen_zero ( priority ) && ( sscanf ( priority , "%d" , & pi ) != 1 )) {
2005-12-27 19:13:13 +00:00
if (( pi = ast_findlabel_extension ( NULL , context , exten , priority , NULL )) < 1 ) {
astman_send_error ( s , m , "Invalid priority \n " );
return 0 ;
}
2002-09-12 18:32:42 +00:00
}
2004-05-26 19:30:12 +00:00
if ( ! ast_strlen_zero ( timeout ) && ( sscanf ( timeout , "%d" , & to ) != 1 )) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Invalid timeout \n " );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2005-07-10 22:56:21 +00:00
ast_copy_string ( tmp , name , sizeof ( tmp ));
2002-09-12 18:32:42 +00:00
tech = tmp ;
data = strchr ( tmp , '/' );
if ( ! data ) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Invalid channel \n " );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2006-03-28 23:52:21 +00:00
* data ++ = '\0' ;
2005-07-10 22:56:21 +00:00
ast_copy_string ( tmp2 , callerid , sizeof ( tmp2 ));
2004-10-02 00:58:31 +00:00
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 ;
}
2004-09-17 14:15:11 +00:00
if ( ast_true ( async )) {
2004-05-01 23:52:27 +00:00
struct fast_originate_helper * fast = malloc ( sizeof ( struct fast_originate_helper ));
2004-09-17 14:15:11 +00:00
if ( ! fast ) {
2004-05-01 23:52:27 +00:00
res = - 1 ;
2004-09-17 14:15:11 +00:00
} else {
2004-05-01 23:52:27 +00:00
memset ( fast , 0 , sizeof ( struct fast_originate_helper ));
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( id ))
2004-09-14 22:15:50 +00:00
snprintf ( fast -> idtext , sizeof ( fast -> idtext ), "ActionID: %s \r\n " , id );
2005-07-10 22:56:21 +00:00
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 ));
2004-10-02 00:58:31 +00:00
if ( l )
2005-07-10 22:56:21 +00:00
ast_copy_string ( fast -> cid_num , l , sizeof ( fast -> cid_num ));
2004-10-02 00:58:31 +00:00
if ( n )
2005-07-10 22:56:21 +00:00
ast_copy_string ( fast -> cid_name , n , sizeof ( fast -> cid_name ));
2005-07-15 23:24:51 +00:00
fast -> vars = vars ;
2005-07-10 22:56:21 +00:00
ast_copy_string ( fast -> context , context , sizeof ( fast -> context ));
ast_copy_string ( fast -> exten , exten , sizeof ( fast -> exten ));
2006-02-11 18:31:59 +00:00
ast_copy_string ( fast -> account , account , sizeof ( fast -> account ));
2004-05-01 23:52:27 +00:00
fast -> timeout = to ;
fast -> priority = pi ;
pthread_attr_init ( & attr );
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_DETACHED );
2004-09-17 14:15:11 +00:00
if ( ast_pthread_create ( & th , & attr , fast_originate , fast )) {
2004-05-01 23:52:27 +00:00
res = - 1 ;
2004-09-17 14:15:11 +00:00
} else {
2004-05-01 23:52:27 +00:00
res = 0 ;
}
}
2004-05-26 19:30:12 +00:00
} else if ( ! ast_strlen_zero ( app )) {
2006-02-11 18:31:59 +00:00
res = ast_pbx_outgoing_app ( tech , AST_FORMAT_SLINEAR , data , to , app , appdata , & reason , 1 , l , n , vars , account , NULL );
2004-01-14 09:31:24 +00:00
} else {
2004-03-09 20:01:46 +00:00
if ( exten && context && pi )
2006-02-11 18:31:59 +00:00
res = ast_pbx_outgoing_exten ( tech , AST_FORMAT_SLINEAR , data , to , context , exten , pi , & reason , 1 , l , n , vars , account , NULL );
2004-03-09 20:01:46 +00:00
else {
astman_send_error ( s , m , "Originate with 'Exten' requires 'Context' and 'Priority'" );
return 0 ;
}
2003-06-26 20:32:52 +00:00
}
2002-09-12 18:32:42 +00:00
if ( ! res )
2003-09-08 16:44:36 +00:00
astman_send_ack ( s , m , "Originate successfully queued" );
2002-09-12 18:32:42 +00:00
else
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Originate failed" );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2005-11-14 19:00:38 +00:00
/*! \brief Help text for manager command mailboxstatus
*/
2005-02-26 07:15:18 +00:00
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 " ;
2005-11-14 19:00:38 +00:00
2003-02-24 06:00:18 +00:00
static int action_mailboxstatus ( struct mansession * s , struct message * m )
{
2003-03-31 17:40:18 +00:00
char * mailbox = astman_get_header ( m , "Mailbox" );
2003-09-08 16:44:36 +00:00
char * id = astman_get_header ( m , "ActionID" );
char idText [ 256 ] = "" ;
2004-09-10 15:11:38 +00:00
int ret ;
2005-10-26 18:54:24 +00:00
if ( ast_strlen_zero ( mailbox )) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Mailbox not specified" );
2003-02-24 06:00:18 +00:00
return 0 ;
}
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( id ))
2006-03-28 23:52:21 +00:00
snprintf ( idText , sizeof ( idText ), "ActionID: %s \r\n " , id );
2004-10-03 21:18:27 +00:00
ret = ast_app_has_voicemail ( mailbox , NULL );
2006-03-25 23:50:09 +00:00
astman_append ( s , "Response: Success \r\n "
2003-09-08 16:44:36 +00:00
"%s"
2003-04-07 02:04:56 +00:00
"Message: Mailbox Status \r\n "
"Mailbox: %s \r\n "
2004-09-10 15:11:38 +00:00
"Waiting: %d \r\n\r\n " , idText , mailbox , ret );
2003-04-06 18:19:51 +00:00
return 0 ;
}
2005-02-26 07:15:18 +00:00
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 " ;
2003-06-21 20:05:16 +00:00
static int action_mailboxcount ( struct mansession * s , struct message * m )
{
char * mailbox = astman_get_header ( m , "Mailbox" );
2003-09-08 16:44:36 +00:00
char * id = astman_get_header ( m , "ActionID" );
char idText [ 256 ] = "" ;
2003-06-21 20:05:16 +00:00
int newmsgs = 0 , oldmsgs = 0 ;
2005-10-26 18:54:24 +00:00
if ( ast_strlen_zero ( mailbox )) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Mailbox not specified" );
2003-06-21 20:05:16 +00:00
return 0 ;
}
ast_app_messagecount ( mailbox , & newmsgs , & oldmsgs );
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( id )) {
2006-03-28 23:52:21 +00:00
snprintf ( idText , sizeof ( idText ), "ActionID: %s \r\n " , id );
2005-09-28 23:10:14 +00:00
}
2006-03-25 23:50:09 +00:00
astman_append ( s , "Response: Success \r\n "
2003-09-08 16:44:36 +00:00
"%s"
2003-06-21 20:05:16 +00:00
"Message: Mailbox Message Count \r\n "
"Mailbox: %s \r\n "
"NewMessages: %d \r\n "
"OldMessages: %d \r\n "
" \r\n " ,
2003-09-08 16:44:36 +00:00
idText , mailbox , newmsgs , oldmsgs );
2003-06-21 20:05:16 +00:00
return 0 ;
}
2005-02-26 07:15:18 +00:00
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 " ;
2003-04-06 18:19:51 +00:00
static int action_extensionstate ( struct mansession * s , struct message * m )
{
char * exten = astman_get_header ( m , "Exten" );
char * context = astman_get_header ( m , "Context" );
2003-09-08 16:44:36 +00:00
char * id = astman_get_header ( m , "ActionID" );
char idText [ 256 ] = "" ;
2003-04-07 02:04:56 +00:00
char hint [ 256 ] = "" ;
2003-04-06 18:19:51 +00:00
int status ;
2005-10-26 18:54:24 +00:00
if ( ast_strlen_zero ( exten )) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Extension not specified" );
2003-04-06 18:19:51 +00:00
return 0 ;
}
2005-10-26 18:54:24 +00:00
if ( ast_strlen_zero ( context ))
2003-04-06 18:19:51 +00:00
context = "default" ;
status = ast_extension_state ( NULL , context , exten );
2005-02-01 01:53:25 +00:00
ast_get_hint ( hint , sizeof ( hint ) - 1 , NULL , 0 , NULL , context , exten );
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( id )) {
2006-03-28 23:52:21 +00:00
snprintf ( idText , sizeof ( idText ), "ActionID: %s \r\n " , id );
2003-09-08 16:44:36 +00:00
}
2006-03-25 23:50:09 +00:00
astman_append ( s , "Response: Success \r\n "
2003-09-08 16:44:36 +00:00
"%s"
2003-04-07 02:04:56 +00:00
"Message: Extension Status \r\n "
"Exten: %s \r\n "
"Context: %s \r\n "
"Hint: %s \r\n "
2003-09-08 16:44:36 +00:00
"Status: %d \r\n\r\n " ,
idText , exten , context , hint , status );
2003-02-24 06:00:18 +00:00
return 0 ;
}
2005-02-26 07:15:18 +00:00
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 " ;
2003-06-09 21:40:20 +00:00
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" ));
2004-05-26 19:30:12 +00:00
if ( ast_strlen_zero ( name )) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "No channel specified" );
2003-06-09 21:40:20 +00:00
return 0 ;
}
if ( ! timeout ) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "No timeout specified" );
2003-06-09 21:40:20 +00:00
return 0 ;
}
2005-06-06 02:29:18 +00:00
c = ast_get_channel_by_name_locked ( name );
2003-06-09 21:40:20 +00:00
if ( ! c ) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "No such channel" );
2003-06-09 21:40:20 +00:00
return 0 ;
}
ast_channel_setwhentohangup ( c , timeout );
2004-05-20 16:30:10 +00:00
ast_mutex_unlock ( & c -> lock );
2003-09-08 16:44:36 +00:00
astman_send_ack ( s , m , "Timeout Set" );
2003-06-09 21:40:20 +00:00
return 0 ;
}
2006-04-02 19:59:55 +00:00
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 ;
}
2002-09-12 18:32:42 +00:00
static int process_message ( struct mansession * s , struct message * m )
{
2004-07-14 07:44:19 +00:00
char action [ 80 ] = "" ;
2003-01-28 22:33:41 +00:00
struct manager_action * tmp = first_action ;
2003-09-08 16:44:36 +00:00
char * id = astman_get_header ( m , "ActionID" );
char idText [ 256 ] = "" ;
2004-06-30 16:56:51 +00:00
char iabuf [ INET_ADDRSTRLEN ];
2006-04-02 19:59:55 +00:00
int ret = 0 ;
2003-01-28 22:33:41 +00:00
2005-07-10 22:56:21 +00:00
ast_copy_string ( action , astman_get_header ( m , "Action" ), sizeof ( action ));
2003-03-20 17:21:54 +00:00
ast_log ( LOG_DEBUG , "Manager received command '%s' \n " , action );
2003-01-28 22:33:41 +00:00
2004-05-26 19:30:12 +00:00
if ( ast_strlen_zero ( action )) {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Missing action in request" );
2002-09-12 18:32:42 +00:00
return 0 ;
}
2006-04-02 19:59:55 +00:00
if ( ! ast_strlen_zero ( id )) {
2006-04-04 19:03:12 +00:00
snprintf ( idText , sizeof ( idText ), "ActionID: %s \r\n " , id );
2006-04-02 19:59:55 +00:00
}
2002-09-12 18:32:42 +00:00
if ( ! s -> authenticated ) {
2003-03-10 06:00:17 +00:00
if ( ! strcasecmp ( action , "Challenge" )) {
char * authtype ;
2003-03-31 17:40:18 +00:00
authtype = astman_get_header ( m , "AuthType" );
2003-03-10 06:00:17 +00:00
if ( ! strcasecmp ( authtype , "MD5" )) {
2005-10-26 18:54:24 +00:00
if ( ast_strlen_zero ( s -> challenge ))
2006-04-05 17:44:44 +00:00
snprintf ( s -> challenge , sizeof ( s -> challenge ), "%ld" , ast_random ());
2005-09-28 23:10:14 +00:00
ast_mutex_lock ( & s -> __lock );
2006-03-25 23:50:09 +00:00
astman_append ( s , "Response: Success \r\n "
2003-09-08 16:44:36 +00:00
"%s"
"Challenge: %s \r\n\r\n " ,
2006-04-04 19:03:12 +00:00
idText , s -> challenge );
2005-09-28 23:10:14 +00:00
ast_mutex_unlock ( & s -> __lock );
2003-03-10 06:00:17 +00:00
return 0 ;
} else {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Must specify AuthType" );
2003-03-10 06:00:17 +00:00
return 0 ;
}
} else if ( ! strcasecmp ( action , "Login" )) {
2002-09-12 18:32:42 +00:00
if ( authenticate ( s , m )) {
sleep ( 1 );
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Authentication failed" );
2002-09-12 18:32:42 +00:00
return - 1 ;
} else {
s -> authenticated = 1 ;
2005-01-05 06:24:59 +00:00
if ( option_verbose > 1 ) {
2006-04-04 19:03:12 +00:00
if ( displayconnects ) {
2006-04-01 08:49:54 +00:00
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 ));
2005-01-05 06:24:59 +00:00
}
}
2006-04-01 08:49:54 +00:00
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 ));
2003-09-08 16:44:36 +00:00
astman_send_ack ( s , m , "Authentication accepted" );
2002-09-12 18:32:42 +00:00
}
2003-08-18 06:03:16 +00:00
} else if ( ! strcasecmp ( action , "Logoff" )) {
2003-09-08 16:44:36 +00:00
astman_send_ack ( s , m , "See ya" );
2003-08-18 06:03:16 +00:00
return - 1 ;
} else
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Authentication Required" );
2002-09-12 18:32:42 +00:00
} else {
2005-09-28 23:10:14 +00:00
ast_mutex_lock ( & s -> __lock );
2006-04-01 08:49:54 +00:00
s -> busy ++ ;
2005-09-28 23:10:14 +00:00
ast_mutex_unlock ( & s -> __lock );
2006-04-04 19:03:12 +00:00
while ( tmp ) {
2003-01-28 22:33:41 +00:00
if ( ! strcasecmp ( action , tmp -> action )) {
if (( s -> writeperm & tmp -> authority ) == tmp -> authority ) {
if ( tmp -> func ( s , m ))
2005-09-28 23:10:14 +00:00
ret = - 1 ;
2002-09-12 18:32:42 +00:00
} else {
2003-09-08 16:44:36 +00:00
astman_send_error ( s , m , "Permission denied" );
2002-09-12 18:32:42 +00:00
}
2005-09-28 23:10:14 +00:00
break ;
2002-09-12 18:32:42 +00:00
}
2003-01-28 22:33:41 +00:00
tmp = tmp -> next ;
2002-09-12 18:32:42 +00:00
}
2005-10-03 21:01:29 +00:00
if ( ! tmp )
2005-09-30 23:44:12 +00:00
astman_send_error ( s , m , "Invalid/unknown command" );
2002-09-12 18:32:42 +00:00
}
2006-04-02 19:59:55 +00:00
if ( ret )
return ret ;
return process_events ( s );
2002-09-12 18:32:42 +00:00
}
static int get_input ( struct mansession * s , char * output )
{
/* output must have at least sizeof(s->inbuf) space */
int res ;
int x ;
2004-04-25 20:42:45 +00:00
struct pollfd fds [ 1 ];
2004-06-30 16:56:51 +00:00
char iabuf [ INET_ADDRSTRLEN ];
2006-04-04 19:03:12 +00:00
for ( x = 1 ; x < s -> inlen ; x ++ ) {
2002-09-12 18:32:42 +00:00
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 ) {
2004-06-29 12:56:46 +00:00
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 );
2002-09-12 18:32:42 +00:00
s -> inlen = 0 ;
}
2004-04-25 20:42:45 +00:00
fds [ 0 ]. fd = s -> fd ;
fds [ 0 ]. events = POLLIN ;
2005-09-25 16:58:56 +00:00
do {
2006-04-02 19:59:55 +00:00
ast_mutex_lock ( & s -> __lock );
s -> waiting_thread = pthread_self ();
ast_mutex_unlock ( & s -> __lock );
2005-09-25 16:58:56 +00:00
res = poll ( fds , 1 , - 1 );
2006-04-02 19:59:55 +00:00
ast_mutex_lock ( & s -> __lock );
s -> waiting_thread = AST_PTHREADT_NULL ;
ast_mutex_unlock ( & s -> __lock );
2005-09-25 16:58:56 +00:00
if ( res < 0 ) {
2005-09-29 20:37:01 +00:00
if ( errno == EINTR ) {
if ( s -> dead )
return - 1 ;
2006-04-02 19:59:55 +00:00
return 0 ;
2005-09-29 20:37:01 +00:00
}
2005-09-25 16:58:56 +00:00
ast_log ( LOG_WARNING , "Select returned error: %s \n " , strerror ( errno ));
return - 1 ;
} else if ( res > 0 ) {
2005-09-28 23:10:14 +00:00
ast_mutex_lock ( & s -> __lock );
2005-09-25 16:58:56 +00:00
res = read ( s -> fd , s -> inbuf + s -> inlen , sizeof ( s -> inbuf ) - 1 - s -> inlen );
2005-09-28 23:10:14 +00:00
ast_mutex_unlock ( & s -> __lock );
2005-09-25 16:58:56 +00:00
if ( res < 1 )
return - 1 ;
break ;
}
} while ( 1 );
2002-09-12 18:32:42 +00:00
s -> inlen += res ;
s -> inbuf [ s -> inlen ] = '\0' ;
return 0 ;
}
static void * session_do ( void * data )
{
struct mansession * s = data ;
struct message m ;
2004-06-30 16:56:51 +00:00
char iabuf [ INET_ADDRSTRLEN ];
2002-09-12 18:32:42 +00:00
int res ;
2005-09-28 23:10:14 +00:00
ast_mutex_lock ( & s -> __lock );
2006-03-25 23:50:09 +00:00
astman_append ( s , "Asterisk Call Manager/1.0 \r\n " );
2005-09-28 23:10:14 +00:00
ast_mutex_unlock ( & s -> __lock );
2005-09-30 16:27:14 +00:00
memset ( & m , 0 , sizeof ( m ));
2002-09-12 18:32:42 +00:00
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' ;
2004-05-26 19:30:12 +00:00
if ( ast_strlen_zero ( m . headers [ m . hdrcount ])) {
2002-09-12 18:32:42 +00:00
if ( process_message ( s , & m ))
break ;
2005-09-30 16:27:14 +00:00
memset ( & m , 0 , sizeof ( m ));
2006-02-11 18:31:59 +00:00
} else if ( m . hdrcount < AST_MAX_MANHEADERS - 1 )
2002-09-12 18:32:42 +00:00
m . hdrcount ++ ;
2006-04-02 19:59:55 +00:00
} else if ( res < 0 ) {
2002-09-12 18:32:42 +00:00
break ;
2006-04-02 19:59:55 +00:00
} else if ( s -> eventq -> next ) {
if ( process_events ( s ))
break ;
}
2002-09-12 18:32:42 +00:00
}
if ( s -> authenticated ) {
2005-01-05 06:24:59 +00:00
if ( option_verbose > 1 ) {
if ( displayconnects )
2006-04-04 19:03:12 +00:00
ast_verbose ( VERBOSE_PREFIX_2 "Manager '%s' logged off from %s \n " , s -> username , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ));
2005-01-05 06:24:59 +00:00
}
2004-06-29 12:56:46 +00:00
ast_log ( LOG_EVENT , "Manager '%s' logged off from %s \n " , s -> username , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ));
2002-09-12 18:32:42 +00:00
} else {
2005-01-05 06:24:59 +00:00
if ( option_verbose > 1 ) {
2006-04-04 19:03:12 +00:00
if ( displayconnects )
2005-01-05 06:24:59 +00:00
ast_verbose ( VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate \n " , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ));
}
2004-06-29 12:56:46 +00:00
ast_log ( LOG_EVENT , "Failed attempt from %s \n " , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ));
2002-09-12 18:32:42 +00:00
}
destroy_session ( s );
return NULL ;
}
static void * accept_thread ( void * ignore )
{
int as ;
struct sockaddr_in sin ;
2005-05-15 03:21:51 +00:00
socklen_t sinlen ;
2006-04-02 19:59:55 +00:00
struct eventqent * eqe ;
2006-04-04 19:03:12 +00:00
struct mansession * s , * prev = NULL , * next ;
2003-03-20 17:21:54 +00:00
struct protoent * p ;
int arg = 1 ;
2004-02-07 07:13:33 +00:00
int flags ;
2003-03-06 06:00:17 +00:00
pthread_attr_t attr ;
2006-04-01 08:49:54 +00:00
time_t now ;
struct pollfd pfds [ 1 ];
char iabuf [ INET_ADDRSTRLEN ];
2003-03-06 06:00:17 +00:00
pthread_attr_init ( & attr );
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_DETACHED );
2003-03-20 17:21:54 +00:00
2002-09-12 18:32:42 +00:00
for (;;) {
2006-04-01 08:49:54 +00:00
time ( & now );
ast_mutex_lock ( & sessionlock );
prev = NULL ;
s = sessions ;
2006-04-04 19:03:12 +00:00
while ( s ) {
2006-04-01 08:49:54 +00:00
next = s -> next ;
if ( s -> sessiontimeout && ( now > s -> sessiontimeout ) && ! s -> inuse ) {
2006-04-02 19:59:55 +00:00
num_sessions -- ;
2006-04-01 08:49:54 +00:00
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 ;
}
2006-04-02 19:59:55 +00:00
/* 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 );
}
2006-04-01 08:49:54 +00:00
ast_mutex_unlock ( & sessionlock );
2002-09-12 18:32:42 +00:00
sinlen = sizeof ( sin );
2006-04-01 08:49:54 +00:00
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 ;
2003-09-08 16:48:07 +00:00
as = accept ( asock , ( struct sockaddr * ) & sin , & sinlen );
2002-09-12 18:32:42 +00:00
if ( as < 0 ) {
ast_log ( LOG_NOTICE , "Accept returned -1: %s \n " , strerror ( errno ));
continue ;
}
2003-03-20 17:21:54 +00:00
p = getprotobyname ( "tcp" );
2005-03-23 05:56:32 +00:00
if ( p ) {
2003-03-20 17:21:54 +00:00
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 ));
}
}
2002-09-12 18:32:42 +00:00
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 ));
2005-10-04 22:25:15 +00:00
s -> writetimeout = 100 ;
2006-04-01 08:49:54 +00:00
s -> waiting_thread = AST_PTHREADT_NULL ;
2004-03-01 21:12:32 +00:00
2006-04-04 19:03:12 +00:00
if ( ! block_sockets ) {
2004-03-01 21:12:32 +00:00
/* For safety, make sure socket is non-blocking */
flags = fcntl ( as , F_GETFL );
fcntl ( as , F_SETFL , flags | O_NONBLOCK );
}
2005-09-28 23:10:14 +00:00
ast_mutex_init ( & s -> __lock );
2002-09-12 18:32:42 +00:00
s -> fd = as ;
2004-07-08 15:39:35 +00:00
s -> send_events = - 1 ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & sessionlock );
2006-04-02 19:59:55 +00:00
num_sessions ++ ;
2002-09-12 18:32:42 +00:00
s -> next = sessions ;
sessions = s ;
2006-04-02 19:59:55 +00:00
/* 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 );
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & sessionlock );
2005-09-29 20:37:01 +00:00
if ( ast_pthread_create ( & s -> t , & attr , session_do , s ))
2002-09-12 18:32:42 +00:00
destroy_session ( s );
}
2003-03-06 06:00:17 +00:00
pthread_attr_destroy ( & attr );
2003-01-28 22:33:41 +00:00
return NULL ;
2002-09-12 18:32:42 +00:00
}
2006-04-02 19:59:55 +00:00
static int append_event ( const char * str , int category )
2005-09-28 23:10:14 +00:00
{
2006-04-04 19:03:12 +00:00
struct eventqent * tmp , * prev = NULL ;
2005-09-28 23:10:14 +00:00
tmp = malloc ( sizeof ( struct eventqent ) + strlen ( str ));
if ( tmp ) {
2006-04-02 19:59:55 +00:00
ast_mutex_init ( & tmp -> lock );
2005-09-28 23:10:14 +00:00
tmp -> next = NULL ;
2006-04-02 19:59:55 +00:00
tmp -> category = category ;
2005-09-28 23:10:14 +00:00
strcpy ( tmp -> eventdata , str );
2006-04-02 19:59:55 +00:00
if ( master_eventq ) {
prev = master_eventq ;
2006-04-04 19:03:12 +00:00
while ( prev -> next )
2005-09-28 23:10:14 +00:00
prev = prev -> next ;
prev -> next = tmp ;
} else {
2006-04-02 19:59:55 +00:00
master_eventq = tmp ;
2005-09-28 23:10:14 +00:00
}
2006-04-02 19:59:55 +00:00
tmp -> usecount = num_sessions ;
2005-09-28 23:10:14 +00:00
return 0 ;
}
return - 1 ;
}
2005-10-26 13:03:17 +00:00
/*! \brief manager_event: Send AMI event to client */
2006-01-07 15:23:38 +00:00
int manager_event ( int category , const char * event , const char * fmt , ...)
2002-09-12 18:32:42 +00:00
{
struct mansession * s ;
2005-10-01 15:41:27 +00:00
char auth [ 80 ];
2005-10-03 19:40:03 +00:00
char tmp [ 4096 ] = "" ;
2005-10-01 15:41:27 +00:00
char * tmp_next = tmp ;
size_t tmp_left = sizeof ( tmp ) - 2 ;
2002-09-12 18:32:42 +00:00
va_list ap ;
2006-04-02 19:59:55 +00:00
struct timeval now ;
2002-09-12 18:32:42 +00:00
2006-04-02 19:59:55 +00:00
/* Abort if there aren't any manager sessions */
if ( ! num_sessions )
return 0 ;
ast_build_string ( & tmp_next , & tmp_left , "Event: %s \r\n Privilege: %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' ;
* tmp_next = '\0' ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & sessionlock );
2006-04-02 19:59:55 +00:00
/* Append even to master list and wake up any sleeping sessions */
append_event ( tmp , category );
2005-09-30 23:52:04 +00:00
for ( s = sessions ; s ; s = s -> next ) {
ast_mutex_lock ( & s -> __lock );
2006-04-02 19:59:55 +00:00
if ( s -> waiting_thread != AST_PTHREADT_NULL )
pthread_kill ( s -> waiting_thread , SIGURG );
2005-09-30 23:52:04 +00:00
ast_mutex_unlock ( & s -> __lock );
2002-09-12 18:32:42 +00:00
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & sessionlock );
2005-09-30 23:52:04 +00:00
2002-09-12 18:32:42 +00:00
return 0 ;
}
2005-09-28 23:10:14 +00:00
int ast_manager_unregister ( char * action )
{
2003-01-28 22:33:41 +00:00
struct manager_action * cur = first_action , * prev = first_action ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & actionlock );
2006-04-04 19:03:12 +00:00
while ( cur ) {
2003-01-28 22:33:41 +00:00
if ( ! strcasecmp ( action , cur -> action )) {
prev -> next = cur -> next ;
free ( cur );
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 "Manager unregistered action %s \n " , action );
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & actionlock );
2003-01-28 22:33:41 +00:00
return 0 ;
}
prev = cur ;
cur = cur -> next ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & actionlock );
2003-01-28 22:33:41 +00:00
return 0 ;
}
2003-04-06 23:32:31 +00:00
static int manager_state_cb ( char * context , char * exten , int state , void * data )
{
/* Notify managers of change */
manager_event ( EVENT_FLAG_CALL , "ExtensionStatus" , "Exten: %s \r\n Context: %s \r\n Status: %d \r\n " , exten , context , state );
return 0 ;
}
2004-06-02 20:08:08 +00:00
static int ast_manager_register_struct ( struct manager_action * act )
2003-01-28 22:33:41 +00:00
{
struct manager_action * cur = first_action , * prev = NULL ;
2004-06-02 20:08:08 +00:00
int ret ;
2003-01-28 22:33:41 +00:00
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & actionlock );
2006-04-04 19:03:12 +00:00
while ( cur ) { /* Walk the list of actions */
2004-06-02 20:08:08 +00:00
ret = strcasecmp ( cur -> action , act -> action );
if ( ret == 0 ) {
ast_log ( LOG_WARNING , "Manager: Action '%s' already registered \n " , act -> action );
2004-01-14 06:31:18 +00:00
ast_mutex_unlock ( & actionlock );
return - 1 ;
2004-06-02 20:08:08 +00:00
} else if ( ret > 0 ) {
/* Insert these alphabetically */
if ( prev ) {
act -> next = prev -> next ;
prev -> next = act ;
} else {
act -> next = first_action ;
first_action = act ;
}
break ;
2004-01-14 06:31:18 +00:00
}
2003-01-28 22:33:41 +00:00
prev = cur ;
cur = cur -> next ;
}
2004-06-02 20:08:08 +00:00
if ( ! cur ) {
if ( prev )
prev -> next = act ;
else
first_action = act ;
act -> next = NULL ;
}
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 "Manager registered action %s \n " , act -> action );
ast_mutex_unlock ( & actionlock );
return 0 ;
}
2005-11-14 19:00:38 +00:00
/*! \brief register a new command with manager, including online help. This is
the preferred way to register a manager command */
2005-03-22 19:09:12 +00:00
int ast_manager_register2 ( const char * action , int auth , int ( * func )( struct mansession * s , struct message * m ), const char * synopsis , const char * description )
2004-06-02 20:08:08 +00:00
{
struct manager_action * cur ;
cur = malloc ( sizeof ( struct manager_action ));
if ( ! cur ) {
2003-01-28 22:33:41 +00:00
ast_log ( LOG_WARNING , "Manager: out of memory trying to register action \n " );
return - 1 ;
}
2004-06-02 20:08:08 +00:00
cur -> action = action ;
2003-01-28 22:33:41 +00:00
cur -> authority = auth ;
cur -> func = func ;
cur -> synopsis = synopsis ;
2004-06-02 20:08:08 +00:00
cur -> description = description ;
2003-01-28 22:33:41 +00:00
cur -> next = NULL ;
2004-06-02 20:08:08 +00:00
ast_manager_register_struct ( cur );
2003-01-28 22:33:41 +00:00
return 0 ;
}
2005-11-14 19:00:38 +00:00
/*! @}
END Doxygen group */
2003-01-28 22:33:41 +00:00
2006-04-01 08:49:54 +00:00
static struct mansession * find_session ( unsigned long ident )
{
struct mansession * s ;
ast_mutex_lock ( & sessionlock );
s = sessions ;
2006-04-04 19:03:12 +00:00
while ( s ) {
2006-04-01 08:49:54 +00:00
ast_mutex_lock ( & s -> __lock );
if ( s -> sessiontimeout && ( s -> managerid == ident ) && ! s -> needdestroy ) {
s -> inuse ++ ;
break ;
}
ast_mutex_unlock ( & s -> __lock );
s = s -> next ;
}
ast_mutex_unlock ( & sessionlock );
return s ;
}
static void vars2msg ( struct message * m , struct ast_variable * vars )
{
int x ;
2006-04-04 19:03:12 +00:00
for ( x = 0 ; vars && ( x < AST_MAX_MANHEADERS ); x ++ , vars = vars -> next ) {
2006-04-01 08:49:54 +00:00
if ( ! vars )
break ;
m -> hdrcount = x + 1 ;
snprintf ( m -> headers [ x ], sizeof ( m -> headers [ x ]), "%s: %s" , vars -> name , vars -> value );
}
}
#define FORMAT_RAW 0
#define FORMAT_HTML 1
#define FORMAT_XML 2
static char * contenttype [] = { "plain" , "html" , "xml" };
static char * generic_http_callback ( int format , struct sockaddr_in * requestor , const char * uri , struct ast_variable * params , int * status , char ** title , int * contentlength )
{
2006-04-04 19:03:12 +00:00
struct mansession * s = NULL ;
unsigned long ident = 0 ;
2006-04-01 08:49:54 +00:00
char workspace [ 256 ];
char cookie [ 128 ];
char iabuf [ INET_ADDRSTRLEN ];
2006-04-03 18:38:28 +00:00
size_t len = sizeof ( workspace );
2006-04-01 08:49:54 +00:00
int blastaway = 0 ;
char * c = workspace ;
2006-04-04 19:03:12 +00:00
char * retval = NULL ;
2006-04-01 08:49:54 +00:00
struct message m ;
struct ast_variable * v ;
2006-04-04 19:03:12 +00:00
2006-04-01 08:49:54 +00:00
v = params ;
2006-04-04 19:03:12 +00:00
while ( v ) {
2006-04-01 08:49:54 +00:00
if ( ! strcasecmp ( v -> name , "mansession_id" )) {
sscanf ( v -> value , "%lx" , & ident );
break ;
}
v = v -> next ;
}
s = find_session ( ident );
if ( ! s ) {
/* Create new session */
s = calloc ( 1 , sizeof ( struct mansession ));
memcpy ( & s -> sin , requestor , sizeof ( s -> sin ));
s -> fd = - 1 ;
s -> waiting_thread = AST_PTHREADT_NULL ;
s -> send_events = 0 ;
ast_mutex_init ( & s -> __lock );
ast_mutex_lock ( & s -> __lock );
ast_mutex_lock ( & sessionlock );
s -> inuse = 1 ;
s -> managerid = rand () | ( unsigned long ) s ;
s -> next = sessions ;
sessions = s ;
2006-04-02 19:59:55 +00:00
num_sessions ++ ;
/* Hook into the last spot in the event queue */
s -> eventq = master_eventq ;
2006-04-04 19:03:12 +00:00
while ( s -> eventq -> next )
2006-04-02 19:59:55 +00:00
s -> eventq = s -> eventq -> next ;
ast_mutex_lock ( & s -> eventq -> lock );
s -> eventq -> usecount ++ ;
ast_mutex_unlock ( & s -> eventq -> lock );
2006-04-01 08:49:54 +00:00
ast_mutex_unlock ( & sessionlock );
}
2006-04-02 19:59:55 +00:00
/* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2006-04-01 08:49:54 +00:00
time ( & s -> sessiontimeout );
2006-04-02 19:59:55 +00:00
if ( ! s -> authenticated && ( httptimeout > 5 ))
s -> sessiontimeout += 5 ;
else
s -> sessiontimeout += httptimeout ;
2006-04-01 08:49:54 +00:00
ast_mutex_unlock ( & s -> __lock );
memset ( & m , 0 , sizeof ( m ));
if ( s ) {
char tmp [ 80 ];
ast_build_string ( & c , & len , "Content-type: text/%s \n " , contenttype [ format ]);
sprintf ( tmp , "%08lx" , s -> managerid );
ast_build_string ( & c , & len , "%s \r\n " , ast_http_setcookie ( "mansession_id" , tmp , httptimeout , cookie , sizeof ( cookie )));
if ( format == FORMAT_HTML )
ast_build_string ( & c , & len , "<title>Asterisk™ Manager Test Interface</title>" );
vars2msg ( & m , params );
if ( format == FORMAT_XML ) {
ast_build_string ( & c , & len , "<ajax-response> \n " );
} else if ( format == FORMAT_HTML ) {
ast_build_string ( & c , & len , "<body bgcolor= \" #ffffff \" ><table align=center bgcolor= \" #f1f1f1 \" width= \" 500 \" > \r\n " );
ast_build_string ( & c , & len , "<tr><td colspan= \" 2 \" bgcolor= \" #f1f1ff \" ><h1> Manager Tester</h1></td></tr> \r\n " );
}
if ( process_message ( s , & m )) {
if ( s -> authenticated ) {
if ( option_verbose > 1 ) {
if ( displayconnects )
ast_verbose ( VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s \n " , s -> username , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ));
}
ast_log ( LOG_EVENT , "HTTP Manager '%s' logged off from %s \n " , s -> username , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ));
} else {
if ( option_verbose > 1 ) {
if ( displayconnects )
ast_verbose ( VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate \n " , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ));
}
ast_log ( LOG_EVENT , "HTTP Failed attempt from %s \n " , ast_inet_ntoa ( iabuf , sizeof ( iabuf ), s -> sin . sin_addr ));
}
s -> needdestroy = 1 ;
}
if ( s -> outputstr ) {
char * tmp ;
if ( format == FORMAT_XML )
tmp = xml_translate ( s -> outputstr , params );
else if ( format == FORMAT_HTML )
tmp = html_translate ( s -> outputstr );
else
tmp = s -> outputstr ;
if ( tmp ) {
retval = malloc ( strlen ( workspace ) + strlen ( tmp ) + 128 );
if ( retval ) {
strcpy ( retval , workspace );
strcpy ( retval + strlen ( retval ), tmp );
c = retval + strlen ( retval );
len = 120 ;
}
free ( tmp );
}
if ( tmp != s -> outputstr )
free ( s -> outputstr );
s -> outputstr = NULL ;
}
/* Still okay because c would safely be pointing to workspace even
if retval failed to allocate above */
if ( format == FORMAT_XML ) {
ast_build_string ( & c , & len , "</ajax-response> \n " );
} else if ( format == FORMAT_HTML )
ast_build_string ( & c , & len , "</table></body> \r\n " );
} else {
* status = 500 ;
* title = strdup ( "Server Error" );
}
ast_mutex_lock ( & s -> __lock );
if ( s -> needdestroy ) {
if ( s -> inuse == 1 ) {
ast_log ( LOG_DEBUG , "Need destroy, doing it now! \n " );
blastaway = 1 ;
} else {
ast_log ( LOG_DEBUG , "Need destroy, but can't do it yet! \n " );
if ( s -> waiting_thread != AST_PTHREADT_NULL )
pthread_kill ( s -> waiting_thread , SIGURG );
s -> inuse -- ;
}
} else
s -> inuse -- ;
ast_mutex_unlock ( & s -> __lock );
if ( blastaway )
destroy_session ( s );
if ( * status != 200 )
return ast_http_error ( 500 , "Server Error" , NULL , "Internal Server Error (out of memory) \n " );
return retval ;
}
static char * manager_http_callback ( struct sockaddr_in * requestor , const char * uri , struct ast_variable * params , int * status , char ** title , int * contentlength )
{
return generic_http_callback ( FORMAT_HTML , requestor , uri , params , status , title , contentlength );
}
static char * mxml_http_callback ( struct sockaddr_in * requestor , const char * uri , struct ast_variable * params , int * status , char ** title , int * contentlength )
{
return generic_http_callback ( FORMAT_XML , requestor , uri , params , status , title , contentlength );
}
static char * rawman_http_callback ( struct sockaddr_in * requestor , const char * uri , struct ast_variable * params , int * status , char ** title , int * contentlength )
{
return generic_http_callback ( FORMAT_RAW , requestor , uri , params , status , title , contentlength );
}
struct ast_http_uri rawmanuri = {
. description = "Raw HTTP Manager Event Interface" ,
. uri = "rawman" ,
. has_subtree = 0 ,
. callback = rawman_http_callback ,
};
struct ast_http_uri manageruri = {
. description = "HTML Manager Event Interface" ,
. uri = "manager" ,
. has_subtree = 0 ,
. callback = manager_http_callback ,
};
struct ast_http_uri managerxmluri = {
. description = "XML Manager Event Interface" ,
. uri = "mxml" ,
. has_subtree = 0 ,
. callback = mxml_http_callback ,
};
2003-01-28 22:33:41 +00:00
static int registered = 0 ;
2006-04-01 08:49:54 +00:00
static int webregged = 0 ;
2003-01-28 22:33:41 +00:00
2002-09-12 18:32:42 +00:00
int init_manager ( void )
{
struct ast_config * cfg ;
char * val ;
int oldportno = portno ;
static struct sockaddr_in ba ;
int x = 1 ;
2006-04-01 08:49:54 +00:00
int flags ;
2006-04-04 19:03:12 +00:00
int webenabled = 0 ;
2006-04-01 08:49:54 +00:00
int newhttptimeout = 60 ;
2003-01-28 22:33:41 +00:00
if ( ! registered ) {
/* Register default actions */
2005-04-04 16:18:11 +00:00
ast_manager_register2 ( "Ping" , 0 , action_ping , "Keepalive command" , mandescr_ping );
ast_manager_register2 ( "Events" , 0 , action_events , "Control Event Flow" , mandescr_events );
2004-06-29 05:04:48 +00:00
ast_manager_register2 ( "Logoff" , 0 , action_logoff , "Logoff Manager" , mandescr_logoff );
ast_manager_register2 ( "Hangup" , EVENT_FLAG_CALL , action_hangup , "Hangup Channel" , mandescr_hangup );
2005-04-13 05:45:53 +00:00
ast_manager_register ( "Status" , EVENT_FLAG_CALL , action_status , "Lists channel status" );
ast_manager_register2 ( "Setvar" , EVENT_FLAG_CALL , action_setvar , "Set Channel Variable" , mandescr_setvar );
ast_manager_register2 ( "Getvar" , EVENT_FLAG_CALL , action_getvar , "Gets a Channel Variable" , mandescr_getvar );
ast_manager_register2 ( "Redirect" , EVENT_FLAG_CALL , action_redirect , "Redirect (transfer) a call" , mandescr_redirect );
2004-06-02 20:08:08 +00:00
ast_manager_register2 ( "Originate" , EVENT_FLAG_CALL , action_originate , "Originate Call" , mandescr_originate );
2005-04-13 05:45:53 +00:00
ast_manager_register2 ( "Command" , EVENT_FLAG_COMMAND , action_command , "Execute Asterisk CLI Command" , mandescr_command );
ast_manager_register2 ( "ExtensionState" , EVENT_FLAG_CALL , action_extensionstate , "Check Extension Status" , mandescr_extensionstate );
ast_manager_register2 ( "AbsoluteTimeout" , EVENT_FLAG_CALL , action_timeout , "Set Absolute Timeout" , mandescr_timeout );
ast_manager_register2 ( "MailboxStatus" , EVENT_FLAG_CALL , action_mailboxstatus , "Check Mailbox" , mandescr_mailboxstatus );
ast_manager_register2 ( "MailboxCount" , EVENT_FLAG_CALL , action_mailboxcount , "Check Mailbox Message Count" , mandescr_mailboxcount );
2004-06-02 22:30:42 +00:00
ast_manager_register2 ( "ListCommands" , 0 , action_listcommands , "List available manager commands" , mandescr_listcommands );
2006-04-01 08:49:54 +00:00
ast_manager_register2 ( "WaitEvent" , 0 , action_waitevent , "Wait for an event to occur" , mandescr_waitevent );
2002-09-12 18:32:42 +00:00
2004-06-02 20:08:08 +00:00
ast_cli_register ( & show_mancmd_cli );
2003-01-28 22:33:41 +00:00
ast_cli_register ( & show_mancmds_cli );
2003-02-21 06:00:08 +00:00
ast_cli_register ( & show_manconn_cli );
2006-04-02 19:59:55 +00:00
ast_cli_register ( & show_maneventq_cli );
2003-04-06 23:32:31 +00:00
ast_extension_state_add ( NULL , NULL , manager_state_cb , NULL );
2003-01-28 22:33:41 +00:00
registered = 1 ;
2006-04-02 19:59:55 +00:00
/* Append placeholder event so master_eventq never runs dry */
append_event ( "Event: Placeholder \r\n\r\n " , 0 );
2003-01-28 22:33:41 +00:00
}
2002-09-12 18:32:42 +00:00
portno = DEFAULT_MANAGER_PORT ;
2005-01-05 06:24:59 +00:00
displayconnects = 1 ;
2005-01-25 06:10:20 +00:00
cfg = ast_config_load ( "manager.conf" );
2002-09-12 18:32:42 +00:00
if ( ! cfg ) {
ast_log ( LOG_NOTICE , "Unable to open management configuration manager.conf. Call management disabled. \n " );
return 0 ;
}
val = ast_variable_retrieve ( cfg , "general" , "enabled" );
if ( val )
enabled = ast_true ( val );
2004-03-01 21:12:32 +00:00
val = ast_variable_retrieve ( cfg , "general" , "block-sockets" );
2006-04-04 19:03:12 +00:00
if ( val )
2004-03-01 21:12:32 +00:00
block_sockets = ast_true ( val );
2006-04-01 08:49:54 +00:00
val = ast_variable_retrieve ( cfg , "general" , "webenabled" );
if ( val )
webenabled = ast_true ( val );
2004-04-05 19:40:30 +00:00
if (( val = ast_variable_retrieve ( cfg , "general" , "port" ))) {
2002-09-12 18:32:42 +00:00
if ( sscanf ( val , "%d" , & portno ) != 1 ) {
ast_log ( LOG_WARNING , "Invalid port number '%s' \n " , val );
portno = DEFAULT_MANAGER_PORT ;
}
}
2006-01-03 22:07:12 +00:00
if (( val = ast_variable_retrieve ( cfg , "general" , "displayconnects" )))
displayconnects = ast_true ( val );
if (( val = ast_variable_retrieve ( cfg , "general" , "timestampevents" )))
timestampevents = ast_true ( val );
2006-04-01 08:49:54 +00:00
if (( val = ast_variable_retrieve ( cfg , "general" , "httptimeout" )))
newhttptimeout = atoi ( val );
2006-04-03 07:30:07 +00:00
memset ( & ba , 0 , sizeof ( ba ));
2002-09-12 18:32:42 +00:00
ba . sin_family = AF_INET ;
ba . sin_port = htons ( portno );
2006-04-03 07:30:07 +00:00
2002-09-12 18:32:42 +00:00
if (( val = ast_variable_retrieve ( cfg , "general" , "bindaddr" ))) {
if ( ! inet_aton ( val , & ba . sin_addr )) {
ast_log ( LOG_WARNING , "Invalid address '%s' specified, using 0.0.0.0 \n " , val );
memset ( & ba . sin_addr , 0 , sizeof ( ba . sin_addr ));
}
}
2004-03-01 21:12:32 +00:00
2006-04-04 19:03:12 +00:00
2002-09-12 18:32:42 +00:00
if (( asock > - 1 ) && (( portno != oldportno ) || ! enabled )) {
#if 0
/* Can't be done yet */
close(asock);
asock = -1;
#else
ast_log ( LOG_WARNING , "Unable to change management port / enabled \n " );
#endif
}
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg );
2003-08-04 21:44:48 +00:00
2006-04-01 08:49:54 +00:00
if ( webenabled && enabled ) {
if ( ! webregged ) {
ast_http_uri_link ( & rawmanuri );
ast_http_uri_link ( & manageruri );
ast_http_uri_link ( & managerxmluri );
webregged = 1 ;
}
} else {
if ( webregged ) {
ast_http_uri_unlink ( & rawmanuri );
ast_http_uri_unlink ( & manageruri );
ast_http_uri_unlink ( & managerxmluri );
webregged = 0 ;
}
}
if ( newhttptimeout > 0 )
httptimeout = newhttptimeout ;
2006-04-04 19:03:12 +00:00
2002-09-12 18:32:42 +00:00
/* If not enabled, do nothing */
2003-08-04 21:44:48 +00:00
if ( ! enabled ) {
2002-09-12 18:32:42 +00:00
return 0 ;
2003-08-04 21:44:48 +00:00
}
2006-04-04 19:03:12 +00:00
2002-09-12 18:32:42 +00:00
if ( asock < 0 ) {
asock = socket ( AF_INET , SOCK_STREAM , 0 );
if ( asock < 0 ) {
ast_log ( LOG_WARNING , "Unable to create socket: %s \n " , strerror ( errno ));
return - 1 ;
}
setsockopt ( asock , SOL_SOCKET , SO_REUSEADDR , & x , sizeof ( x ));
2003-09-08 16:48:07 +00:00
if ( bind ( asock , ( struct sockaddr * ) & ba , sizeof ( ba ))) {
2002-09-12 18:32:42 +00:00
ast_log ( LOG_WARNING , "Unable to bind socket: %s \n " , strerror ( errno ));
close ( asock );
asock = - 1 ;
return - 1 ;
}
if ( listen ( asock , 2 )) {
ast_log ( LOG_WARNING , "Unable to listen on socket: %s \n " , strerror ( errno ));
close ( asock );
asock = - 1 ;
return - 1 ;
}
2006-04-01 08:49:54 +00:00
flags = fcntl ( asock , F_GETFL );
fcntl ( asock , F_SETFL , flags | O_NONBLOCK );
2002-09-12 18:32:42 +00:00
if ( option_verbose )
ast_verbose ( "Asterisk Management interface listening on port %d \n " , portno );
2004-08-08 17:15:02 +00:00
ast_pthread_create ( & t , NULL , accept_thread , NULL );
2002-09-12 18:32:42 +00:00
}
return 0 ;
}
int reload_manager ( void )
{
2003-03-12 06:00:18 +00:00
manager_event ( EVENT_FLAG_SYSTEM , "Reload" , "Message: Reload Requested \r\n " );
2002-09-12 18:32:42 +00:00
return init_manager ();
}