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.
*/
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 Standard Command Line Interface
2005-09-14 20:46:50 +00:00
*
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"
2001-05-09 03:11:22 +00:00
2005-11-05 18:58:27 +00:00
extern const char * ast_build_hostname ;
extern const char * ast_build_kernel ;
extern const char * ast_build_machine ;
extern const char * ast_build_os ;
extern const char * ast_build_date ;
extern const char * ast_build_user ;
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 ;
2005-11-05 18:58:27 +00:00
ast_cli ( fd , "Asterisk %s built by %s @ %s on a %s running %s on %s \n " ,
ASTERISK_VERSION , ast_build_user , ast_build_hostname ,
ast_build_machine , ast_build_os , ast_build_date );
2001-05-09 03:11:22 +00:00
return RESULT_SUCCESS ;
}
2005-11-05 18:58:27 +00:00
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-10-11 20:20:40 +00:00
if ( tws && ! ast_strlen_zero ( dest ))
2005-09-25 03:13:00 +00:00
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 ;
}