1999-12-19 22:38:55 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
1999-12-19 22:38:55 +00:00
*
2005-01-21 07:06:25 +00:00
* Copyright ( C ) 1999 - 2005 , Digium , Inc .
1999-12-19 22:38:55 +00:00
*
2004-09-03 03:44:35 +00:00
* Mark Spencer < markster @ digium . com >
1999-12-19 22:38:55 +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 .
*
1999-12-19 22:38:55 +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 .
*/
/*
*
* Standard Command Line Interface
*
1999-12-19 22:38:55 +00:00
*/
# include <unistd.h>
# include <stdlib.h>
2005-04-22 13:11:34 +00:00
# include <sys/signal.h>
# include <stdio.h>
# include <signal.h>
# include <string.h>
# include <ctype.h>
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/logger.h"
# include "asterisk/options.h"
# include "asterisk/cli.h"
# include "asterisk/module.h"
# include "asterisk/pbx.h"
# include "asterisk/channel.h"
# include "asterisk/manager.h"
# include "asterisk/utils.h"
# include "asterisk/lock.h"
1999-12-19 22:38:55 +00:00
/* For rl_filename_completion */
2003-01-30 15:03:20 +00:00
# include "editline/readline/readline.h"
1999-12-19 22:38:55 +00:00
/* For module directory */
2005-06-06 03:04:58 +00:00
# include "asterisk/version.h"
# include "asterisk/build.h"
2001-05-09 03:11:22 +00:00
# define VERSION_INFO "Asterisk " ASTERISK_VERSION " built by " BUILD_USER "@" BUILD_HOSTNAME \
2005-06-06 03:04:58 +00:00
" on a " BUILD_MACHINE " running " BUILD_OS " on " BUILD_DATE
2001-05-09 03:11:22 +00:00
2004-11-06 21:33:01 +00:00
extern unsigned long global_fin , global_fout ;
1999-12-19 22:38:55 +00:00
void ast_cli ( int fd , char * fmt , . . . )
{
2003-10-22 03:52:56 +00:00
char * stuff ;
2004-07-14 07:44:19 +00:00
int res = 0 ;
1999-12-19 22:38:55 +00:00
va_list ap ;
2005-07-15 22:06:15 +00:00
1999-12-19 22:38:55 +00:00
va_start ( ap , fmt ) ;
2004-07-14 07:44:19 +00:00
res = vasprintf ( & stuff , fmt , ap ) ;
1999-12-19 22:38:55 +00:00
va_end ( ap ) ;
2004-07-14 07:44:19 +00:00
if ( res = = - 1 ) {
ast_log ( LOG_ERROR , " Out of memory \n " ) ;
2004-07-14 14:53:24 +00:00
} else {
2004-07-14 07:44:19 +00:00
ast_carefulwrite ( fd , stuff , strlen ( stuff ) , 100 ) ;
free ( stuff ) ;
}
1999-12-19 22:38:55 +00:00
}
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( clilock ) ;
1999-12-19 22:38:55 +00:00
struct ast_cli_entry * helpers = NULL ;
static char load_help [ ] =
" Usage: load <module name> \n "
" Loads the specified module into Asterisk. \n " ;
static char unload_help [ ] =
" Usage: unload [-f|-h] <module name> \n "
2005-08-08 01:45:29 +00:00
" Unloads the specified module from Asterisk. The -f \n "
1999-12-19 22:38:55 +00:00
" option causes the module to be unloaded even if it is \n "
" in use (may cause a crash) and the -h module causes the \n "
" module to be unloaded even if the module says it cannot, \n "
" which almost always will cause a crash. \n " ;
static char help_help [ ] =
" Usage: help [topic] \n "
" When called with a topic as an argument, displays usage \n "
2005-08-08 01:45:29 +00:00
" information on the given command. If called without a \n "
1999-12-19 22:38:55 +00:00
" topic, it provides a list of commands. \n " ;
static char chanlist_help [ ] =
2005-08-08 01:45:29 +00:00
" Usage: show channels [concise|verbose] \n "
" Lists currently defined channels and some information about them. If \n "
" 'concise' is specified, the format is abridged and in a more easily \n "
" machine parsable format. If 'verbose' is specified, the output includes \n "
" more and longer fields. \n " ;
1999-12-19 22:38:55 +00:00
2001-05-09 03:11:22 +00:00
static char reload_help [ ] =
2004-09-03 14:02:12 +00:00
" Usage: reload [module ...] \n "
" Reloads configuration files for all listed modules which support \n "
" reloading, or for all supported modules if none are listed. \n " ;
2001-05-09 03:11:22 +00:00
static char set_verbose_help [ ] =
" Usage: set verbose <level> \n "
" Sets level of verbose messages to be displayed. 0 means \n "
2004-09-13 18:19:15 +00:00
" no messages should be displayed. Equivalent to -v[v[v...]] \n "
" on startup \n " ;
static char set_debug_help [ ] =
" Usage: set debug <level> \n "
" Sets level of core debug messages to be displayed. 0 means \n "
" no messages should be displayed. Equivalent to -d[d[d...]] \n "
" on startup. \n " ;
2001-05-09 03:11:22 +00:00
static char softhangup_help [ ] =
" Usage: soft hangup <channel> \n "
2005-08-08 01:45:29 +00:00
" Request that a channel be hung up. The hangup takes effect \n "
2001-05-09 03:11:22 +00:00
" the next time the driver reads or writes from the channel \n " ;
1999-12-19 22:38:55 +00:00
static int handle_load ( int fd , int argc , char * argv [ ] )
{
if ( argc ! = 2 )
return RESULT_SHOWUSAGE ;
if ( ast_load_resource ( argv [ 1 ] ) ) {
ast_cli ( fd , " Unable to load module %s \n " , argv [ 1 ] ) ;
return RESULT_FAILURE ;
}
return RESULT_SUCCESS ;
}
2001-05-09 03:11:22 +00:00
static int handle_reload ( int fd , int argc , char * argv [ ] )
{
2004-09-02 20:45:24 +00:00
int x ;
2004-10-16 21:14:05 +00:00
int res ;
2004-09-02 20:45:24 +00:00
if ( argc < 1 )
2001-05-09 03:11:22 +00:00
return RESULT_SHOWUSAGE ;
2004-09-02 20:45:24 +00:00
if ( argc > 1 ) {
2004-10-16 21:14:05 +00:00
for ( x = 1 ; x < argc ; x + + ) {
res = ast_module_reload ( argv [ x ] ) ;
switch ( res ) {
case 0 :
ast_cli ( fd , " No such module '%s' \n " , argv [ x ] ) ;
break ;
case 1 :
ast_cli ( fd , " Module '%s' does not support reload \n " , argv [ x ] ) ;
break ;
}
}
2004-09-02 20:45:24 +00:00
} else
ast_module_reload ( NULL ) ;
2001-05-09 03:11:22 +00:00
return RESULT_SUCCESS ;
}
static int handle_set_verbose ( int fd , int argc , char * argv [ ] )
{
2004-09-13 18:19:15 +00:00
int val = 0 ;
int oldval = 0 ;
2004-12-29 07:46:10 +00:00
2001-05-09 03:11:22 +00:00
/* Has a hidden 'at least' argument */
if ( ( argc ! = 3 ) & & ( argc ! = 4 ) )
return RESULT_SHOWUSAGE ;
if ( ( argc = = 4 ) & & strcasecmp ( argv [ 2 ] , " atleast " ) )
return RESULT_SHOWUSAGE ;
2004-09-13 18:19:15 +00:00
oldval = option_verbose ;
2001-05-09 03:11:22 +00:00
if ( argc = = 3 )
option_verbose = atoi ( argv [ 2 ] ) ;
else {
val = atoi ( argv [ 3 ] ) ;
if ( val > option_verbose )
option_verbose = val ;
}
2004-09-13 18:19:15 +00:00
if ( oldval ! = option_verbose & & option_verbose > 0 )
ast_cli ( fd , " Verbosity was %d and is now %d \n " , oldval , option_verbose ) ;
else if ( oldval > 0 & & option_verbose > 0 )
2004-12-29 07:46:10 +00:00
ast_cli ( fd , " Verbosity is at least %d \n " , option_verbose ) ;
2004-10-08 04:23:22 +00:00
else if ( oldval > 0 & & option_verbose = = 0 )
2004-09-13 18:19:15 +00:00
ast_cli ( fd , " Verbosity is now OFF \n " ) ;
return RESULT_SUCCESS ;
}
static int handle_set_debug ( int fd , int argc , char * argv [ ] )
{
int val = 0 ;
int oldval = 0 ;
/* Has a hidden 'at least' argument */
if ( ( argc ! = 3 ) & & ( argc ! = 4 ) )
return RESULT_SHOWUSAGE ;
if ( ( argc = = 4 ) & & strcasecmp ( argv [ 2 ] , " atleast " ) )
return RESULT_SHOWUSAGE ;
oldval = option_debug ;
if ( argc = = 3 )
option_debug = atoi ( argv [ 2 ] ) ;
else {
val = atoi ( argv [ 3 ] ) ;
if ( val > option_debug )
option_debug = val ;
}
if ( oldval ! = option_debug & & option_debug > 0 )
ast_cli ( fd , " Core debug was %d and is now %d \n " , oldval , option_debug ) ;
else if ( oldval > 0 & & option_debug > 0 )
2004-12-29 07:46:10 +00:00
ast_cli ( fd , " Core debug is at least %d \n " , option_debug ) ;
2004-09-13 18:19:15 +00:00
else if ( oldval > 0 & & option_debug = = 0 )
ast_cli ( fd , " Core debug is now OFF \n " ) ;
2001-05-09 03:11:22 +00:00
return RESULT_SUCCESS ;
}
1999-12-19 22:38:55 +00:00
static int handle_unload ( int fd , int argc , char * argv [ ] )
{
int x ;
int force = AST_FORCE_SOFT ;
if ( argc < 2 )
return RESULT_SHOWUSAGE ;
for ( x = 1 ; x < argc ; x + + ) {
if ( argv [ x ] [ 0 ] = = ' - ' ) {
switch ( argv [ x ] [ 1 ] ) {
case ' f ' :
force = AST_FORCE_FIRM ;
break ;
case ' h ' :
force = AST_FORCE_HARD ;
break ;
default :
return RESULT_SHOWUSAGE ;
}
} else if ( x ! = argc - 1 )
return RESULT_SHOWUSAGE ;
else if ( ast_unload_resource ( argv [ x ] , force ) ) {
ast_cli ( fd , " Unable to unload resource %s \n " , argv [ x ] ) ;
return RESULT_FAILURE ;
}
}
return RESULT_SUCCESS ;
}
2005-06-06 20:27:51 +00:00
# define MODLIST_FORMAT "%-30s %-40.40s %-10d\n"
# define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
1999-12-19 22:38:55 +00:00
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( climodentrylock ) ;
1999-12-19 22:38:55 +00:00
static int climodentryfd = - 1 ;
2005-06-06 20:27:51 +00:00
static int modlist_modentry ( const char * module , const char * description , int usecnt , const char * like )
1999-12-19 22:38:55 +00:00
{
2004-10-14 04:38:29 +00:00
/* Comparing the like with the module */
2005-08-22 18:43:32 +00:00
if ( strcasestr ( module , like ) ) {
2005-06-06 20:27:51 +00:00
ast_cli ( climodentryfd , MODLIST_FORMAT , module , description , usecnt ) ;
2004-10-14 04:38:29 +00:00
return 1 ;
}
1999-12-19 22:38:55 +00:00
return 0 ;
}
static char modlist_help [ ] =
2004-10-14 04:38:29 +00:00
" Usage: show modules [like keyword] \n "
" Shows Asterisk modules currently in use, and usage statistics. \n " ;
1999-12-19 22:38:55 +00:00
2001-05-09 03:11:22 +00:00
static char version_help [ ] =
" Usage: show version \n "
2005-02-05 16:49:14 +00:00
" Shows Asterisk version information. \n " ;
static char uptime_help [ ] =
" Usage: show uptime [seconds] \n "
" Shows Asterisk uptime information. \n "
" The seconds word returns the uptime in seconds only. \n " ;
2001-05-09 03:11:22 +00:00
2003-05-02 15:37:34 +00:00
static char * format_uptimestr ( time_t timeval )
{
int years = 0 , weeks = 0 , days = 0 , hours = 0 , mins = 0 , secs = 0 ;
2004-07-14 07:44:19 +00:00
char timestr [ 256 ] = " " ;
int bytes = 0 ;
int maxbytes = 0 ;
int offset = 0 ;
2003-05-02 15:37:34 +00:00
# define SECOND (1)
2004-06-22 17:42:14 +00:00
# define MINUTE (SECOND*60)
# define HOUR (MINUTE*60)
2003-05-02 15:37:34 +00:00
# define DAY (HOUR*24)
# define WEEK (DAY*7)
# define YEAR (DAY*365)
2004-07-14 07:44:19 +00:00
# define ESS(x) ((x == 1) ? "" : "s")
2003-05-02 15:37:34 +00:00
2004-07-14 07:44:19 +00:00
maxbytes = sizeof ( timestr ) ;
2004-03-14 09:11:32 +00:00
if ( timeval < 0 )
return NULL ;
2003-05-02 15:37:34 +00:00
if ( timeval > YEAR ) {
years = ( timeval / YEAR ) ;
timeval - = ( years * YEAR ) ;
2004-07-14 07:44:19 +00:00
if ( years > 0 ) {
snprintf ( timestr + offset , maxbytes , " %d year%s, " , years , ESS ( years ) ) ;
bytes = strlen ( timestr + offset ) ;
offset + = bytes ;
maxbytes - = bytes ;
}
2003-05-02 15:37:34 +00:00
}
if ( timeval > WEEK ) {
weeks = ( timeval / WEEK ) ;
timeval - = ( weeks * WEEK ) ;
2004-07-14 07:44:19 +00:00
if ( weeks > 0 ) {
snprintf ( timestr + offset , maxbytes , " %d week%s, " , weeks , ESS ( weeks ) ) ;
bytes = strlen ( timestr + offset ) ;
offset + = bytes ;
maxbytes - = bytes ;
}
2003-05-02 15:37:34 +00:00
}
if ( timeval > DAY ) {
days = ( timeval / DAY ) ;
timeval - = ( days * DAY ) ;
2004-07-14 07:44:19 +00:00
if ( days > 0 ) {
snprintf ( timestr + offset , maxbytes , " %d day%s, " , days , ESS ( days ) ) ;
bytes = strlen ( timestr + offset ) ;
offset + = bytes ;
maxbytes - = bytes ;
}
2003-05-02 15:37:34 +00:00
}
if ( timeval > HOUR ) {
hours = ( timeval / HOUR ) ;
timeval - = ( hours * HOUR ) ;
2004-07-14 07:44:19 +00:00
if ( hours > 0 ) {
snprintf ( timestr + offset , maxbytes , " %d hour%s, " , hours , ESS ( hours ) ) ;
bytes = strlen ( timestr + offset ) ;
offset + = bytes ;
maxbytes - = bytes ;
}
2003-05-02 15:37:34 +00:00
}
2004-06-22 17:42:14 +00:00
if ( timeval > MINUTE ) {
mins = ( timeval / MINUTE ) ;
timeval - = ( mins * MINUTE ) ;
2004-07-14 07:44:19 +00:00
if ( mins > 0 ) {
snprintf ( timestr + offset , maxbytes , " %d minute%s, " , mins , ESS ( mins ) ) ;
bytes = strlen ( timestr + offset ) ;
offset + = bytes ;
maxbytes - = bytes ;
}
2003-05-02 15:37:34 +00:00
}
secs = timeval ;
2004-07-14 07:44:19 +00:00
if ( secs > 0 ) {
snprintf ( timestr + offset , maxbytes , " %d second%s " , secs , ESS ( secs ) ) ;
}
2003-05-02 15:37:34 +00:00
return timestr ? strdup ( timestr ) : NULL ;
}
static int handle_showuptime ( int fd , int argc , char * argv [ ] )
{
time_t curtime , tmptime ;
char * timestr ;
2005-02-05 16:49:14 +00:00
int printsec ;
printsec = ( ( argc = = 3 ) & & ( ! strcasecmp ( argv [ 2 ] , " seconds " ) ) ) ;
if ( ( argc ! = 2 ) & & ( ! printsec ) )
return RESULT_SHOWUSAGE ;
2003-05-02 15:37:34 +00:00
time ( & curtime ) ;
if ( ast_startuptime ) {
tmptime = curtime - ast_startuptime ;
2005-02-05 16:49:14 +00:00
if ( printsec ) {
ast_cli ( fd , " System uptime: %lu \n " , tmptime ) ;
} else {
timestr = format_uptimestr ( tmptime ) ;
if ( timestr ) {
ast_cli ( fd , " System uptime: %s \n " , timestr ) ;
free ( timestr ) ;
}
2003-05-02 15:37:34 +00:00
}
}
if ( ast_lastreloadtime ) {
tmptime = curtime - ast_lastreloadtime ;
2005-02-05 16:49:14 +00:00
if ( printsec ) {
ast_cli ( fd , " Last reload: %lu \n " , tmptime ) ;
} else {
timestr = format_uptimestr ( tmptime ) ;
if ( ( timestr ) & & ( ! printsec ) ) {
ast_cli ( fd , " Last reload: %s \n " , timestr ) ;
free ( timestr ) ;
}
2003-05-02 15:37:34 +00:00
}
}
return RESULT_SUCCESS ;
}
1999-12-19 22:38:55 +00:00
static int handle_modlist ( int fd , int argc , char * argv [ ] )
{
2004-10-14 04:38:29 +00:00
char * like = " " ;
if ( argc = = 3 )
1999-12-19 22:38:55 +00:00
return RESULT_SHOWUSAGE ;
2004-10-14 04:38:29 +00:00
else if ( argc > = 4 ) {
2005-03-23 05:56:32 +00:00
if ( strcmp ( argv [ 2 ] , " like " ) )
2004-10-14 04:38:29 +00:00
return RESULT_SHOWUSAGE ;
like = argv [ 3 ] ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & climodentrylock ) ;
1999-12-19 22:38:55 +00:00
climodentryfd = fd ;
2005-06-06 20:27:51 +00:00
ast_cli ( fd , MODLIST_FORMAT2 , " Module " , " Description " , " Use Count " ) ;
2005-06-06 18:31:29 +00:00
ast_cli ( fd , " %d modules loaded \n " , ast_update_module_list ( modlist_modentry , like ) ) ;
1999-12-19 22:38:55 +00:00
climodentryfd = - 1 ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & climodentrylock ) ;
1999-12-19 22:38:55 +00:00
return RESULT_SUCCESS ;
}
2005-08-08 01:45:29 +00:00
# undef MODLIST_FORMAT
# undef MODLIST_FORMAT2
1999-12-19 22:38:55 +00:00
2001-05-09 03:11:22 +00:00
static int handle_version ( int fd , int argc , char * argv [ ] )
{
if ( argc ! = 2 )
return RESULT_SHOWUSAGE ;
2001-10-11 18:51:39 +00:00
ast_cli ( fd , " %s \n " , VERSION_INFO ) ;
2001-05-09 03:11:22 +00:00
return RESULT_SUCCESS ;
}
1999-12-19 22:38:55 +00:00
static int handle_chanlist ( int fd , int argc , char * argv [ ] )
{
2005-08-08 01:45:29 +00:00
# define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
# define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
# define CONCISE_FORMAT_STRING "%s:%s:%s:%d:%s:%s:%s:%s:%s:%d:%s:%s\n"
# define VERBOSE_FORMAT_STRING "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
# define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
struct ast_channel * c = NULL , * bc = NULL ;
char durbuf [ 10 ] = " - " ;
char locbuf [ 40 ] ;
char appdata [ 40 ] ;
int duration ;
int durh , durm , durs ;
int numchans = 0 , concise = 0 , verbose = 0 ;
2004-06-11 00:18:30 +00:00
concise = ( argc = = 3 & & ( ! strcasecmp ( argv [ 2 ] , " concise " ) ) ) ;
2005-08-08 01:45:29 +00:00
verbose = ( argc = = 3 & & ( ! strcasecmp ( argv [ 2 ] , " verbose " ) ) ) ;
2004-06-11 00:18:30 +00:00
2005-08-08 01:45:29 +00:00
if ( argc < 2 | | argc > 3 | | ( argc = = 3 & & ! concise & & ! verbose ) )
return RESULT_SHOWUSAGE ;
if ( ! concise & & ! verbose )
ast_cli ( fd , FORMAT_STRING2 , " Channel " , " Location " , " State " , " Application(Data) " ) ;
else if ( verbose )
ast_cli ( fd , VERBOSE_FORMAT_STRING2 , " Channel " , " Context " , " Extension " , " Priority " , " State " , " Application " , " Data " ,
" CallerID " , " Duration " , " Accountcode " , " BridgedTo " ) ;
while ( ( c = ast_channel_walk_locked ( c ) ) ! = NULL ) {
bc = ast_bridged_channel ( c ) ;
if ( ( concise | | verbose ) & & c - > cdr & & ! ast_tvzero ( c - > cdr - > start ) ) {
duration = ( int ) ( ast_tvdiff_ms ( ast_tvnow ( ) , c - > cdr - > start ) / 1000 ) ;
if ( verbose ) {
durh = duration / 3600 ;
durm = ( duration % 3600 ) / 60 ;
durs = duration % 60 ;
snprintf ( durbuf , sizeof ( durbuf ) , " %02d:%02d:%02d " , durh , durm , durs ) ;
} else {
snprintf ( durbuf , sizeof ( durbuf ) , " %d " , duration ) ;
}
} else {
durbuf [ 0 ] = ' \0 ' ;
}
if ( concise ) {
ast_cli ( fd , CONCISE_FORMAT_STRING , c - > name , c - > context , c - > exten , c - > priority , ast_state2str ( c - > _state ) ,
c - > appl ? c - > appl : " (None) " , c - > data ? ( ! ast_strlen_zero ( c - > data ) ? c - > data : " " ) : " " ,
( c - > cid . cid_num & & ! ast_strlen_zero ( c - > cid . cid_num ) ) ? c - > cid . cid_num : " " ,
( c - > accountcode & & ! ast_strlen_zero ( c - > accountcode ) ) ? c - > accountcode : " " , c - > amaflags ,
durbuf , bc ? bc - > name : " (None) " ) ;
} else if ( verbose ) {
ast_cli ( fd , VERBOSE_FORMAT_STRING , c - > name , c - > context , c - > exten , c - > priority , ast_state2str ( c - > _state ) ,
c - > appl ? c - > appl : " (None) " , c - > data ? ( ! ast_strlen_zero ( c - > data ) ? c - > data : " (Empty) " ) : " (None) " ,
( c - > cid . cid_num & & ! ast_strlen_zero ( c - > cid . cid_num ) ) ? c - > cid . cid_num : " " , durbuf ,
( c - > accountcode & & ! ast_strlen_zero ( c - > accountcode ) ) ? c - > accountcode : " " , bc ? bc - > name : " (None) " ) ;
} else {
if ( ! ast_strlen_zero ( c - > context ) & & ! ast_strlen_zero ( c - > exten ) )
snprintf ( locbuf , sizeof ( locbuf ) , " %s@%s:%d " , c - > exten , c - > context , c - > priority ) ;
else
strcpy ( locbuf , " (None) " ) ;
if ( c - > appl ) {
snprintf ( appdata , sizeof ( appdata ) , " %s(%s) " , c - > appl , c - > data ? c - > data : " " ) ;
} else {
strcpy ( appdata , " (None) " ) ;
}
ast_cli ( fd , FORMAT_STRING , c - > name , locbuf , ast_state2str ( c - > _state ) , appdata ) ;
}
2003-02-12 13:59:15 +00:00
numchans + + ;
2004-05-20 16:30:10 +00:00
ast_mutex_unlock ( & c - > lock ) ;
1999-12-19 22:38:55 +00:00
}
2005-08-08 01:45:29 +00:00
if ( ! concise ) {
2005-07-07 22:32:20 +00:00
ast_cli ( fd , " %d active channel%s \n " , numchans , ( numchans ! = 1 ) ? " s " : " " ) ;
2005-05-18 01:49:13 +00:00
if ( option_maxcalls )
2005-07-07 22:32:20 +00:00
ast_cli ( fd , " %d of %d max active call%s (%5.2f%% of capacity) \n " , ast_active_calls ( ) , option_maxcalls , ( ast_active_calls ( ) ! = 1 ) ? " s " : " " , ( ( float ) ast_active_calls ( ) / ( float ) option_maxcalls ) * 100.0 ) ;
2005-05-18 01:49:13 +00:00
else
2005-07-07 22:32:20 +00:00
ast_cli ( fd , " %d active call%s \n " , ast_active_calls ( ) , ( ast_active_calls ( ) ! = 1 ) ? " s " : " " ) ;
2005-05-18 01:49:13 +00:00
}
1999-12-19 22:38:55 +00:00
return RESULT_SUCCESS ;
2005-08-08 01:45:29 +00:00
# undef FORMAT_STRING
# undef FORMAT_STRING2
# undef CONCISE_FORMAT_STRING
# undef VERBOSE_FORMAT_STRING
# undef VERBOSE_FORMAT_STRING2
1999-12-19 22:38:55 +00:00
}
static char showchan_help [ ] =
" Usage: show channel <channel> \n "
" Shows lots of information about the specified channel. \n " ;
2003-03-12 06:00:18 +00:00
static char debugchan_help [ ] =
" Usage: debug channel <channel> \n "
" Enables debugging on a specific channel. \n " ;
2005-03-05 04:04:55 +00:00
static char debuglevel_help [ ] =
" Usage: debug level <level> [filename] \n "
" Set debug to specified level (0 to disable). If filename \n "
" is specified, debugging will be limited to just that file. \n " ;
2003-03-12 06:00:18 +00:00
static char nodebugchan_help [ ] =
" Usage: no debug channel <channel> \n "
" Disables debugging on a specific channel. \n " ;
2001-05-09 03:11:22 +00:00
static char commandcomplete_help [ ] =
" Usage: _command complete \" <line> \" text state \n "
" This function is used internally to help with command completion and should. \n "
" never be called by the user directly. \n " ;
2003-01-30 15:03:20 +00:00
static char commandnummatches_help [ ] =
" Usage: _command nummatches \" <line> \" text \n "
" This function is used internally to help with command completion and should. \n "
" never be called by the user directly. \n " ;
static char commandmatchesarray_help [ ] =
" Usage: _command matchesarray \" <line> \" text \n "
" This function is used internally to help with command completion and should. \n "
" never be called by the user directly. \n " ;
2001-05-09 03:11:22 +00:00
static int handle_softhangup ( int fd , int argc , char * argv [ ] )
{
struct ast_channel * c = NULL ;
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2005-06-06 02:29:18 +00:00
c = ast_get_channel_by_name_locked ( argv [ 2 ] ) ;
if ( c ) {
ast_cli ( fd , " Requested Hangup on channel '%s' \n " , c - > name ) ;
ast_softhangup ( c , AST_SOFTHANGUP_EXPLICIT ) ;
2004-05-20 16:30:10 +00:00
ast_mutex_unlock ( & c - > lock ) ;
2005-06-06 02:29:18 +00:00
} else
2001-05-09 03:11:22 +00:00
ast_cli ( fd , " %s is not a known channel \n " , argv [ 2 ] ) ;
return RESULT_SUCCESS ;
}
static char * __ast_cli_generator ( char * text , char * word , int state , int lock ) ;
2003-01-30 15:03:20 +00:00
static int handle_commandmatchesarray ( int fd , int argc , char * argv [ ] )
{
2004-10-14 04:12:05 +00:00
char * buf , * obuf ;
2004-04-06 07:42:01 +00:00
int buflen = 2048 ;
2003-01-30 15:03:20 +00:00
int len = 0 ;
char * * matches ;
2004-10-14 04:12:05 +00:00
int x , matchlen ;
2003-01-30 15:03:20 +00:00
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
2004-04-06 07:42:01 +00:00
buf = malloc ( buflen ) ;
if ( ! buf )
return RESULT_FAILURE ;
2003-01-30 15:03:20 +00:00
buf [ len ] = ' \0 ' ;
matches = ast_cli_completion_matches ( argv [ 2 ] , argv [ 3 ] ) ;
if ( matches ) {
for ( x = 0 ; matches [ x ] ; x + + ) {
#if 0
printf ( " command matchesarray for '%s' %s got '%s' \n " , argv [ 2 ] , argv [ 3 ] , matches [ x ] ) ;
# endif
2004-10-14 04:12:05 +00:00
matchlen = strlen ( matches [ x ] ) + 1 ;
if ( len + matchlen > = buflen ) {
buflen + = matchlen * 3 ;
obuf = buf ;
buf = realloc ( obuf , buflen ) ;
if ( ! buf )
/* Out of memory... Just free old buffer and be done */
free ( obuf ) ;
2004-04-06 07:42:01 +00:00
}
2004-10-14 04:12:05 +00:00
if ( buf )
len + = sprintf ( buf + len , " %s " , matches [ x ] ) ;
2003-04-08 13:45:36 +00:00
free ( matches [ x ] ) ;
matches [ x ] = NULL ;
2003-01-30 15:03:20 +00:00
}
2003-04-08 13:45:36 +00:00
free ( matches ) ;
2003-01-30 15:03:20 +00:00
}
#if 0
printf ( " array for '%s' %s got '%s' \n " , argv [ 2 ] , argv [ 3 ] , buf ) ;
# endif
if ( buf ) {
2004-04-06 07:42:01 +00:00
ast_cli ( fd , " %s%s " , buf , AST_CLI_COMPLETE_EOF ) ;
free ( buf ) ;
2003-01-30 15:03:20 +00:00
} else
ast_cli ( fd , " NULL \n " ) ;
return RESULT_SUCCESS ;
}
static int handle_commandnummatches ( int fd , int argc , char * argv [ ] )
{
int matches = 0 ;
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
matches = ast_cli_generatornummatches ( argv [ 2 ] , argv [ 3 ] ) ;
#if 0
printf ( " Search for '%s' %s got '%d' \n " , argv [ 2 ] , argv [ 3 ] , matches ) ;
# endif
ast_cli ( fd , " %d " , matches ) ;
return RESULT_SUCCESS ;
}
2001-05-09 03:11:22 +00:00
static int handle_commandcomplete ( int fd , int argc , char * argv [ ] )
{
char * buf ;
#if 0
printf ( " Search for %d args: '%s', '%s', '%s', '%s' \n " , argc , argv [ 0 ] , argv [ 1 ] , argv [ 2 ] , argv [ 3 ] ) ;
# endif
if ( argc ! = 5 )
return RESULT_SHOWUSAGE ;
buf = __ast_cli_generator ( argv [ 2 ] , argv [ 3 ] , atoi ( argv [ 4 ] ) , 0 ) ;
#if 0
printf ( " Search for '%s' %s %d got '%s' \n " , argv [ 2 ] , argv [ 3 ] , atoi ( argv [ 4 ] ) , buf ) ;
# endif
if ( buf ) {
ast_cli ( fd , buf ) ;
free ( buf ) ;
} else
ast_cli ( fd , " NULL \n " ) ;
return RESULT_SUCCESS ;
}
2005-03-05 04:04:55 +00:00
static int handle_debuglevel ( int fd , int argc , char * argv [ ] )
{
int newlevel ;
char * filename = " <any> " ;
if ( ( argc < 3 ) | | ( argc > 4 ) )
return RESULT_SHOWUSAGE ;
2005-04-29 17:00:33 +00:00
if ( sscanf ( argv [ 2 ] , " %d " , & newlevel ) ! = 1 )
2005-03-05 04:04:55 +00:00
return RESULT_SHOWUSAGE ;
option_debug = newlevel ;
if ( argc = = 4 ) {
filename = argv [ 3 ] ;
2005-07-10 22:56:21 +00:00
ast_copy_string ( debug_filename , filename , sizeof ( debug_filename ) ) ;
2005-03-05 04:04:55 +00:00
} else {
debug_filename [ 0 ] = ' \0 ' ;
}
ast_cli ( fd , " Debugging level set to %d, file '%s' \n " , newlevel , filename ) ;
return RESULT_SUCCESS ;
}
2005-06-06 02:29:18 +00:00
# define DEBUGCHAN_FLAG 0x80000000
/* XXX todo: merge next two functions!!! */
2003-03-12 06:00:18 +00:00
static int handle_debugchan ( int fd , int argc , char * argv [ ] )
{
struct ast_channel * c = NULL ;
2004-11-06 21:33:01 +00:00
int is_all ;
2003-03-12 06:00:18 +00:00
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2004-11-06 21:33:01 +00:00
is_all = ! strcasecmp ( " all " , argv [ 2 ] ) ;
if ( is_all ) {
2005-06-06 02:29:18 +00:00
global_fin | = DEBUGCHAN_FLAG ;
global_fout | = DEBUGCHAN_FLAG ;
c = ast_channel_walk_locked ( NULL ) ;
} else {
c = ast_get_channel_by_name_locked ( argv [ 2 ] ) ;
if ( c = = NULL )
ast_cli ( fd , " No such channel %s \n " , argv [ 2 ] ) ;
2004-11-06 21:33:01 +00:00
}
2003-03-12 06:00:18 +00:00
while ( c ) {
2005-06-06 02:29:18 +00:00
if ( ! ( c - > fin & DEBUGCHAN_FLAG ) | | ! ( c - > fout & DEBUGCHAN_FLAG ) ) {
c - > fin | = DEBUGCHAN_FLAG ;
c - > fout | = DEBUGCHAN_FLAG ;
ast_cli ( fd , " Debugging enabled on channel %s \n " , c - > name ) ;
2003-03-12 06:00:18 +00:00
}
2004-05-20 16:30:10 +00:00
ast_mutex_unlock ( & c - > lock ) ;
2005-06-06 02:29:18 +00:00
if ( ! is_all )
break ;
2004-05-20 16:30:10 +00:00
c = ast_channel_walk_locked ( c ) ;
2003-03-12 06:00:18 +00:00
}
2005-06-06 02:29:18 +00:00
ast_cli ( fd , " Debugging on new channels is enabled \n " ) ;
2003-03-12 06:00:18 +00:00
return RESULT_SUCCESS ;
}
static int handle_nodebugchan ( int fd , int argc , char * argv [ ] )
{
struct ast_channel * c = NULL ;
2004-11-06 21:33:01 +00:00
int is_all ;
2003-03-12 06:00:18 +00:00
if ( argc ! = 4 )
return RESULT_SHOWUSAGE ;
2004-11-06 21:33:01 +00:00
is_all = ! strcasecmp ( " all " , argv [ 3 ] ) ;
if ( is_all ) {
2005-06-06 02:29:18 +00:00
global_fin & = ~ DEBUGCHAN_FLAG ;
global_fout & = ~ DEBUGCHAN_FLAG ;
c = ast_channel_walk_locked ( NULL ) ;
} else {
c = ast_get_channel_by_name_locked ( argv [ 3 ] ) ;
if ( c = = NULL )
ast_cli ( fd , " No such channel %s \n " , argv [ 3 ] ) ;
}
2003-03-12 06:00:18 +00:00
while ( c ) {
2005-06-06 02:29:18 +00:00
if ( ( c - > fin & DEBUGCHAN_FLAG ) | | ( c - > fout & DEBUGCHAN_FLAG ) ) {
c - > fin & = ~ DEBUGCHAN_FLAG ;
c - > fout & = ~ DEBUGCHAN_FLAG ;
ast_cli ( fd , " Debugging disabled on channel %s \n " , c - > name ) ;
2003-03-12 06:00:18 +00:00
}
2004-05-20 16:30:10 +00:00
ast_mutex_unlock ( & c - > lock ) ;
2005-06-06 02:29:18 +00:00
if ( ! is_all )
break ;
2004-05-20 16:30:10 +00:00
c = ast_channel_walk_locked ( c ) ;
2003-03-12 06:00:18 +00:00
}
2005-06-06 02:29:18 +00:00
ast_cli ( fd , " Debugging on new channels is disabled \n " ) ;
2003-03-12 06:00:18 +00:00
return RESULT_SUCCESS ;
}
1999-12-19 22:38:55 +00:00
static int handle_showchan ( int fd , int argc , char * argv [ ] )
{
struct ast_channel * c = NULL ;
2004-08-03 17:48:18 +00:00
struct timeval now ;
2005-02-23 22:48:47 +00:00
char buf [ 2048 ] ;
2004-12-26 11:08:34 +00:00
char cdrtime [ 256 ] ;
2004-08-03 17:48:18 +00:00
long elapsed_seconds = 0 ;
2004-09-03 03:44:35 +00:00
int hour = 0 , min = 0 , sec = 0 ;
2005-02-23 22:48:47 +00:00
1999-12-19 22:38:55 +00:00
if ( argc ! = 3 )
return RESULT_SHOWUSAGE ;
2005-07-15 23:00:47 +00:00
now = ast_tvnow ( ) ;
2005-06-06 02:29:18 +00:00
c = ast_get_channel_by_name_locked ( argv [ 2 ] ) ;
if ( ! c ) {
1999-12-19 22:38:55 +00:00
ast_cli ( fd , " %s is not a known channel \n " , argv [ 2 ] ) ;
2005-06-06 02:29:18 +00:00
return RESULT_SUCCESS ;
}
if ( c - > cdr ) {
elapsed_seconds = now . tv_sec - c - > cdr - > start . tv_sec ;
hour = elapsed_seconds / 3600 ;
min = ( elapsed_seconds % 3600 ) / 60 ;
sec = elapsed_seconds % 60 ;
snprintf ( cdrtime , sizeof ( cdrtime ) , " %dh%dm%ds " , hour , min , sec ) ;
} else
2005-07-10 22:56:21 +00:00
strcpy ( cdrtime , " N/A " ) ;
2005-06-06 02:29:18 +00:00
ast_cli ( fd ,
" -- General -- \n "
" Name: %s \n "
" Type: %s \n "
" UniqueID: %s \n "
" Caller ID: %s \n "
" Caller ID Name: %s \n "
" DNID Digits: %s \n "
" State: %s (%d) \n "
" Rings: %d \n "
" NativeFormat: %d \n "
" WriteFormat: %d \n "
" ReadFormat: %d \n "
" 1st File Descriptor: %d \n "
" Frames in: %d%s \n "
" Frames out: %d%s \n "
" Time to Hangup: %ld \n "
" Elapsed Time: %s \n "
" Direct Bridge: %s \n "
" Indirect Bridge: %s \n "
" -- PBX -- \n "
" Context: %s \n "
" Extension: %s \n "
" Priority: %d \n "
" Call Group: %d \n "
" Pickup Group: %d \n "
" Application: %s \n "
" Data: %s \n "
" Blocking in: %s \n " ,
c - > name , c - > type , c - > uniqueid ,
( c - > cid . cid_num ? c - > cid . cid_num : " (N/A) " ) ,
( c - > cid . cid_name ? c - > cid . cid_name : " (N/A) " ) ,
( c - > cid . cid_dnid ? c - > cid . cid_dnid : " (N/A) " ) , ast_state2str ( c - > _state ) , c - > _state , c - > rings , c - > nativeformats , c - > writeformat , c - > readformat ,
c - > fds [ 0 ] , c - > fin & 0x7fffffff , ( c - > fin & 0x80000000 ) ? " (DEBUGGED) " : " " ,
c - > fout & 0x7fffffff , ( c - > fout & 0x80000000 ) ? " (DEBUGGED) " : " " , ( long ) c - > whentohangup ,
cdrtime , c - > _bridge ? c - > _bridge - > name : " <none> " , ast_bridged_channel ( c ) ? ast_bridged_channel ( c ) - > name : " <none> " ,
c - > context , c - > exten , c - > priority , c - > callgroup , c - > pickupgroup , ( c - > appl ? c - > appl : " (N/A) " ) ,
( c - > data ? ( ! ast_strlen_zero ( c - > data ) ? c - > data : " (Empty) " ) : " (None) " ) ,
( ast_test_flag ( c , AST_FLAG_BLOCKING ) ? c - > blockproc : " (Not Blocking) " ) ) ;
if ( pbx_builtin_serialize_variables ( c , buf , sizeof ( buf ) ) )
ast_cli ( fd , " Variables: \n %s \n " , buf ) ;
if ( c - > cdr & & ast_cdr_serialize_variables ( c - > cdr , buf , sizeof ( buf ) , ' = ' , ' \n ' , 1 ) )
ast_cli ( fd , " CDR Variables: \n %s \n " , buf ) ;
ast_mutex_unlock ( & c - > lock ) ;
1999-12-19 22:38:55 +00:00
return RESULT_SUCCESS ;
}
2005-08-08 01:45:29 +00:00
static char * complete_show_channels ( char * line , char * word , int pos , int state )
{
static char * choices [ ] = { " concise " , " verbose " } ;
int match = 0 ;
int x ;
if ( pos ! = 2 )
return NULL ;
for ( x = 0 ; x < sizeof ( choices ) / sizeof ( choices [ 0 ] ) ; x + + ) {
if ( ! strncasecmp ( word , choices [ x ] , strlen ( word ) ) ) {
match + + ;
if ( match > state ) return strdup ( choices [ x ] ) ;
}
}
return NULL ;
}
2004-08-07 19:27:54 +00:00
static char * complete_ch_helper ( char * line , char * word , int pos , int state , int rpos )
1999-12-19 22:38:55 +00:00
{
2005-06-06 02:29:18 +00:00
struct ast_channel * c = NULL ;
1999-12-19 22:38:55 +00:00
int which = 0 ;
2005-06-06 02:29:18 +00:00
char * ret = NULL ;
2004-08-07 19:27:54 +00:00
if ( pos ! = rpos )
return NULL ;
2005-06-06 02:29:18 +00:00
while ( ( c = ast_channel_walk_locked ( c ) ) ! = NULL ) {
2001-05-09 03:11:22 +00:00
if ( ! strncasecmp ( word , c - > name , strlen ( word ) ) ) {
2005-06-06 02:29:18 +00:00
if ( + + which > state ) {
ret = strdup ( c - > name ) ;
ast_mutex_unlock ( & c - > lock ) ;
2001-05-09 03:11:22 +00:00
break ;
2005-06-06 02:29:18 +00:00
}
2001-05-09 03:11:22 +00:00
}
2004-05-20 16:30:10 +00:00
ast_mutex_unlock ( & c - > lock ) ;
1999-12-19 22:38:55 +00:00
}
2004-05-20 16:30:10 +00:00
return ret ;
1999-12-19 22:38:55 +00:00
}
2004-08-07 19:27:54 +00:00
static char * complete_ch_3 ( char * line , char * word , int pos , int state )
{
return complete_ch_helper ( line , word , pos , state , 2 ) ;
}
static char * complete_ch_4 ( char * line , char * word , int pos , int state )
{
return complete_ch_helper ( line , word , pos , state , 3 ) ;
}
2004-10-16 21:14:05 +00:00
static char * complete_mod_2 ( char * line , char * word , int pos , int state )
{
return ast_module_helper ( line , word , pos , state , 1 , 1 ) ;
}
static char * complete_mod_4 ( char * line , char * word , int pos , int state )
{
return ast_module_helper ( line , word , pos , state , 3 , 0 ) ;
}
1999-12-19 22:38:55 +00:00
static char * complete_fn ( char * line , char * word , int pos , int state )
{
char * c ;
char filename [ 256 ] ;
if ( pos ! = 1 )
return NULL ;
if ( word [ 0 ] = = ' / ' )
2005-07-10 22:56:21 +00:00
ast_copy_string ( filename , word , sizeof ( filename ) ) ;
1999-12-19 22:38:55 +00:00
else
2003-01-30 15:03:20 +00:00
snprintf ( filename , sizeof ( filename ) , " %s/%s " , ( char * ) ast_config_AST_MODULE_DIR , word ) ;
2001-05-09 03:11:22 +00:00
c = ( char * ) filename_completion_function ( filename , state ) ;
1999-12-19 22:38:55 +00:00
if ( c & & word [ 0 ] ! = ' / ' )
2003-01-30 15:03:20 +00:00
c + = ( strlen ( ( char * ) ast_config_AST_MODULE_DIR ) + 1 ) ;
1999-12-19 22:38:55 +00:00
return c ? strdup ( c ) : c ;
}
static int handle_help ( int fd , int argc , char * argv [ ] ) ;
static struct ast_cli_entry builtins [ ] = {
2004-03-06 09:01:08 +00:00
/* Keep alphabetized, with longer matches first (example: abcd before abc) */
2001-05-09 03:11:22 +00:00
{ { " _command " , " complete " , NULL } , handle_commandcomplete , " Command complete " , commandcomplete_help } ,
2003-01-30 15:03:20 +00:00
{ { " _command " , " nummatches " , NULL } , handle_commandnummatches , " Returns number of command matches " , commandnummatches_help } ,
{ { " _command " , " matchesarray " , NULL } , handle_commandmatchesarray , " Returns command matches array " , commandmatchesarray_help } ,
2004-08-07 19:27:54 +00:00
{ { " debug " , " channel " , NULL } , handle_debugchan , " Enable debugging on a channel " , debugchan_help , complete_ch_3 } ,
2005-03-05 04:04:55 +00:00
{ { " debug " , " level " , NULL } , handle_debuglevel , " Set global debug level " , debuglevel_help } ,
2003-03-12 06:00:18 +00:00
{ { " help " , NULL } , handle_help , " Display help list, or specific help on a command " , help_help } ,
1999-12-19 22:38:55 +00:00
{ { " load " , NULL } , handle_load , " Load a dynamic module by name " , load_help , complete_fn } ,
2004-08-07 19:27:54 +00:00
{ { " no " , " debug " , " channel " , NULL } , handle_nodebugchan , " Disable debugging on a channel " , nodebugchan_help , complete_ch_4 } ,
2004-10-16 21:14:05 +00:00
{ { " reload " , NULL } , handle_reload , " Reload configuration " , reload_help , complete_mod_2 } ,
2004-09-13 18:19:15 +00:00
{ { " set " , " debug " , NULL } , handle_set_debug , " Set level of debug chattiness " , set_debug_help } ,
2001-05-09 03:11:22 +00:00
{ { " set " , " verbose " , NULL } , handle_set_verbose , " Set level of verboseness " , set_verbose_help } ,
2004-08-07 19:27:54 +00:00
{ { " show " , " channel " , NULL } , handle_showchan , " Display information on a specific channel " , showchan_help , complete_ch_3 } ,
2005-08-08 01:45:29 +00:00
{ { " show " , " channels " , NULL } , handle_chanlist , " Display information on channels " , chanlist_help , complete_show_channels } ,
2001-10-11 18:51:39 +00:00
{ { " show " , " modules " , NULL } , handle_modlist , " List modules and info " , modlist_help } ,
2004-10-16 21:14:05 +00:00
{ { " show " , " modules " , " like " , NULL } , handle_modlist , " List modules and info " , modlist_help , complete_mod_4 } ,
2005-02-05 16:49:14 +00:00
{ { " show " , " uptime " , NULL } , handle_showuptime , " Show uptime information " , uptime_help } ,
2001-10-11 18:51:39 +00:00
{ { " show " , " version " , NULL } , handle_version , " Display version info " , version_help } ,
2004-08-07 19:27:54 +00:00
{ { " soft " , " hangup " , NULL } , handle_softhangup , " Request a hangup on a given channel " , softhangup_help , complete_ch_3 } ,
1999-12-19 22:38:55 +00:00
{ { " unload " , NULL } , handle_unload , " Unload a dynamic module by name " , unload_help , complete_fn } ,
{ { NULL } , NULL , NULL , NULL }
} ;
static struct ast_cli_entry * find_cli ( char * cmds [ ] , int exact )
{
int x ;
int y ;
int match ;
struct ast_cli_entry * e = NULL ;
2005-06-06 20:27:51 +00:00
for ( e = helpers ; e ; e = e - > next ) {
match = 1 ;
for ( y = 0 ; match & & cmds [ y ] ; y + + ) {
if ( ! e - > cmda [ y ] & & ! exact )
break ;
if ( ! e - > cmda [ y ] | | strcasecmp ( e - > cmda [ y ] , cmds [ y ] ) )
match = 0 ;
}
if ( ( exact > - 1 ) & & e - > cmda [ y ] )
match = 0 ;
if ( match )
break ;
}
if ( e )
return e ;
1999-12-19 22:38:55 +00:00
for ( x = 0 ; builtins [ x ] . cmda [ 0 ] ; x + + ) {
/* start optimistic */
match = 1 ;
for ( y = 0 ; match & & cmds [ y ] ; y + + ) {
/* If there are no more words in the candidate command, then we're
there . */
if ( ! builtins [ x ] . cmda [ y ] & & ! exact )
break ;
/* If there are no more words in the command (and we're looking for
an exact match ) or there is a difference between the two words ,
then this is not a match */
if ( ! builtins [ x ] . cmda [ y ] | | strcasecmp ( builtins [ x ] . cmda [ y ] , cmds [ y ] ) )
match = 0 ;
}
/* If more words are needed to complete the command then this is not
a candidate ( unless we ' re looking for a really inexact answer */
if ( ( exact > - 1 ) & & builtins [ x ] . cmda [ y ] )
match = 0 ;
if ( match )
return & builtins [ x ] ;
}
2005-06-06 20:27:51 +00:00
return NULL ;
1999-12-19 22:38:55 +00:00
}
2005-09-25 03:13:00 +00:00
static void join ( char * dest , size_t destsize , char * w [ ] , int tws )
1999-12-19 22:38:55 +00:00
{
int x ;
/* Join words into a string */
2004-07-14 07:44:19 +00:00
if ( ! dest | | destsize < 1 ) {
return ;
}
dest [ 0 ] = ' \0 ' ;
1999-12-19 22:38:55 +00:00
for ( x = 0 ; w [ x ] ; x + + ) {
if ( x )
2004-07-14 07:44:19 +00:00
strncat ( dest , " " , destsize - strlen ( dest ) - 1 ) ;
strncat ( dest , w [ x ] , destsize - strlen ( dest ) - 1 ) ;
1999-12-19 22:38:55 +00:00
}
2005-09-25 03:13:00 +00:00
if ( tws )
strncat ( dest , " " , destsize - strlen ( dest ) - 1 ) ;
1999-12-19 22:38:55 +00:00
}
2004-07-14 07:44:19 +00:00
static void join2 ( char * dest , size_t destsize , char * w [ ] )
1999-12-27 19:20:20 +00:00
{
int x ;
/* Join words into a string */
2004-07-14 07:44:19 +00:00
if ( ! dest | | destsize < 1 ) {
return ;
}
dest [ 0 ] = ' \0 ' ;
1999-12-27 19:20:20 +00:00
for ( x = 0 ; w [ x ] ; x + + ) {
2004-07-14 07:44:19 +00:00
strncat ( dest , w [ x ] , destsize - strlen ( dest ) - 1 ) ;
1999-12-27 19:20:20 +00:00
}
}
1999-12-19 22:38:55 +00:00
static char * find_best ( char * argv [ ] )
{
static char cmdline [ 80 ] ;
int x ;
/* See how close we get, then print the */
char * myargv [ AST_MAX_CMD_LEN ] ;
for ( x = 0 ; x < AST_MAX_CMD_LEN ; x + + )
myargv [ x ] = NULL ;
for ( x = 0 ; argv [ x ] ; x + + ) {
myargv [ x ] = argv [ x ] ;
if ( ! find_cli ( myargv , - 1 ) )
break ;
}
2005-09-25 03:13:00 +00:00
join ( cmdline , sizeof ( cmdline ) , myargv , 0 ) ;
1999-12-19 22:38:55 +00:00
return cmdline ;
}
1999-12-27 19:20:20 +00:00
int ast_cli_unregister ( struct ast_cli_entry * e )
{
struct ast_cli_entry * cur , * l = NULL ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & clilock ) ;
1999-12-27 19:20:20 +00:00
cur = helpers ;
while ( cur ) {
if ( e = = cur ) {
2002-05-13 22:29:39 +00:00
if ( e - > inuse ) {
ast_log ( LOG_WARNING , " Can't remove command that is in use \n " ) ;
} else {
/* Rewrite */
if ( l )
l - > next = e - > next ;
else
helpers = e - > next ;
e - > next = NULL ;
break ;
}
1999-12-27 19:20:20 +00:00
}
l = cur ;
cur = cur - > next ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & clilock ) ;
1999-12-27 19:20:20 +00:00
return 0 ;
}
1999-12-19 22:38:55 +00:00
int ast_cli_register ( struct ast_cli_entry * e )
{
struct ast_cli_entry * cur , * l = NULL ;
2002-07-29 15:38:22 +00:00
char fulle [ 80 ] = " " , fulltst [ 80 ] = " " ;
1999-12-27 19:20:20 +00:00
static int len ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & clilock ) ;
1999-12-27 19:20:20 +00:00
join2 ( fulle , sizeof ( fulle ) , e - > cmda ) ;
1999-12-19 22:38:55 +00:00
if ( find_cli ( e - > cmda , - 1 ) ) {
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & clilock ) ;
2001-10-11 18:51:39 +00:00
ast_log ( LOG_WARNING , " Command '%s' already registered (or something close enough) \n " , fulle ) ;
1999-12-19 22:38:55 +00:00
return - 1 ;
}
cur = helpers ;
while ( cur ) {
1999-12-27 19:20:20 +00:00
join2 ( fulltst , sizeof ( fulltst ) , cur - > cmda ) ;
len = strlen ( fulltst ) ;
if ( strlen ( fulle ) < len )
len = strlen ( fulle ) ;
if ( strncasecmp ( fulle , fulltst , len ) < 0 ) {
if ( l ) {
e - > next = l - > next ;
l - > next = e ;
} else {
e - > next = helpers ;
helpers = e ;
}
1999-12-19 22:38:55 +00:00
break ;
}
l = cur ;
cur = cur - > next ;
}
if ( ! cur ) {
if ( l )
l - > next = e ;
else
helpers = e ;
e - > next = NULL ;
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & clilock ) ;
1999-12-19 22:38:55 +00:00
return 0 ;
}
2005-05-15 03:03:48 +00:00
/*
* register / unregister an array of entries .
*/
void ast_cli_register_multiple ( struct ast_cli_entry * e , int len )
{
int i ;
for ( i = 0 ; i < len ; i + + )
ast_cli_register ( e + i ) ;
}
void ast_cli_unregister_multiple ( struct ast_cli_entry * e , int len )
{
int i ;
for ( i = 0 ; i < len ; i + + )
ast_cli_unregister ( e + i ) ;
}
1999-12-19 22:38:55 +00:00
static int help_workhorse ( int fd , char * match [ ] )
{
2004-11-17 19:28:08 +00:00
char fullcmd1 [ 80 ] = " " ;
char fullcmd2 [ 80 ] = " " ;
1999-12-19 22:38:55 +00:00
char matchstr [ 80 ] ;
2004-11-17 19:28:08 +00:00
char * fullcmd = NULL ;
1999-12-19 22:38:55 +00:00
struct ast_cli_entry * e , * e1 , * e2 ;
e1 = builtins ;
e2 = helpers ;
if ( match )
2005-09-25 03:13:00 +00:00
join ( matchstr , sizeof ( matchstr ) , match , 0 ) ;
1999-12-19 22:38:55 +00:00
while ( e1 - > cmda [ 0 ] | | e2 ) {
if ( e2 )
2005-09-25 03:13:00 +00:00
join ( fullcmd2 , sizeof ( fullcmd2 ) , e2 - > cmda , 0 ) ;
1999-12-19 22:38:55 +00:00
if ( e1 - > cmda [ 0 ] )
2005-09-25 03:13:00 +00:00
join ( fullcmd1 , sizeof ( fullcmd1 ) , e1 - > cmda , 0 ) ;
1999-12-27 19:20:20 +00:00
if ( ! e1 - > cmda [ 0 ] | |
1999-12-19 22:38:55 +00:00
( e2 & & ( strcmp ( fullcmd2 , fullcmd1 ) < 0 ) ) ) {
/* Use e2 */
e = e2 ;
fullcmd = fullcmd2 ;
/* Increment by going to next */
e2 = e2 - > next ;
} else {
/* Use e1 */
e = e1 ;
fullcmd = fullcmd1 ;
e1 + + ;
}
2001-05-09 03:11:22 +00:00
/* Hide commands that start with '_' */
if ( fullcmd [ 0 ] = = ' _ ' )
continue ;
1999-12-19 22:38:55 +00:00
if ( match ) {
if ( strncasecmp ( matchstr , fullcmd , strlen ( matchstr ) ) ) {
continue ;
}
}
2004-05-10 18:45:20 +00:00
ast_cli ( fd , " %25.25s %s \n " , fullcmd , e - > summary ) ;
1999-12-19 22:38:55 +00:00
}
return 0 ;
}
static int handle_help ( int fd , int argc , char * argv [ ] ) {
struct ast_cli_entry * e ;
char fullcmd [ 80 ] ;
if ( ( argc < 1 ) )
return RESULT_SHOWUSAGE ;
if ( argc > 1 ) {
e = find_cli ( argv + 1 , 1 ) ;
2005-05-24 10:23:51 +00:00
if ( e ) {
if ( e - > usage )
2005-08-12 18:29:41 +00:00
ast_cli ( fd , " %s " , e - > usage ) ;
2005-05-24 10:23:51 +00:00
else {
2005-09-25 03:13:00 +00:00
join ( fullcmd , sizeof ( fullcmd ) , argv + 1 , 0 ) ;
2005-05-24 10:23:51 +00:00
ast_cli ( fd , " No help text available for '%s'. \n " , fullcmd ) ;
}
} else {
1999-12-19 22:38:55 +00:00
if ( find_cli ( argv + 1 , - 1 ) ) {
return help_workhorse ( fd , argv + 1 ) ;
} else {
2005-09-25 03:13:00 +00:00
join ( fullcmd , sizeof ( fullcmd ) , argv + 1 , 0 ) ;
1999-12-19 22:38:55 +00:00
ast_cli ( fd , " No such command '%s'. \n " , fullcmd ) ;
}
}
} else {
return help_workhorse ( fd , NULL ) ;
}
return RESULT_SUCCESS ;
}
2005-09-25 03:13:00 +00:00
static char * parse_args ( char * s , int * argc , char * argv [ ] , int max , int * trailingwhitespace )
1999-12-19 22:38:55 +00:00
{
char * dup , * cur ;
2005-06-10 20:25:23 +00:00
int x = 0 ;
int quoted = 0 ;
int escaped = 0 ;
int whitespace = 1 ;
2005-09-25 03:13:00 +00:00
* trailingwhitespace = 0 ;
2005-06-10 20:25:23 +00:00
if ( ! ( dup = strdup ( s ) ) )
return NULL ;
cur = dup ;
while ( * s ) {
if ( ( * s = = ' " ' ) & & ! escaped ) {
quoted = ! quoted ;
if ( quoted & whitespace ) {
/* If we're starting a quoted string, coming off white space, start a new argument */
if ( x > = ( max - 1 ) ) {
ast_log ( LOG_WARNING , " Too many arguments, truncating \n " ) ;
break ;
1999-12-19 22:38:55 +00:00
}
2005-06-10 20:25:23 +00:00
argv [ x + + ] = cur ;
whitespace = 0 ;
}
escaped = 0 ;
} else if ( ( ( * s = = ' ' ) | | ( * s = = ' \t ' ) ) & & ! ( quoted | | escaped ) ) {
/* If we are not already in whitespace, and not in a quoted string or
processing an escape sequence , and just entered whitespace , then
finalize the previous argument and remember that we are in whitespace
*/
if ( ! whitespace ) {
* ( cur + + ) = ' \0 ' ;
whitespace = 1 ;
}
} else if ( ( * s = = ' \\ ' ) & & ! escaped ) {
escaped = 1 ;
} else {
if ( whitespace ) {
/* If we are coming out of whitespace, start a new argument */
if ( x > = ( max - 1 ) ) {
ast_log ( LOG_WARNING , " Too many arguments, truncating \n " ) ;
break ;
1999-12-19 22:38:55 +00:00
}
2005-06-10 20:25:23 +00:00
argv [ x + + ] = cur ;
whitespace = 0 ;
1999-12-19 22:38:55 +00:00
}
2005-06-10 20:25:23 +00:00
* ( cur + + ) = * s ;
escaped = 0 ;
1999-12-19 22:38:55 +00:00
}
2005-06-10 20:25:23 +00:00
s + + ;
1999-12-19 22:38:55 +00:00
}
2005-06-10 20:25:23 +00:00
/* Null terminate */
* ( cur + + ) = ' \0 ' ;
argv [ x ] = NULL ;
* argc = x ;
2005-09-25 03:13:00 +00:00
* trailingwhitespace = whitespace ;
1999-12-19 22:38:55 +00:00
return dup ;
}
2003-01-30 15:03:20 +00:00
/* This returns the number of unique matches for the generator */
int ast_cli_generatornummatches ( char * text , char * word )
{
int matches = 0 , i = 0 ;
2004-11-17 19:28:08 +00:00
char * buf = NULL , * oldbuf = NULL ;
2003-01-30 15:03:20 +00:00
2005-09-24 15:25:52 +00:00
while ( ( buf = ast_cli_generator ( text , word , i + + ) ) ) {
if ( ! oldbuf | | strcmp ( buf , oldbuf ) )
matches + + ;
if ( oldbuf )
free ( oldbuf ) ;
2003-01-30 15:03:20 +00:00
oldbuf = buf ;
}
2005-09-24 15:25:52 +00:00
if ( oldbuf )
free ( oldbuf ) ;
2003-01-30 15:03:20 +00:00
return matches ;
}
char * * ast_cli_completion_matches ( char * text , char * word )
{
char * * match_list = NULL , * retstr , * prevstr ;
size_t match_list_len , max_equal , which , i ;
int matches = 0 ;
match_list_len = 1 ;
while ( ( retstr = ast_cli_generator ( text , word , matches ) ) ! = NULL ) {
if ( matches + 1 > = match_list_len ) {
match_list_len < < = 1 ;
match_list = realloc ( match_list , match_list_len * sizeof ( char * ) ) ;
}
match_list [ + + matches ] = retstr ;
}
if ( ! match_list )
return ( char * * ) NULL ;
which = 2 ;
prevstr = match_list [ 1 ] ;
max_equal = strlen ( prevstr ) ;
for ( ; which < = matches ; which + + ) {
2004-11-18 04:11:51 +00:00
for ( i = 0 ; i < max_equal & & toupper ( prevstr [ i ] ) = = toupper ( match_list [ which ] [ i ] ) ; i + + )
2003-01-30 15:03:20 +00:00
continue ;
max_equal = i ;
}
retstr = malloc ( max_equal + 1 ) ;
( void ) strncpy ( retstr , match_list [ 1 ] , max_equal ) ;
retstr [ max_equal ] = ' \0 ' ;
match_list [ 0 ] = retstr ;
if ( matches + 1 > = match_list_len )
match_list = realloc ( match_list , ( match_list_len + 1 ) * sizeof ( char * ) ) ;
match_list [ matches + 1 ] = ( char * ) NULL ;
return ( match_list ) ;
}
2001-05-09 03:11:22 +00:00
static char * __ast_cli_generator ( char * text , char * word , int state , int lock )
1999-12-19 22:38:55 +00:00
{
char * argv [ AST_MAX_ARGS ] ;
struct ast_cli_entry * e , * e1 , * e2 ;
int x ;
int matchnum = 0 ;
char * dup , * res ;
2004-11-17 19:28:08 +00:00
char fullcmd1 [ 80 ] = " " ;
char fullcmd2 [ 80 ] = " " ;
2005-08-14 01:48:14 +00:00
char matchstr [ 80 ] = " " ;
2004-11-17 19:28:08 +00:00
char * fullcmd = NULL ;
2005-09-25 03:13:00 +00:00
int tws ;
1999-12-19 22:38:55 +00:00
2005-09-25 03:13:00 +00:00
if ( ( dup = parse_args ( text , & x , argv , sizeof ( argv ) / sizeof ( argv [ 0 ] ) , & tws ) ) ) {
join ( matchstr , sizeof ( matchstr ) , argv , tws ) ;
2001-05-09 03:11:22 +00:00
if ( lock )
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & clilock ) ;
1999-12-19 22:38:55 +00:00
e1 = builtins ;
e2 = helpers ;
while ( e1 - > cmda [ 0 ] | | e2 ) {
if ( e2 )
2005-09-25 03:13:00 +00:00
join ( fullcmd2 , sizeof ( fullcmd2 ) , e2 - > cmda , tws ) ;
1999-12-19 22:38:55 +00:00
if ( e1 - > cmda [ 0 ] )
2005-09-25 03:13:00 +00:00
join ( fullcmd1 , sizeof ( fullcmd1 ) , e1 - > cmda , tws ) ;
2001-10-11 18:51:39 +00:00
if ( ! e1 - > cmda [ 0 ] | |
1999-12-19 22:38:55 +00:00
( e2 & & ( strcmp ( fullcmd2 , fullcmd1 ) < 0 ) ) ) {
/* Use e2 */
e = e2 ;
fullcmd = fullcmd2 ;
/* Increment by going to next */
e2 = e2 - > next ;
} else {
/* Use e1 */
e = e1 ;
fullcmd = fullcmd1 ;
e1 + + ;
}
2004-08-07 19:27:54 +00:00
if ( ( fullcmd [ 0 ] ! = ' _ ' ) & & ! strncasecmp ( matchstr , fullcmd , strlen ( matchstr ) ) ) {
1999-12-19 22:38:55 +00:00
/* We contain the first part of one or more commands */
2005-01-06 08:11:43 +00:00
/* Now, what we're supposed to return is the next word... */
if ( ! ast_strlen_zero ( word ) & & x > 0 ) {
res = e - > cmda [ x - 1 ] ;
} else {
res = e - > cmda [ x ] ;
}
if ( res ) {
matchnum + + ;
if ( matchnum > state ) {
2001-05-09 03:11:22 +00:00
if ( lock )
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & clilock ) ;
2003-04-08 13:45:36 +00:00
free ( dup ) ;
2005-01-06 08:11:43 +00:00
return strdup ( res ) ;
1999-12-19 22:38:55 +00:00
}
}
}
2005-08-08 01:45:29 +00:00
if ( e - > generator & & ! strncasecmp ( matchstr , fullcmd , strlen ( fullcmd ) ) & &
( matchstr [ strlen ( fullcmd ) ] < 33 ) ) {
1999-12-19 22:38:55 +00:00
/* We have a command in its entirity within us -- theoretically only one
command can have this occur */
2004-08-07 19:27:54 +00:00
fullcmd = e - > generator ( matchstr , word , ( ! ast_strlen_zero ( word ) ? ( x - 1 ) : ( x ) ) , state ) ;
2005-01-06 08:11:43 +00:00
if ( fullcmd ) {
if ( lock )
ast_mutex_unlock ( & clilock ) ;
free ( dup ) ;
return fullcmd ;
}
1999-12-19 22:38:55 +00:00
}
}
2001-05-09 03:11:22 +00:00
if ( lock )
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & clilock ) ;
1999-12-19 22:38:55 +00:00
free ( dup ) ;
}
return NULL ;
}
2001-05-09 03:11:22 +00:00
char * ast_cli_generator ( char * text , char * word , int state )
{
return __ast_cli_generator ( text , word , state , 1 ) ;
}
1999-12-19 22:38:55 +00:00
int ast_cli_command ( int fd , char * s )
{
char * argv [ AST_MAX_ARGS ] ;
struct ast_cli_entry * e ;
int x ;
char * dup ;
2005-09-25 03:13:00 +00:00
int tws ;
2005-06-10 20:25:23 +00:00
2005-09-25 03:13:00 +00:00
if ( ( dup = parse_args ( s , & x , argv , sizeof ( argv ) / sizeof ( argv [ 0 ] ) , & tws ) ) ) {
1999-12-19 22:38:55 +00:00
/* We need at least one entry, or ignore */
if ( x > 0 ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & clilock ) ;
1999-12-19 22:38:55 +00:00
e = find_cli ( argv , 0 ) ;
2002-05-13 22:29:39 +00:00
if ( e )
e - > inuse + + ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & clilock ) ;
1999-12-19 22:38:55 +00:00
if ( e ) {
switch ( e - > handler ( fd , x , argv ) ) {
case RESULT_SHOWUSAGE :
2005-08-26 15:59:00 +00:00
ast_cli ( fd , " %s " , e - > usage ) ;
1999-12-19 22:38:55 +00:00
break ;
}
} else
1999-12-27 19:20:20 +00:00
ast_cli ( fd , " No such command '%s' (type 'help' for help) \n " , find_best ( argv ) ) ;
2002-05-13 22:29:39 +00:00
if ( e ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & clilock ) ;
2002-05-13 22:29:39 +00:00
e - > inuse - - ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & clilock ) ;
2002-05-13 22:29:39 +00:00
}
1999-12-19 22:38:55 +00:00
}
free ( dup ) ;
} else {
ast_log ( LOG_WARNING , " Out of memory \n " ) ;
return - 1 ;
}
return 0 ;
}