2015-06-15 19:53:58 +03:00
/*
* vsh . c : common data to be used by clients to exercise the libvirt API
*
2019-03-22 22:02:39 +03:00
* Copyright ( C ) 2005 - 2019 Red Hat , Inc .
2015-06-15 19:53:58 +03:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see
* < http : //www.gnu.org/licenses/>.
*/
# include <config.h>
# include "vsh.h"
# include <assert.h>
# include <stdarg.h>
# include <unistd.h>
# include <sys/time.h>
# include <fcntl.h>
# include <time.h>
# include <sys/stat.h>
# include <inttypes.h>
# include <signal.h>
# if WITH_READLINE
# include <readline / readline.h>
# include <readline / history.h>
# endif
# include "internal.h"
# include "virerror.h"
# include "virbuffer.h"
# include "viralloc.h"
# include "virfile.h"
# include "virthread.h"
# include "vircommand.h"
# include "virtypedparam.h"
# include "virstring.h"
/* Gnulib doesn't guarantee SA_SIGINFO support. */
# ifndef SA_SIGINFO
# define SA_SIGINFO 0
# endif
2016-09-06 15:04:37 +03:00
# ifdef WITH_READLINE
/* For autocompletion */
2017-11-07 12:41:00 +03:00
vshControl * autoCompleteOpaque ;
2016-09-06 15:04:37 +03:00
# endif
2015-06-15 19:53:58 +03:00
/* NOTE: It would be much nicer to have these two as part of vshControl
* structure , unfortunately readline doesn ' t support passing opaque data
* and only relies on static data accessible from the user - side callback
*/
const vshCmdGrp * cmdGroups ;
const vshCmdDef * cmdSet ;
2016-02-12 16:00:28 +03:00
2015-06-15 19:53:58 +03:00
double
vshPrettyCapacity ( unsigned long long val , const char * * unit )
{
double limit = 1024 ;
if ( val < limit ) {
* unit = " B " ;
return val ;
}
limit * = 1024 ;
if ( val < limit ) {
* unit = " KiB " ;
return val / ( limit / 1024 ) ;
}
limit * = 1024 ;
if ( val < limit ) {
* unit = " MiB " ;
return val / ( limit / 1024 ) ;
}
limit * = 1024 ;
if ( val < limit ) {
* unit = " GiB " ;
return val / ( limit / 1024 ) ;
}
limit * = 1024 ;
if ( val < limit ) {
* unit = " TiB " ;
return val / ( limit / 1024 ) ;
}
limit * = 1024 ;
if ( val < limit ) {
* unit = " PiB " ;
return val / ( limit / 1024 ) ;
}
limit * = 1024 ;
* unit = " EiB " ;
return val / ( limit / 1024 ) ;
}
void *
_vshMalloc ( vshControl * ctl , size_t size , const char * filename , int line )
{
char * x ;
if ( VIR_ALLOC_N ( x , size ) = = 0 )
return x ;
vshError ( ctl , _ ( " %s: %d: failed to allocate %d bytes " ) ,
filename , line , ( int ) size ) ;
exit ( EXIT_FAILURE ) ;
}
void *
2019-10-24 10:02:24 +03:00
vshCalloc ( vshControl * ctl G_GNUC_UNUSED , size_t nmemb , size_t size )
2015-06-15 19:53:58 +03:00
{
2019-10-24 10:02:24 +03:00
return g_malloc0_n ( nmemb , size ) ;
2015-06-15 19:53:58 +03:00
}
int
vshNameSorter ( const void * a , const void * b )
{
const char * * sa = ( const char * * ) a ;
const char * * sb = ( const char * * ) b ;
return vshStrcasecmp ( * sa , * sb ) ;
}
/*
* Convert the strings separated by ' , ' into array . The returned
* array is a NULL terminated string list . The caller has to free
2016-11-25 11:18:35 +03:00
* the array using virStringListFree or a similar method .
2015-06-15 19:53:58 +03:00
*
* Returns the length of the filled array on success , or - 1
* on error .
*/
int
vshStringToArray ( const char * str ,
char * * * array )
{
2019-10-18 18:24:02 +03:00
char * str_copied = g_strdup ( str ) ;
2015-06-15 19:53:58 +03:00
char * str_tok = NULL ;
char * tmp ;
unsigned int nstr_tokens = 0 ;
char * * arr = NULL ;
size_t len = strlen ( str_copied ) ;
/* tokenize the string from user and save its parts into an array */
nstr_tokens = 1 ;
/* count the delimiters, recognizing ,, as an escape for a
* literal comma */
str_tok = str_copied ;
while ( ( str_tok = strchr ( str_tok , ' , ' ) ) ) {
if ( str_tok [ 1 ] = = ' , ' )
str_tok + + ;
else
nstr_tokens + + ;
str_tok + + ;
}
/* reserve the NULL element at the end */
if ( VIR_ALLOC_N ( arr , nstr_tokens + 1 ) < 0 ) {
VIR_FREE ( str_copied ) ;
return - 1 ;
}
/* tokenize the input string, while treating ,, as a literal comma */
nstr_tokens = 0 ;
tmp = str_tok = str_copied ;
while ( ( tmp = strchr ( tmp , ' , ' ) ) ) {
if ( tmp [ 1 ] = = ' , ' ) {
memmove ( & tmp [ 1 ] , & tmp [ 2 ] , len - ( tmp - str_copied ) - 2 + 1 ) ;
len - - ;
tmp + + ;
continue ;
}
* tmp + + = ' \0 ' ;
2019-10-18 18:24:02 +03:00
arr [ nstr_tokens + + ] = g_strdup ( str_tok ) ;
2015-06-15 19:53:58 +03:00
str_tok = tmp ;
}
2019-10-18 18:24:02 +03:00
arr [ nstr_tokens + + ] = g_strdup ( str_tok ) ;
2015-06-15 19:53:58 +03:00
* array = arr ;
VIR_FREE ( str_copied ) ;
return nstr_tokens ;
}
virErrorPtr last_error ;
/*
* Quieten libvirt until we ' re done with the command .
*/
void
2019-10-14 15:44:29 +03:00
vshErrorHandler ( void * opaque G_GNUC_UNUSED ,
virErrorPtr error G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
virFreeError ( last_error ) ;
last_error = virSaveLastError ( ) ;
}
/* Store a libvirt error that is from a helper API that doesn't raise errors
* so it doesn ' t get overwritten */
void
vshSaveLibvirtError ( void )
{
virFreeError ( last_error ) ;
last_error = virSaveLastError ( ) ;
}
2017-04-11 18:23:23 +03:00
/* Store libvirt error from helper API but don't overwrite existing errors */
void
vshSaveLibvirtHelperError ( void )
{
if ( last_error )
return ;
2018-05-05 15:04:21 +03:00
if ( virGetLastErrorCode ( ) = = VIR_ERR_OK )
2017-04-11 18:23:23 +03:00
return ;
vshSaveLibvirtError ( ) ;
}
2015-06-15 19:53:58 +03:00
/*
* Reset libvirt error on graceful fallback paths
*/
void
vshResetLibvirtError ( void )
{
virFreeError ( last_error ) ;
last_error = NULL ;
2018-10-11 19:29:58 +03:00
virResetLastError ( ) ;
2015-06-15 19:53:58 +03:00
}
/*
* Report an error when a command finishes . This is better than before
* ( when correct operation would report errors ) , but it has some
* problems : we lose the smarter formatting of virDefaultErrorFunc ( ) ,
* and it can become harder to debug problems , if errors get reported
* twice during one command . This case shouldn ' t really happen anyway ,
* and it ' s IMHO a bug that libvirt does that sometimes .
*/
void
vshReportError ( vshControl * ctl )
{
if ( last_error = = NULL ) {
/* Calling directly into libvirt util functions won't trigger the
* error callback ( which sets last_error ) , so check it ourselves .
*
* If the returned error has CODE_OK , this most likely means that
* no error was ever raised , so just ignore */
last_error = virSaveLastError ( ) ;
if ( ! last_error | | last_error - > code = = VIR_ERR_OK )
goto out ;
}
if ( last_error - > code = = VIR_ERR_OK ) {
vshError ( ctl , " %s " , _ ( " unknown error " ) ) ;
goto out ;
}
vshError ( ctl , " %s " , last_error - > message ) ;
out :
vshResetLibvirtError ( ) ;
}
/*
* Detection of disconnections and automatic reconnection support
*/
static int disconnected ; /* we may have been disconnected */
/* ---------------
* Utils for work with command definition
* - - - - - - - - - - - - - - -
*/
const char *
vshCmddefGetInfo ( const vshCmdDef * cmd , const char * name )
{
const vshCmdInfo * info ;
for ( info = cmd - > info ; info & & info - > name ; info + + ) {
if ( STREQ ( info - > name , name ) )
return info - > data ;
}
return NULL ;
}
2016-07-08 15:56:02 +03:00
/* Check if the internal command definitions are correct */
2015-06-15 19:53:58 +03:00
static int
2019-03-12 05:17:33 +03:00
vshCmddefCheckInternals ( vshControl * ctl ,
const vshCmdDef * cmd )
2015-06-15 19:53:58 +03:00
{
size_t i ;
2016-09-19 14:10:38 +03:00
const char * help = NULL ;
2016-09-07 18:30:48 +03:00
/* in order to perform the validation resolve the alias first */
if ( cmd - > flags & VSH_CMD_FLAG_ALIAS ) {
2019-03-12 05:17:33 +03:00
if ( ! cmd - > alias ) {
vshError ( ctl , _ ( " command '%s' has inconsistent alias " ) , cmd - > name ) ;
2016-09-07 18:30:48 +03:00
return - 1 ;
2019-03-12 05:17:33 +03:00
}
2016-09-07 18:30:48 +03:00
cmd = vshCmddefSearch ( cmd - > alias ) ;
}
2016-09-19 14:10:38 +03:00
/* Each command has to provide a non-empty help string. */
2019-03-12 05:17:33 +03:00
if ( ! ( help = vshCmddefGetInfo ( cmd , " help " ) ) | | ! * help ) {
vshError ( ctl , _ ( " command '%s' lacks help " ) , cmd - > name ) ;
2016-09-19 14:10:38 +03:00
return - 1 ;
2019-03-12 05:17:33 +03:00
}
2015-06-15 19:53:58 +03:00
if ( ! cmd - > opts )
return 0 ;
for ( i = 0 ; cmd - > opts [ i ] . name ; i + + ) {
const vshCmdOptDef * opt = & cmd - > opts [ i ] ;
2019-03-12 05:17:33 +03:00
if ( i > 63 ) {
vshError ( ctl , _ ( " command '%s' has too many options " ) , cmd - > name ) ;
2015-06-15 19:53:58 +03:00
return - 1 ; /* too many options */
2019-03-12 05:17:33 +03:00
}
2017-11-16 15:38:27 +03:00
switch ( opt - > type ) {
case VSH_OT_STRING :
case VSH_OT_BOOL :
2019-03-12 05:17:33 +03:00
if ( opt - > flags & VSH_OFLAG_REQ ) {
vshError ( ctl , _ ( " command '%s' misused VSH_OFLAG_REQ " ) ,
cmd - > name ) ;
return - 1 ; /* neither bool nor string options can be mandatory */
}
2017-11-16 15:38:27 +03:00
break ;
case VSH_OT_ALIAS : {
2015-06-15 19:53:58 +03:00
size_t j ;
char * name = ( char * ) opt - > help ; /* cast away const */
char * p ;
2019-03-12 05:17:33 +03:00
if ( opt - > flags | | ! opt - > help ) {
vshError ( ctl , _ ( " command '%s' has incorrect alias option " ) ,
cmd - > name ) ;
2015-06-15 19:53:58 +03:00
return - 1 ; /* alias options are tracked by the original name */
2019-03-12 05:17:33 +03:00
}
2019-10-24 20:34:57 +03:00
if ( ( p = strchr ( name , ' = ' ) ) )
name = g_strndup ( name , p - name ) ;
2015-06-15 19:53:58 +03:00
for ( j = i + 1 ; cmd - > opts [ j ] . name ; j + + ) {
if ( STREQ ( name , cmd - > opts [ j ] . name ) & &
cmd - > opts [ j ] . type ! = VSH_OT_ALIAS )
break ;
}
if ( name ! = opt - > help ) {
VIR_FREE ( name ) ;
/* If alias comes with value, replacement must not be bool */
2019-03-12 05:17:33 +03:00
if ( cmd - > opts [ j ] . type = = VSH_OT_BOOL ) {
vshError ( ctl , _ ( " command '%s' has mismatched alias type " ) ,
cmd - > name ) ;
2015-06-15 19:53:58 +03:00
return - 1 ;
2019-03-12 05:17:33 +03:00
}
2015-06-15 19:53:58 +03:00
}
2019-03-12 05:17:33 +03:00
if ( ! cmd - > opts [ j ] . name ) {
vshError ( ctl , _ ( " command '%s' has missing alias option " ) ,
cmd - > name ) ;
2015-06-15 19:53:58 +03:00
return - 1 ; /* alias option must map to a later option name */
2019-03-12 05:17:33 +03:00
}
2015-06-15 19:53:58 +03:00
}
2017-11-16 15:38:27 +03:00
break ;
case VSH_OT_ARGV :
2019-03-12 05:17:33 +03:00
if ( cmd - > opts [ i + 1 ] . name ) {
vshError ( ctl , _ ( " command '%s' does not list argv option last " ) ,
cmd - > name ) ;
2017-11-16 15:38:27 +03:00
return - 1 ; /* argv option must be listed last */
2019-03-12 05:17:33 +03:00
}
2017-11-16 15:38:27 +03:00
break ;
case VSH_OT_DATA :
2019-03-12 05:17:33 +03:00
if ( ! ( opt - > flags & VSH_OFLAG_REQ ) ) {
vshError ( ctl , _ ( " command '%s' has non-required VSH_OT_DATA " ) ,
cmd - > name ) ;
2017-11-16 15:38:27 +03:00
return - 1 ; /* OT_DATA should always be required. */
2019-03-12 05:17:33 +03:00
}
2017-11-16 15:38:27 +03:00
break ;
case VSH_OT_INT :
break ;
}
2016-07-08 15:56:02 +03:00
}
return 0 ;
}
2016-09-16 11:17:46 +03:00
/* Parse the options associated with @cmd, i.e. test whether options are
* required or need an argument .
*
* Returns - 1 on error or 0 on success , filling the caller - provided bitmaps
* which keep track of required options and options needing an argument .
*/
2016-07-08 15:56:02 +03:00
static int
2016-09-16 11:17:46 +03:00
vshCmddefOptParse ( const vshCmdDef * cmd , uint64_t * opts_need_arg ,
uint64_t * opts_required )
2016-07-08 15:56:02 +03:00
{
size_t i ;
bool optional = false ;
* opts_need_arg = 0 ;
* opts_required = 0 ;
if ( ! cmd - > opts )
return 0 ;
for ( i = 0 ; cmd - > opts [ i ] . name ; i + + ) {
const vshCmdOptDef * opt = & cmd - > opts [ i ] ;
if ( opt - > type = = VSH_OT_BOOL ) {
optional = true ;
continue ;
}
2015-06-15 19:53:58 +03:00
if ( opt - > flags & VSH_OFLAG_REQ_OPT ) {
if ( opt - > flags & VSH_OFLAG_REQ )
2016-04-20 16:51:43 +03:00
* opts_required | = 1ULL < < i ;
2015-06-15 19:53:58 +03:00
else
optional = true ;
continue ;
}
2016-07-08 15:56:02 +03:00
if ( opt - > type = = VSH_OT_ALIAS )
continue ; /* skip the alias option */
2016-04-20 16:51:43 +03:00
* opts_need_arg | = 1ULL < < i ;
2015-06-15 19:53:58 +03:00
if ( opt - > flags & VSH_OFLAG_REQ ) {
if ( optional & & opt - > type ! = VSH_OT_ARGV )
return - 1 ; /* mandatory options must be listed first */
2016-04-20 16:51:43 +03:00
* opts_required | = 1ULL < < i ;
2015-06-15 19:53:58 +03:00
} else {
optional = true ;
}
}
2016-07-08 15:56:02 +03:00
return 0 ;
}
2015-06-15 19:53:58 +03:00
static vshCmdOptDef helpopt = {
. name = " help " ,
. type = VSH_OT_BOOL ,
. help = N_ ( " print help for this function " )
} ;
static const vshCmdOptDef *
vshCmddefGetOption ( vshControl * ctl , const vshCmdDef * cmd , const char * name ,
2016-07-27 18:28:40 +03:00
uint64_t * opts_seen , size_t * opt_index , char * * optstr ,
2016-07-08 15:56:04 +03:00
bool report )
2015-06-15 19:53:58 +03:00
{
size_t i ;
const vshCmdOptDef * ret = NULL ;
char * alias = NULL ;
if ( STREQ ( name , helpopt . name ) )
return & helpopt ;
for ( i = 0 ; cmd - > opts & & cmd - > opts [ i ] . name ; i + + ) {
const vshCmdOptDef * opt = & cmd - > opts [ i ] ;
if ( STREQ ( opt - > name , name ) ) {
if ( opt - > type = = VSH_OT_ALIAS ) {
char * value ;
/* Two types of replacements:
opt - > help = " string " : straight replacement of name
opt - > help = " string=value " : treat boolean flag as
alias of option and its default value */
sa_assert ( ! alias ) ;
2019-10-20 14:49:46 +03:00
alias = g_strdup ( opt - > help ) ;
2015-06-15 19:53:58 +03:00
name = alias ;
if ( ( value = strchr ( name , ' = ' ) ) ) {
* value = ' \0 ' ;
if ( * optstr ) {
2016-07-08 15:56:04 +03:00
if ( report )
vshError ( ctl , _ ( " invalid '=' after option --%s " ) ,
opt - > name ) ;
2015-06-15 19:53:58 +03:00
goto cleanup ;
}
2019-10-20 14:49:46 +03:00
* optstr = g_strdup ( value + 1 ) ;
2015-06-15 19:53:58 +03:00
}
continue ;
}
2016-04-20 16:51:43 +03:00
if ( ( * opts_seen & ( 1ULL < < i ) ) & & opt - > type ! = VSH_OT_ARGV ) {
2016-07-08 15:56:04 +03:00
if ( report )
vshError ( ctl , _ ( " option --%s already seen " ) , name ) ;
2015-06-15 19:53:58 +03:00
goto cleanup ;
}
2016-04-20 16:51:43 +03:00
* opts_seen | = 1ULL < < i ;
2015-06-15 19:53:58 +03:00
* opt_index = i ;
ret = opt ;
goto cleanup ;
}
}
2016-07-08 15:56:04 +03:00
if ( STRNEQ ( cmd - > name , " help " ) & & report ) {
2015-06-15 19:53:58 +03:00
vshError ( ctl , _ ( " command '%s' doesn't support option --%s " ) ,
cmd - > name , name ) ;
}
cleanup :
VIR_FREE ( alias ) ;
return ret ;
}
static const vshCmdOptDef *
2016-03-31 15:41:52 +03:00
vshCmddefGetData ( const vshCmdDef * cmd , uint64_t * opts_need_arg ,
uint64_t * opts_seen )
2015-06-15 19:53:58 +03:00
{
size_t i ;
const vshCmdOptDef * opt ;
if ( ! * opts_need_arg )
return NULL ;
/* Grab least-significant set bit */
2019-10-03 17:51:30 +03:00
i = __builtin_ffsl ( * opts_need_arg ) - 1 ;
2015-06-15 19:53:58 +03:00
opt = & cmd - > opts [ i ] ;
if ( opt - > type ! = VSH_OT_ARGV )
2016-04-20 16:51:43 +03:00
* opts_need_arg & = ~ ( 1ULL < < i ) ;
* opts_seen | = 1ULL < < i ;
2015-06-15 19:53:58 +03:00
return opt ;
}
/*
* Checks for required options
*/
static int
2016-03-31 15:41:52 +03:00
vshCommandCheckOpts ( vshControl * ctl , const vshCmd * cmd , uint64_t opts_required ,
uint64_t opts_seen )
2015-06-15 19:53:58 +03:00
{
const vshCmdDef * def = cmd - > def ;
size_t i ;
opts_required & = ~ opts_seen ;
if ( ! opts_required )
return 0 ;
for ( i = 0 ; def - > opts [ i ] . name ; i + + ) {
2016-04-20 16:51:43 +03:00
if ( opts_required & ( 1ULL < < i ) ) {
2015-06-15 19:53:58 +03:00
const vshCmdOptDef * opt = & def - > opts [ i ] ;
vshError ( ctl ,
opt - > type = = VSH_OT_DATA | | opt - > type = = VSH_OT_ARGV ?
_ ( " command '%s' requires <%s> option " ) :
_ ( " command '%s' requires --%s option " ) ,
def - > name , opt - > name ) ;
}
}
return - 1 ;
}
static const vshCmdDef *
vshCmdDefSearchGrp ( const char * cmdname )
{
const vshCmdGrp * g ;
const vshCmdDef * c ;
for ( g = cmdGroups ; g - > name ; g + + ) {
for ( c = g - > commands ; c - > name ; c + + ) {
if ( STREQ ( c - > name , cmdname ) )
return c ;
}
}
return NULL ;
}
static const vshCmdDef *
vshCmdDefSearchSet ( const char * cmdname )
{
const vshCmdDef * s ;
for ( s = cmdSet ; s - > name ; s + + ) {
if ( STREQ ( s - > name , cmdname ) )
return s ;
}
return NULL ;
}
const vshCmdDef *
vshCmddefSearch ( const char * cmdname )
{
if ( cmdGroups )
return vshCmdDefSearchGrp ( cmdname ) ;
else
return vshCmdDefSearchSet ( cmdname ) ;
}
const vshCmdGrp *
vshCmdGrpSearch ( const char * grpname )
{
const vshCmdGrp * g ;
for ( g = cmdGroups ; g - > name ; g + + ) {
if ( STREQ ( g - > name , grpname ) | | STREQ ( g - > keyword , grpname ) )
return g ;
}
return NULL ;
}
bool
2018-01-25 18:08:46 +03:00
vshCmdGrpHelp ( vshControl * ctl , const vshCmdGrp * grp )
2015-06-15 19:53:58 +03:00
{
const vshCmdDef * cmd = NULL ;
2018-01-25 18:08:46 +03:00
vshPrint ( ctl , _ ( " %s (help keyword '%s'): \n " ) , grp - > name ,
grp - > keyword ) ;
2015-06-15 19:53:58 +03:00
2018-01-25 18:08:46 +03:00
for ( cmd = grp - > commands ; cmd - > name ; cmd + + ) {
if ( cmd - > flags & VSH_CMD_FLAG_ALIAS )
continue ;
vshPrint ( ctl , " %-30s %s \n " , cmd - > name ,
_ ( vshCmddefGetInfo ( cmd , " help " ) ) ) ;
2015-06-15 19:53:58 +03:00
}
return true ;
}
bool
2018-01-25 18:08:46 +03:00
vshCmddefHelp ( vshControl * ctl , const vshCmdDef * def )
2015-06-15 19:53:58 +03:00
{
2016-09-15 18:52:11 +03:00
const char * desc = NULL ;
char buf [ 256 ] ;
uint64_t opts_need_arg ;
uint64_t opts_required ;
bool shortopt = false ; /* true if 'arg' works instead of '--opt arg' */
2015-06-15 19:53:58 +03:00
2016-09-15 18:52:11 +03:00
if ( vshCmddefOptParse ( def , & opts_need_arg , & opts_required ) ) {
vshError ( ctl , _ ( " internal error: bad options in command: '%s' " ) ,
def - > name ) ;
return false ;
}
fputs ( _ ( " NAME \n " ) , stdout ) ;
fprintf ( stdout , " %s - %s \n " , def - > name ,
_ ( vshCmddefGetInfo ( def , " help " ) ) ) ;
fputs ( _ ( " \n SYNOPSIS \n " ) , stdout ) ;
fprintf ( stdout , " %s " , def - > name ) ;
if ( def - > opts ) {
const vshCmdOptDef * opt ;
for ( opt = def - > opts ; opt - > name ; opt + + ) {
const char * fmt = " %s " ;
switch ( opt - > type ) {
case VSH_OT_BOOL :
fmt = " [--%s] " ;
break ;
case VSH_OT_INT :
/* xgettext:c-format */
fmt = ( ( opt - > flags & VSH_OFLAG_REQ ) ? " <%s> "
: _ ( " [--%s <number>] " ) ) ;
if ( ! ( opt - > flags & VSH_OFLAG_REQ_OPT ) )
shortopt = true ;
break ;
case VSH_OT_STRING :
/* xgettext:c-format */
fmt = _ ( " [--%s <string>] " ) ;
if ( ! ( opt - > flags & VSH_OFLAG_REQ_OPT ) )
shortopt = true ;
break ;
case VSH_OT_DATA :
fmt = ( ( opt - > flags & VSH_OFLAG_REQ ) ? " <%s> " : " [<%s>] " ) ;
if ( ! ( opt - > flags & VSH_OFLAG_REQ_OPT ) )
shortopt = true ;
break ;
case VSH_OT_ARGV :
/* xgettext:c-format */
if ( shortopt ) {
fmt = ( opt - > flags & VSH_OFLAG_REQ )
? _ ( " {[--%s] <string>}... " )
: _ ( " [[--%s] <string>]... " ) ;
} else {
fmt = ( opt - > flags & VSH_OFLAG_REQ ) ? _ ( " <%s>... " )
: _ ( " [<%s>]... " ) ;
2015-06-15 19:53:58 +03:00
}
2016-09-15 18:52:11 +03:00
break ;
case VSH_OT_ALIAS :
/* aliases are intentionally undocumented */
continue ;
2015-06-15 19:53:58 +03:00
}
2016-09-15 18:52:11 +03:00
fputc ( ' ' , stdout ) ;
fprintf ( stdout , fmt , opt - > name ) ;
2015-06-15 19:53:58 +03:00
}
2016-09-15 18:52:11 +03:00
}
fputc ( ' \n ' , stdout ) ;
2015-06-15 19:53:58 +03:00
2016-09-15 18:52:11 +03:00
desc = vshCmddefGetInfo ( def , " desc " ) ;
if ( * desc ) {
/* Print the description only if it's not empty. */
fputs ( _ ( " \n DESCRIPTION \n " ) , stdout ) ;
fprintf ( stdout , " %s \n " , _ ( desc ) ) ;
}
2015-06-15 19:53:58 +03:00
2016-09-15 18:52:11 +03:00
if ( def - > opts & & def - > opts - > name ) {
const vshCmdOptDef * opt ;
fputs ( _ ( " \n OPTIONS \n " ) , stdout ) ;
for ( opt = def - > opts ; opt - > name ; opt + + ) {
switch ( opt - > type ) {
case VSH_OT_BOOL :
2019-11-13 16:53:42 +03:00
g_snprintf ( buf , sizeof ( buf ) , " --%s " , opt - > name ) ;
2016-09-15 18:52:11 +03:00
break ;
case VSH_OT_INT :
2019-11-13 16:53:42 +03:00
g_snprintf ( buf , sizeof ( buf ) ,
( opt - > flags & VSH_OFLAG_REQ ) ? _ ( " [--%s] <number> " )
: _ ( " --%s <number> " ) , opt - > name ) ;
2016-09-15 18:52:11 +03:00
break ;
case VSH_OT_STRING :
2019-11-13 16:53:42 +03:00
g_snprintf ( buf , sizeof ( buf ) , _ ( " --%s <string> " ) , opt - > name ) ;
2016-09-15 18:52:11 +03:00
break ;
case VSH_OT_DATA :
2019-11-13 16:53:42 +03:00
g_snprintf ( buf , sizeof ( buf ) , _ ( " [--%s] <string> " ) ,
opt - > name ) ;
2016-09-15 18:52:11 +03:00
break ;
case VSH_OT_ARGV :
2019-11-13 16:53:42 +03:00
g_snprintf ( buf , sizeof ( buf ) ,
shortopt ? _ ( " [--%s] <string> " ) : _ ( " <%s> " ) ,
opt - > name ) ;
2016-09-15 18:52:11 +03:00
break ;
case VSH_OT_ALIAS :
continue ;
2015-06-15 19:53:58 +03:00
}
2016-09-15 18:52:11 +03:00
fprintf ( stdout , " %-15s %s \n " , buf , _ ( opt - > help ) ) ;
2015-06-15 19:53:58 +03:00
}
}
2016-09-15 18:52:11 +03:00
fputc ( ' \n ' , stdout ) ;
2015-06-15 19:53:58 +03:00
return true ;
}
/* ---------------
* Utils for work with runtime commands data
* - - - - - - - - - - - - - - -
*/
static void
vshCommandOptFree ( vshCmdOpt * arg )
{
vshCmdOpt * a = arg ;
while ( a ) {
vshCmdOpt * tmp = a ;
a = a - > next ;
VIR_FREE ( tmp - > data ) ;
VIR_FREE ( tmp ) ;
}
}
static void
vshCommandFree ( vshCmd * cmd )
{
vshCmd * c = cmd ;
while ( c ) {
vshCmd * tmp = c ;
c = c - > next ;
2017-12-29 14:19:47 +03:00
vshCommandOptFree ( tmp - > opts ) ;
2015-06-15 19:53:58 +03:00
VIR_FREE ( tmp ) ;
}
}
/**
* vshCommandOpt :
* @ cmd : parsed command line to search
* @ name : option name to search for
* @ opt : result of the search
* @ needData : true if option must be non - boolean
*
* Look up an option passed to CMD by NAME . Returns 1 with * OPT set
* to the option if found , 0 with * OPT set to NULL if the name is
* valid and the option is not required , - 1 with * OPT set to NULL if
* the option is required but not present , and assert if NAME is not
2017-11-09 20:06:11 +03:00
* valid ( which indicates a programming error ) unless cmd - > skipChecks
* is set . No error messages are issued if a value is returned .
2015-06-15 19:53:58 +03:00
*/
static int
vshCommandOpt ( const vshCmd * cmd , const char * name , vshCmdOpt * * opt ,
bool needData )
{
vshCmdOpt * candidate = cmd - > opts ;
const vshCmdOptDef * valid = cmd - > def - > opts ;
int ret = 0 ;
/* See if option is valid and/or required. */
* opt = NULL ;
2017-11-09 20:06:11 +03:00
2018-02-28 12:09:56 +03:00
while ( valid & & valid - > name ) {
if ( STREQ ( name , valid - > name ) )
break ;
valid + + ;
}
2017-11-09 20:06:11 +03:00
2018-02-28 12:09:56 +03:00
if ( ! cmd - > skipChecks )
2017-11-09 20:06:11 +03:00
assert ( valid & & ( ! needData | | valid - > type ! = VSH_OT_BOOL ) ) ;
2018-02-28 12:09:56 +03:00
if ( valid & & valid - > flags & VSH_OFLAG_REQ )
ret = - 1 ;
2015-06-15 19:53:58 +03:00
/* See if option is present on command line. */
while ( candidate ) {
if ( STREQ ( candidate - > def - > name , name ) ) {
* opt = candidate ;
ret = 1 ;
break ;
}
candidate = candidate - > next ;
}
return ret ;
}
/**
* vshCommandOptInt :
* @ ctl virtshell control structure
* @ cmd command reference
* @ name option name
* @ value result
*
* Convert option to int .
* On error , a message is displayed .
*
* Return value :
* > 0 if option found and valid ( @ value updated )
* 0 if option not found and not required ( @ value untouched )
* < 0 in all other cases ( @ value untouched )
*/
int
vshCommandOptInt ( vshControl * ctl , const vshCmd * cmd ,
const char * name , int * value )
{
vshCmdOpt * arg ;
int ret ;
if ( ( ret = vshCommandOpt ( cmd , name , & arg , true ) ) < = 0 )
return ret ;
if ( ( ret = virStrToLong_i ( arg - > data , NULL , 10 , value ) ) < 0 )
vshError ( ctl ,
_ ( " Numeric value '%s' for <%s> option is malformed or out of range " ) ,
arg - > data , name ) ;
else
ret = 1 ;
return ret ;
}
static int
vshCommandOptUIntInternal ( vshControl * ctl ,
const vshCmd * cmd ,
const char * name ,
unsigned int * value ,
bool wrap )
{
vshCmdOpt * arg ;
int ret ;
if ( ( ret = vshCommandOpt ( cmd , name , & arg , true ) ) < = 0 )
return ret ;
if ( wrap )
ret = virStrToLong_ui ( arg - > data , NULL , 10 , value ) ;
else
ret = virStrToLong_uip ( arg - > data , NULL , 10 , value ) ;
if ( ret < 0 )
vshError ( ctl ,
_ ( " Numeric value '%s' for <%s> option is malformed or out of range " ) ,
arg - > data , name ) ;
else
ret = 1 ;
return ret ;
}
/**
* vshCommandOptUInt :
* @ ctl virtshell control structure
* @ cmd command reference
* @ name option name
* @ value result
*
* Convert option to unsigned int , reject negative numbers
* See vshCommandOptInt ( )
*/
int
vshCommandOptUInt ( vshControl * ctl , const vshCmd * cmd ,
const char * name , unsigned int * value )
{
return vshCommandOptUIntInternal ( ctl , cmd , name , value , false ) ;
}
/**
* vshCommandOptUIntWrap :
* @ ctl virtshell control structure
* @ cmd command reference
* @ name option name
* @ value result
*
* Convert option to unsigned int , wraps negative numbers to positive
* See vshCommandOptInt ( )
*/
int
vshCommandOptUIntWrap ( vshControl * ctl , const vshCmd * cmd ,
const char * name , unsigned int * value )
{
return vshCommandOptUIntInternal ( ctl , cmd , name , value , true ) ;
}
static int
vshCommandOptULInternal ( vshControl * ctl ,
const vshCmd * cmd ,
const char * name ,
unsigned long * value ,
bool wrap )
{
vshCmdOpt * arg ;
int ret ;
if ( ( ret = vshCommandOpt ( cmd , name , & arg , true ) ) < = 0 )
return ret ;
if ( wrap )
ret = virStrToLong_ul ( arg - > data , NULL , 10 , value ) ;
else
ret = virStrToLong_ulp ( arg - > data , NULL , 10 , value ) ;
if ( ret < 0 )
vshError ( ctl ,
_ ( " Numeric value '%s' for <%s> option is malformed or out of range " ) ,
arg - > data , name ) ;
else
ret = 1 ;
return ret ;
}
/*
* vshCommandOptUL :
* @ ctl virtshell control structure
* @ cmd command reference
* @ name option name
* @ value result
*
* Convert option to unsigned long
* See vshCommandOptInt ( )
*/
int
vshCommandOptUL ( vshControl * ctl , const vshCmd * cmd ,
const char * name , unsigned long * value )
{
return vshCommandOptULInternal ( ctl , cmd , name , value , false ) ;
}
/**
* vshCommandOptULWrap :
* @ ctl virtshell control structure
* @ cmd command reference
* @ name option name
* @ value result
*
* Convert option to unsigned long , wraps negative numbers to positive
* See vshCommandOptInt ( )
*/
int
vshCommandOptULWrap ( vshControl * ctl , const vshCmd * cmd ,
const char * name , unsigned long * value )
{
return vshCommandOptULInternal ( ctl , cmd , name , value , true ) ;
}
/**
2015-12-03 15:47:56 +03:00
* vshCommandOptStringQuiet :
2015-06-15 19:53:58 +03:00
* @ ctl virtshell control structure
* @ cmd command reference
* @ name option name
* @ value result
*
2015-12-03 15:47:56 +03:00
* Returns option as STRING . On error - 1 is returned but no error is set .
2015-06-15 19:53:58 +03:00
* Return value :
* > 0 if option found and valid ( @ value updated )
* 0 if option not found and not required ( @ value untouched )
* < 0 in all other cases ( @ value untouched )
*/
int
2019-10-14 15:44:29 +03:00
vshCommandOptStringQuiet ( vshControl * ctl G_GNUC_UNUSED , const vshCmd * cmd ,
2015-12-03 15:47:56 +03:00
const char * name , const char * * value )
2015-06-15 19:53:58 +03:00
{
vshCmdOpt * arg ;
int ret ;
if ( ( ret = vshCommandOpt ( cmd , name , & arg , true ) ) < = 0 )
return ret ;
if ( ! * arg - > data & & ! ( arg - > def - > flags & VSH_OFLAG_EMPTY_OK ) )
return - 1 ;
* value = arg - > data ;
return 1 ;
}
/**
* vshCommandOptStringReq :
* @ ctl virtshell control structure
* @ cmd command structure
* @ name option name
* @ value result ( updated to NULL or the option argument )
*
* Gets a option argument as string .
*
* Returns 0 on success or when the option is not present and not
* required , * value is set to the option argument . On error - 1 is
* returned and error message printed .
*/
int
vshCommandOptStringReq ( vshControl * ctl ,
const vshCmd * cmd ,
const char * name ,
const char * * value )
{
vshCmdOpt * arg ;
int ret ;
const char * error = NULL ;
/* clear out the value */
* value = NULL ;
ret = vshCommandOpt ( cmd , name , & arg , true ) ;
/* option is not required and not present */
if ( ret = = 0 )
return 0 ;
/* this should not be propagated here, just to be sure */
if ( ret = = - 1 )
error = N_ ( " Mandatory option not present " ) ;
else if ( ! * arg - > data & & ! ( arg - > def - > flags & VSH_OFLAG_EMPTY_OK ) )
error = N_ ( " Option argument is empty " ) ;
if ( error ) {
2018-02-28 12:09:56 +03:00
if ( ! cmd - > skipChecks )
vshError ( ctl , _ ( " Failed to get option '%s': %s " ) , name , _ ( error ) ) ;
2015-06-15 19:53:58 +03:00
return - 1 ;
}
* value = arg - > data ;
return 0 ;
}
/**
* vshCommandOptLongLong :
* @ ctl virtshell control structure
* @ cmd command reference
* @ name option name
* @ value result
*
* Returns option as long long
* See vshCommandOptInt ( )
*/
int
vshCommandOptLongLong ( vshControl * ctl , const vshCmd * cmd ,
const char * name , long long * value )
{
vshCmdOpt * arg ;
int ret ;
if ( ( ret = vshCommandOpt ( cmd , name , & arg , true ) ) < = 0 )
return ret ;
if ( ( ret = virStrToLong_ll ( arg - > data , NULL , 10 , value ) ) < 0 )
vshError ( ctl ,
_ ( " Numeric value '%s' for <%s> option is malformed or out of range " ) ,
arg - > data , name ) ;
else
ret = 1 ;
return ret ;
}
static int
vshCommandOptULongLongInternal ( vshControl * ctl ,
const vshCmd * cmd ,
const char * name ,
unsigned long long * value ,
bool wrap )
{
vshCmdOpt * arg ;
int ret ;
if ( ( ret = vshCommandOpt ( cmd , name , & arg , true ) ) < = 0 )
return ret ;
if ( wrap )
ret = virStrToLong_ull ( arg - > data , NULL , 10 , value ) ;
else
ret = virStrToLong_ullp ( arg - > data , NULL , 10 , value ) ;
if ( ret < 0 )
vshError ( ctl ,
_ ( " Numeric value '%s' for <%s> option is malformed or out of range " ) ,
arg - > data , name ) ;
else
ret = 1 ;
return ret ;
}
/**
* vshCommandOptULongLong :
* @ ctl virtshell control structure
* @ cmd command reference
* @ name option name
* @ value result
*
* Returns option as long long , rejects negative numbers
* See vshCommandOptInt ( )
*/
int
vshCommandOptULongLong ( vshControl * ctl , const vshCmd * cmd ,
const char * name , unsigned long long * value )
{
return vshCommandOptULongLongInternal ( ctl , cmd , name , value , false ) ;
}
/**
* vshCommandOptULongLongWrap :
* @ ctl virtshell control structure
* @ cmd command reference
* @ name option name
* @ value result
*
* Returns option as long long , wraps negative numbers to positive
* See vshCommandOptInt ( )
*/
int
vshCommandOptULongLongWrap ( vshControl * ctl , const vshCmd * cmd ,
const char * name , unsigned long long * value )
{
return vshCommandOptULongLongInternal ( ctl , cmd , name , value , true ) ;
}
/**
* vshCommandOptScaledInt :
* @ ctl virtshell control structure
* @ cmd command reference
* @ name option name
* @ value result
* @ scale default of 1 or 1024 , if no suffix is present
* @ max maximum value permitted
*
* Returns option as long long , scaled according to suffix
* See vshCommandOptInt ( )
*/
int
vshCommandOptScaledInt ( vshControl * ctl , const vshCmd * cmd ,
const char * name , unsigned long long * value ,
int scale , unsigned long long max )
{
vshCmdOpt * arg ;
char * end ;
int ret ;
if ( ( ret = vshCommandOpt ( cmd , name , & arg , true ) ) < = 0 )
return ret ;
2016-03-17 17:45:09 +03:00
2015-06-15 19:53:58 +03:00
if ( virStrToLong_ullp ( arg - > data , & end , 10 , value ) < 0 | |
2016-03-17 17:45:09 +03:00
virScaleInteger ( value , end , scale , max ) < 0 ) {
2015-06-15 19:53:58 +03:00
vshError ( ctl ,
2016-03-17 17:40:43 +03:00
_ ( " Scaled numeric value '%s' for <%s> option is malformed or "
" out of range " ) , arg - > data , name ) ;
2016-03-17 17:45:09 +03:00
return - 1 ;
2015-06-15 19:53:58 +03:00
}
2016-03-17 17:45:09 +03:00
return 1 ;
2015-06-15 19:53:58 +03:00
}
/**
* vshCommandOptBool :
* @ cmd command reference
* @ name option name
*
* Returns true / false if the option exists . Note that this does NOT
* validate whether the option is actually boolean , or even whether
* name is legal ; so that this can be used to probe whether a data
* option is present without actually using that data .
*/
bool
vshCommandOptBool ( const vshCmd * cmd , const char * name )
{
vshCmdOpt * dummy ;
return vshCommandOpt ( cmd , name , & dummy , false ) = = 1 ;
}
/**
* vshCommandOptArgv :
* @ ctl virtshell control structure
* @ cmd command reference
* @ opt starting point for the search
*
* Returns the next argv argument after OPT ( or the first one if OPT
* is NULL ) , or NULL if no more are present .
*
* Requires that a VSH_OT_ARGV option be last in the
* list of supported options in CMD - > def - > opts .
*/
const vshCmdOpt *
2019-10-14 15:44:29 +03:00
vshCommandOptArgv ( vshControl * ctl G_GNUC_UNUSED , const vshCmd * cmd ,
2015-06-15 19:53:58 +03:00
const vshCmdOpt * opt )
{
opt = opt ? opt - > next : cmd - > opts ;
while ( opt ) {
if ( opt - > def - > type = = VSH_OT_ARGV )
return opt ;
opt = opt - > next ;
}
return NULL ;
}
2016-03-29 16:47:40 +03:00
/**
* vshBlockJobOptionBandwidth :
* @ ctl : virsh control data
* @ cmd : virsh command description
* @ bytes : return bandwidth in bytes / s instead of MiB / s
* @ bandwidth : return value
*
* Extracts the value of - - bandwidth either as a wrap - able number without scale
* or as a scaled integer . The returned value is checked to fit into a unsigned
* long data type . This is a legacy compatibility function and it should not
* be used for things other the block job APIs .
*
* Returns 0 on success , - 1 on error .
*/
int
vshBlockJobOptionBandwidth ( vshControl * ctl ,
const vshCmd * cmd ,
bool bytes ,
unsigned long * bandwidth )
{
vshCmdOpt * arg ;
char * end ;
unsigned long long bw ;
int ret ;
if ( ( ret = vshCommandOpt ( cmd , " bandwidth " , & arg , true ) ) < = 0 )
return ret ;
/* due to historical reasons we declare to parse negative numbers and wrap
* them to the unsigned data type . */
if ( virStrToLong_ul ( arg - > data , NULL , 10 , bandwidth ) < 0 ) {
/* try to parse the number as scaled size in this case we don't accept
* wrapping since it would be ridiculous . In case of a 32 bit host ,
* limit the value to ULONG_MAX */
if ( virStrToLong_ullp ( arg - > data , & end , 10 , & bw ) < 0 | |
virScaleInteger ( & bw , end , 1 , ULONG_MAX ) < 0 ) {
vshError ( ctl ,
_ ( " Scaled numeric value '%s' for <--bandwidth> option is "
" malformed or out of range " ) , arg - > data ) ;
return - 1 ;
}
if ( ! bytes )
bw > > = 20 ;
* bandwidth = bw ;
}
return 0 ;
}
2015-06-15 19:53:58 +03:00
/*
* Executes command ( s ) and returns return code from last command
*/
bool
vshCommandRun ( vshControl * ctl , const vshCmd * cmd )
{
const vshClientHooks * hooks = ctl - > hooks ;
bool ret = true ;
while ( cmd ) {
2019-12-20 19:19:30 +03:00
gint64 before , after ;
2015-06-15 19:53:58 +03:00
bool enable_timing = ctl - > timing ;
2019-12-20 19:19:30 +03:00
before = g_get_real_time ( ) ;
2015-06-15 19:53:58 +03:00
if ( ( cmd - > def - > flags & VSH_CMD_FLAG_NOCONNECT ) | |
( hooks & & hooks - > connHandler & & hooks - > connHandler ( ctl ) ) ) {
ret = cmd - > def - > handler ( ctl , cmd ) ;
} else {
/* connection is not usable, return error */
ret = false ;
}
2019-12-20 19:19:30 +03:00
after = g_get_real_time ( ) ;
2015-06-15 19:53:58 +03:00
/* try to automatically catch disconnections */
if ( ! ret & &
( ( last_error ! = NULL ) & &
( ( ( last_error - > code = = VIR_ERR_SYSTEM_ERROR ) & &
( last_error - > domain = = VIR_FROM_REMOTE ) ) | |
( last_error - > code = = VIR_ERR_RPC ) | |
( last_error - > code = = VIR_ERR_NO_CONNECT ) | |
( last_error - > code = = VIR_ERR_INVALID_CONN ) ) ) )
disconnected + + ;
if ( ! ret )
vshReportError ( ctl ) ;
if ( STREQ ( cmd - > def - > name , " quit " ) | |
STREQ ( cmd - > def - > name , " exit " ) ) /* hack ... */
return ret ;
if ( enable_timing ) {
2019-12-20 19:19:30 +03:00
double diff_ms = ( after - before ) / 1000.0 ;
2015-06-15 19:53:58 +03:00
vshPrint ( ctl , _ ( " \n (Time: %.3f ms) \n \n " ) , diff_ms ) ;
} else {
vshPrintExtra ( ctl , " \n " ) ;
}
cmd = cmd - > next ;
}
return ret ;
}
/* ---------------
* Command parsing
* - - - - - - - - - - - - - - -
*/
typedef enum {
VSH_TK_ERROR , /* Failed to parse a token */
VSH_TK_ARG , /* Arbitrary argument, might be option or empty */
VSH_TK_SUBCMD_END , /* Separation between commands */
VSH_TK_END /* No more commands */
} vshCommandToken ;
typedef struct _vshCommandParser vshCommandParser ;
struct _vshCommandParser {
vshCommandToken ( * getNextArg ) ( vshControl * , vshCommandParser * ,
2016-07-08 15:56:04 +03:00
char * * , bool ) ;
2015-06-15 19:53:58 +03:00
/* vshCommandStringGetArg() */
char * pos ;
/* vshCommandArgvGetArg() */
char * * arg_pos ;
char * * arg_end ;
} ;
static bool
2017-11-06 17:46:50 +03:00
vshCommandParse ( vshControl * ctl , vshCommandParser * parser , vshCmd * * partial )
2015-06-15 19:53:58 +03:00
{
char * tkdata = NULL ;
vshCmd * clast = NULL ;
vshCmdOpt * first = NULL ;
2017-11-06 17:46:50 +03:00
const vshCmdDef * cmd = NULL ;
2015-06-15 19:53:58 +03:00
2017-11-06 17:46:50 +03:00
if ( ! partial ) {
vshCommandFree ( ctl - > cmd ) ;
ctl - > cmd = NULL ;
}
2015-06-15 19:53:58 +03:00
while ( 1 ) {
vshCmdOpt * last = NULL ;
vshCommandToken tk ;
bool data_only = false ;
2016-03-31 15:41:52 +03:00
uint64_t opts_need_arg = 0 ;
uint64_t opts_required = 0 ;
uint64_t opts_seen = 0 ;
2015-06-15 19:53:58 +03:00
2017-11-06 17:46:50 +03:00
cmd = NULL ;
2015-06-15 19:53:58 +03:00
first = NULL ;
2017-11-06 17:46:50 +03:00
if ( partial ) {
vshCommandFree ( * partial ) ;
* partial = NULL ;
}
2015-06-15 19:53:58 +03:00
while ( 1 ) {
const vshCmdOptDef * opt = NULL ;
tkdata = NULL ;
2016-07-08 15:56:04 +03:00
tk = parser - > getNextArg ( ctl , parser , & tkdata , true ) ;
2015-06-15 19:53:58 +03:00
if ( tk = = VSH_TK_ERROR )
goto syntaxError ;
if ( tk ! = VSH_TK_ARG ) {
VIR_FREE ( tkdata ) ;
break ;
}
if ( cmd = = NULL ) {
2019-03-22 22:02:39 +03:00
/* first token must be command name or comment */
if ( * tkdata = = ' # ' ) {
do {
VIR_FREE ( tkdata ) ;
tk = parser - > getNextArg ( ctl , parser , & tkdata , false ) ;
} while ( tk = = VSH_TK_ARG ) ;
VIR_FREE ( tkdata ) ;
break ;
} else if ( ! ( cmd = vshCmddefSearch ( tkdata ) ) ) {
2017-11-06 17:46:50 +03:00
if ( ! partial )
vshError ( ctl , _ ( " unknown command: '%s' " ) , tkdata ) ;
2015-06-15 19:53:58 +03:00
goto syntaxError ; /* ... or ignore this command only? */
}
2016-09-07 18:30:48 +03:00
/* aliases need to be resolved to the actual commands */
if ( cmd - > flags & VSH_CMD_FLAG_ALIAS ) {
VIR_FREE ( tkdata ) ;
2019-10-18 18:24:02 +03:00
tkdata = g_strdup ( cmd - > alias ) ;
2016-09-07 18:30:48 +03:00
cmd = vshCmddefSearch ( tkdata ) ;
}
2015-06-15 19:53:58 +03:00
if ( vshCmddefOptParse ( cmd , & opts_need_arg ,
& opts_required ) < 0 ) {
2017-11-06 17:46:50 +03:00
if ( ! partial )
vshError ( ctl ,
_ ( " internal error: bad options in command: '%s' " ) ,
tkdata ) ;
2015-06-15 19:53:58 +03:00
goto syntaxError ;
}
VIR_FREE ( tkdata ) ;
} else if ( data_only ) {
goto get_data ;
} else if ( tkdata [ 0 ] = = ' - ' & & tkdata [ 1 ] = = ' - ' & &
2019-11-18 17:14:06 +03:00
g_ascii_isalnum ( tkdata [ 2 ] ) ) {
2015-06-15 19:53:58 +03:00
char * optstr = strchr ( tkdata + 2 , ' = ' ) ;
2016-07-27 18:28:40 +03:00
size_t opt_index = 0 ;
2015-06-15 19:53:58 +03:00
if ( optstr ) {
* optstr = ' \0 ' ; /* convert the '=' to '\0' */
2019-10-18 18:24:02 +03:00
optstr = g_strdup ( optstr + 1 ) ;
2015-06-15 19:53:58 +03:00
}
/* Special case 'help' to ignore all spurious options */
if ( ! ( opt = vshCmddefGetOption ( ctl , cmd , tkdata + 2 ,
& opts_seen , & opt_index ,
2017-11-06 17:46:50 +03:00
& optstr , partial = = NULL ) ) ) {
2015-06-15 19:53:58 +03:00
VIR_FREE ( optstr ) ;
if ( STREQ ( cmd - > name , " help " ) )
continue ;
goto syntaxError ;
}
VIR_FREE ( tkdata ) ;
if ( opt - > type ! = VSH_OT_BOOL ) {
/* option data */
if ( optstr )
tkdata = optstr ;
else
2016-07-08 15:56:04 +03:00
tk = parser - > getNextArg ( ctl , parser , & tkdata , true ) ;
2015-06-15 19:53:58 +03:00
if ( tk = = VSH_TK_ERROR )
goto syntaxError ;
if ( tk ! = VSH_TK_ARG ) {
2017-11-06 17:46:50 +03:00
if ( partial ) {
vshCmdOpt * arg = vshMalloc ( ctl , sizeof ( vshCmdOpt ) ) ;
arg - > def = opt ;
arg - > data = tkdata ;
tkdata = NULL ;
arg - > next = NULL ;
if ( ! first )
first = arg ;
if ( last )
last - > next = arg ;
last = arg ;
} else {
vshError ( ctl ,
_ ( " expected syntax: --%s <%s> " ) ,
opt - > name ,
opt - > type = =
VSH_OT_INT ? _ ( " number " ) : _ ( " string " ) ) ;
}
2015-06-15 19:53:58 +03:00
goto syntaxError ;
}
if ( opt - > type ! = VSH_OT_ARGV )
2016-04-20 16:51:43 +03:00
opts_need_arg & = ~ ( 1ULL < < opt_index ) ;
2015-06-15 19:53:58 +03:00
} else {
tkdata = NULL ;
if ( optstr ) {
2017-11-06 17:46:50 +03:00
if ( ! partial )
vshError ( ctl , _ ( " invalid '=' after option --%s " ) ,
opt - > name ) ;
2015-06-15 19:53:58 +03:00
VIR_FREE ( optstr ) ;
goto syntaxError ;
}
}
} else if ( tkdata [ 0 ] = = ' - ' & & tkdata [ 1 ] = = ' - ' & &
tkdata [ 2 ] = = ' \0 ' ) {
2017-11-13 16:46:36 +03:00
VIR_FREE ( tkdata ) ;
2015-06-15 19:53:58 +03:00
data_only = true ;
continue ;
} else {
get_data :
/* Special case 'help' to ignore spurious data */
if ( ! ( opt = vshCmddefGetData ( cmd , & opts_need_arg ,
& opts_seen ) ) & &
STRNEQ ( cmd - > name , " help " ) ) {
2017-11-06 17:46:50 +03:00
if ( ! partial )
vshError ( ctl , _ ( " unexpected data '%s' " ) , tkdata ) ;
2015-06-15 19:53:58 +03:00
goto syntaxError ;
}
}
if ( opt ) {
/* save option */
vshCmdOpt * arg = vshMalloc ( ctl , sizeof ( vshCmdOpt ) ) ;
arg - > def = opt ;
arg - > data = tkdata ;
arg - > next = NULL ;
tkdata = NULL ;
if ( ! first )
first = arg ;
if ( last )
last - > next = arg ;
last = arg ;
2017-11-06 17:46:50 +03:00
if ( ! partial )
vshDebug ( ctl , VSH_ERR_INFO , " %s: %s(%s): %s \n " ,
cmd - > name ,
opt - > name ,
opt - > type ! = VSH_OT_BOOL ? _ ( " optdata " ) : _ ( " bool " ) ,
opt - > type ! = VSH_OT_BOOL ? arg - > data : _ ( " (none) " ) ) ;
2015-06-15 19:53:58 +03:00
}
}
/* command parsed -- allocate new struct for the command */
if ( cmd ) {
vshCmd * c = vshMalloc ( ctl , sizeof ( vshCmd ) ) ;
vshCmdOpt * tmpopt = first ;
/* if we encountered --help, replace parsed command with
* ' help < cmdname > ' */
for ( tmpopt = first ; tmpopt ; tmpopt = tmpopt - > next ) {
2016-10-10 16:01:18 +03:00
const vshCmdDef * help ;
2015-06-15 19:53:58 +03:00
if ( STRNEQ ( tmpopt - > def - > name , " help " ) )
continue ;
2016-10-10 16:01:18 +03:00
help = vshCmddefSearch ( " help " ) ;
2015-06-15 19:53:58 +03:00
vshCommandOptFree ( first ) ;
first = vshMalloc ( ctl , sizeof ( vshCmdOpt ) ) ;
first - > def = help - > opts ;
2019-10-18 18:24:02 +03:00
first - > data = g_strdup ( cmd - > name ) ;
2015-06-15 19:53:58 +03:00
first - > next = NULL ;
cmd = help ;
opts_required = 0 ;
opts_seen = 0 ;
break ;
}
c - > opts = first ;
c - > def = cmd ;
c - > next = NULL ;
2017-11-06 17:46:50 +03:00
first = NULL ;
2015-06-15 19:53:58 +03:00
2017-11-06 17:46:50 +03:00
if ( ! partial & &
vshCommandCheckOpts ( ctl , c , opts_required , opts_seen ) < 0 ) {
2015-06-15 19:53:58 +03:00
VIR_FREE ( c ) ;
goto syntaxError ;
}
2017-11-06 17:46:50 +03:00
if ( partial ) {
vshCommandFree ( * partial ) ;
* partial = c ;
} else {
if ( ! ctl - > cmd )
ctl - > cmd = c ;
if ( clast )
clast - > next = c ;
clast = c ;
}
2015-06-15 19:53:58 +03:00
}
if ( tk = = VSH_TK_END )
break ;
}
return true ;
syntaxError :
2017-11-06 17:46:50 +03:00
if ( partial ) {
vshCmd * tmp ;
tmp = vshMalloc ( ctl , sizeof ( * tmp ) ) ;
tmp - > opts = first ;
tmp - > def = cmd ;
* partial = tmp ;
} else {
vshCommandFree ( ctl - > cmd ) ;
ctl - > cmd = NULL ;
vshCommandOptFree ( first ) ;
}
2015-06-15 19:53:58 +03:00
VIR_FREE ( tkdata ) ;
return false ;
}
/* --------------------
* Command argv parsing
* - - - - - - - - - - - - - - - - - - - -
*/
static vshCommandToken ATTRIBUTE_NONNULL ( 2 ) ATTRIBUTE_NONNULL ( 3 )
2019-10-19 01:12:53 +03:00
vshCommandArgvGetArg ( vshControl * ctl G_GNUC_UNUSED ,
2019-10-19 01:12:53 +03:00
vshCommandParser * parser ,
char * * res ,
2019-10-14 15:44:29 +03:00
bool report G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
if ( parser - > arg_pos = = parser - > arg_end ) {
* res = NULL ;
return VSH_TK_END ;
}
2019-10-19 01:12:53 +03:00
* res = g_strdup ( * parser - > arg_pos ) ;
2015-06-15 19:53:58 +03:00
parser - > arg_pos + + ;
return VSH_TK_ARG ;
}
bool
vshCommandArgvParse ( vshControl * ctl , int nargs , char * * argv )
{
vshCommandParser parser ;
if ( nargs < = 0 )
return false ;
parser . arg_pos = argv ;
parser . arg_end = argv + nargs ;
parser . getNextArg = vshCommandArgvGetArg ;
2017-11-06 17:46:50 +03:00
return vshCommandParse ( ctl , & parser , NULL ) ;
2015-06-15 19:53:58 +03:00
}
/* ----------------------
* Command string parsing
* - - - - - - - - - - - - - - - - - - - - - -
*/
static vshCommandToken ATTRIBUTE_NONNULL ( 2 ) ATTRIBUTE_NONNULL ( 3 )
2016-07-08 15:56:04 +03:00
vshCommandStringGetArg ( vshControl * ctl , vshCommandParser * parser , char * * res ,
bool report )
2015-06-15 19:53:58 +03:00
{
bool single_quote = false ;
bool double_quote = false ;
int sz = 0 ;
char * p = parser - > pos ;
2019-10-18 18:24:02 +03:00
char * q = g_strdup ( p ) ;
2015-06-15 19:53:58 +03:00
* res = q ;
2019-02-26 21:49:25 +03:00
while ( * p = = ' ' | | * p = = ' \t ' | | ( * p = = ' \\ ' & & p [ 1 ] = = ' \n ' ) )
p + = 1 + ( * p = = ' \\ ' ) ;
2015-06-15 19:53:58 +03:00
if ( * p = = ' \0 ' )
return VSH_TK_END ;
2019-02-21 21:36:32 +03:00
if ( * p = = ' ; ' | | * p = = ' \n ' ) {
2015-06-15 19:53:58 +03:00
parser - > pos = + + p ; /* = \0 or begin of next command */
return VSH_TK_SUBCMD_END ;
}
2019-03-22 22:02:39 +03:00
if ( * p = = ' # ' ) { /* Argument starting with # is comment to end of line */
while ( * p & & * p ! = ' \n ' )
p + + ;
parser - > pos = p + ! ! * p ;
return VSH_TK_SUBCMD_END ;
}
2015-06-15 19:53:58 +03:00
while ( * p ) {
/* end of token is blank space or ';' */
if ( ! double_quote & & ! single_quote & &
2019-02-21 21:36:32 +03:00
( * p = = ' ' | | * p = = ' \t ' | | * p = = ' ; ' | | * p = = ' \n ' ) )
2015-06-15 19:53:58 +03:00
break ;
if ( ! double_quote & & * p = = ' \' ' ) { /* single quote */
single_quote = ! single_quote ;
p + + ;
continue ;
} else if ( ! single_quote & & * p = = ' \\ ' ) { /* escape */
/*
2019-02-26 21:49:25 +03:00
* The same as in shell , a \ in " " is an escaper ,
2015-06-15 19:53:58 +03:00
* but a \ in ' ' is not an escaper .
*/
p + + ;
if ( * p = = ' \0 ' ) {
2016-07-08 15:56:04 +03:00
if ( report )
vshError ( ctl , " %s " , _ ( " dangling \\ " ) ) ;
2015-06-15 19:53:58 +03:00
return VSH_TK_ERROR ;
2019-02-26 21:49:25 +03:00
} else if ( * p = = ' \n ' ) {
/* Elide backslash-newline entirely */
p + + ;
continue ;
2015-06-15 19:53:58 +03:00
}
} else if ( ! single_quote & & * p = = ' " ' ) { /* double quote */
double_quote = ! double_quote ;
p + + ;
continue ;
}
* q + + = * p + + ;
sz + + ;
}
if ( double_quote ) {
2016-07-08 15:56:04 +03:00
if ( report )
vshError ( ctl , " %s " , _ ( " missing \" " ) ) ;
2015-06-15 19:53:58 +03:00
return VSH_TK_ERROR ;
}
* q = ' \0 ' ;
parser - > pos = p ;
return VSH_TK_ARG ;
}
bool
2017-11-06 17:46:50 +03:00
vshCommandStringParse ( vshControl * ctl , char * cmdstr , vshCmd * * partial )
2015-06-15 19:53:58 +03:00
{
vshCommandParser parser ;
if ( cmdstr = = NULL | | * cmdstr = = ' \0 ' )
return false ;
parser . pos = cmdstr ;
parser . getNextArg = vshCommandStringGetArg ;
2017-11-06 17:46:50 +03:00
return vshCommandParse ( ctl , & parser , partial ) ;
2015-06-15 19:53:58 +03:00
}
/**
* virshCommandOptTimeoutToMs :
* @ ctl virsh control structure
* @ cmd command reference
* @ timeout result
*
* Parse an optional - - timeout parameter in seconds , but store the
* value of the timeout in milliseconds .
* See vshCommandOptInt ( )
*/
int
vshCommandOptTimeoutToMs ( vshControl * ctl , const vshCmd * cmd , int * timeout )
{
int ret ;
unsigned int utimeout ;
if ( ( ret = vshCommandOptUInt ( ctl , cmd , " timeout " , & utimeout ) ) < = 0 )
return ret ;
/* Ensure that the timeout is not zero and that we can convert
* it from seconds to milliseconds without overflowing . */
if ( utimeout = = 0 | | utimeout > INT_MAX / 1000 ) {
vshError ( ctl ,
_ ( " Numeric value '%u' for <%s> option is malformed or out of range " ) ,
utimeout ,
" timeout " ) ;
ret = - 1 ;
} else {
* timeout = ( ( int ) utimeout ) * 1000 ;
}
return ret ;
}
/* ---------------
* Misc utils
* - - - - - - - - - - - - - - -
*/
/* Return a non-NULL string representation of a typed parameter; exit
* if we are out of memory . */
char *
vshGetTypedParamValue ( vshControl * ctl , virTypedParameterPtr item )
{
char * str = NULL ;
switch ( item - > type ) {
case VIR_TYPED_PARAM_INT :
2019-10-22 16:26:14 +03:00
str = g_strdup_printf ( " %d " , item - > value . i ) ;
2015-06-15 19:53:58 +03:00
break ;
case VIR_TYPED_PARAM_UINT :
2019-10-22 16:26:14 +03:00
str = g_strdup_printf ( " %u " , item - > value . ui ) ;
2015-06-15 19:53:58 +03:00
break ;
case VIR_TYPED_PARAM_LLONG :
2019-10-22 16:26:14 +03:00
str = g_strdup_printf ( " %lld " , item - > value . l ) ;
2015-06-15 19:53:58 +03:00
break ;
case VIR_TYPED_PARAM_ULLONG :
2019-10-22 16:26:14 +03:00
str = g_strdup_printf ( " %llu " , item - > value . ul ) ;
2015-06-15 19:53:58 +03:00
break ;
case VIR_TYPED_PARAM_DOUBLE :
2019-10-22 16:26:14 +03:00
str = g_strdup_printf ( " %f " , item - > value . d ) ;
2015-06-15 19:53:58 +03:00
break ;
case VIR_TYPED_PARAM_BOOLEAN :
2019-10-18 18:24:02 +03:00
str = g_strdup ( item - > value . b ? _ ( " yes " ) : _ ( " no " ) ) ;
2015-06-15 19:53:58 +03:00
break ;
case VIR_TYPED_PARAM_STRING :
2019-10-18 18:24:02 +03:00
str = g_strdup ( item - > value . s ) ;
2015-06-15 19:53:58 +03:00
break ;
default :
vshError ( ctl , _ ( " unimplemented parameter type %d " ) , item - > type ) ;
}
2019-10-22 16:26:14 +03:00
if ( ! str ) {
2015-06-15 19:53:58 +03:00
vshError ( ctl , " %s " , _ ( " Out of memory " ) ) ;
exit ( EXIT_FAILURE ) ;
}
return str ;
}
void
vshDebug ( vshControl * ctl , int level , const char * format , . . . )
{
va_list ap ;
char * str ;
/* Aligning log levels to that of libvirt.
* Traces with levels > = user - specified - level
* gets logged into file
*/
if ( level < ctl - > debug )
return ;
va_start ( ap , format ) ;
vshOutputLogFile ( ctl , level , format , ap ) ;
va_end ( ap ) ;
va_start ( ap , format ) ;
2019-10-22 15:11:15 +03:00
str = g_strdup_vprintf ( format , ap ) ;
2015-06-15 19:53:58 +03:00
va_end ( ap ) ;
fputs ( str , stdout ) ;
VIR_FREE ( str ) ;
}
void
vshPrintExtra ( vshControl * ctl , const char * format , . . . )
{
va_list ap ;
char * str ;
if ( ctl & & ctl - > quiet )
return ;
va_start ( ap , format ) ;
2019-10-22 15:11:15 +03:00
str = g_strdup_vprintf ( format , ap ) ;
2015-06-15 19:53:58 +03:00
va_end ( ap ) ;
fputs ( str , stdout ) ;
VIR_FREE ( str ) ;
}
2016-02-12 16:09:02 +03:00
void
2019-10-14 15:44:29 +03:00
vshPrint ( vshControl * ctl G_GNUC_UNUSED , const char * format , . . . )
2016-02-12 16:09:02 +03:00
{
va_list ap ;
char * str ;
va_start ( ap , format ) ;
2019-10-22 15:11:15 +03:00
str = g_strdup_vprintf ( format , ap ) ;
2016-02-12 16:09:02 +03:00
va_end ( ap ) ;
fputs ( str , stdout ) ;
VIR_FREE ( str ) ;
}
2015-06-15 19:53:58 +03:00
bool
2019-10-14 15:44:29 +03:00
vshTTYIsInterruptCharacter ( vshControl * ctl G_GNUC_UNUSED ,
const char chr G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
# ifndef WIN32
if ( ctl - > istty & &
ctl - > termattr . c_cc [ VINTR ] = = chr )
return true ;
# endif
return false ;
}
bool
vshTTYAvailable ( vshControl * ctl )
{
return ctl - > istty ;
}
int
2019-10-14 15:44:29 +03:00
vshTTYDisableInterrupt ( vshControl * ctl G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
# ifndef WIN32
struct termios termset = ctl - > termattr ;
if ( ! ctl - > istty )
return - 1 ;
/* check if we need to set the terminal */
if ( termset . c_cc [ VINTR ] = = _POSIX_VDISABLE )
return 0 ;
termset . c_cc [ VINTR ] = _POSIX_VDISABLE ;
termset . c_lflag & = ~ ICANON ;
if ( tcsetattr ( STDIN_FILENO , TCSANOW , & termset ) < 0 )
return - 1 ;
# endif
return 0 ;
}
int
2019-10-14 15:44:29 +03:00
vshTTYRestore ( vshControl * ctl G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
# ifndef WIN32
if ( ! ctl - > istty )
return 0 ;
if ( tcsetattr ( STDIN_FILENO , TCSAFLUSH , & ctl - > termattr ) < 0 )
return - 1 ;
# endif
return 0 ;
}
# if !defined(WIN32) && !defined(HAVE_CFMAKERAW)
/* provide fallback in case cfmakeraw isn't available */
static void
cfmakeraw ( struct termios * attr )
{
attr - > c_iflag & = ~ ( IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON ) ;
attr - > c_oflag & = ~ OPOST ;
attr - > c_lflag & = ~ ( ECHO | ECHONL | ICANON | ISIG | IEXTEN ) ;
attr - > c_cflag & = ~ ( CSIZE | PARENB ) ;
attr - > c_cflag | = CS8 ;
}
# endif /* !WIN32 && !HAVE_CFMAKERAW */
int
2019-10-14 15:44:29 +03:00
vshTTYMakeRaw ( vshControl * ctl G_GNUC_UNUSED ,
bool report_errors G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
# ifndef WIN32
struct termios rawattr = ctl - > termattr ;
char ebuf [ 1024 ] ;
if ( ! ctl - > istty ) {
if ( report_errors ) {
vshError ( ctl , " %s " ,
_ ( " unable to make terminal raw: console isn't a tty " ) ) ;
}
return - 1 ;
}
cfmakeraw ( & rawattr ) ;
if ( tcsetattr ( STDIN_FILENO , TCSAFLUSH , & rawattr ) < 0 ) {
if ( report_errors )
vshError ( ctl , _ ( " unable to set tty attributes: %s " ) ,
virStrerror ( errno , ebuf , sizeof ( ebuf ) ) ) ;
return - 1 ;
}
# endif
return 0 ;
}
void
vshError ( vshControl * ctl , const char * format , . . . )
{
va_list ap ;
char * str ;
if ( ctl ! = NULL ) {
va_start ( ap , format ) ;
vshOutputLogFile ( ctl , VSH_ERR_ERROR , format , ap ) ;
va_end ( ap ) ;
}
/* Most output is to stdout, but if someone ran virsh 2>&1, then
* printing to stderr will not interleave correctly with stdout
* unless we flush between every transition between streams . */
fflush ( stdout ) ;
fputs ( _ ( " error: " ) , stderr ) ;
va_start ( ap , format ) ;
2019-10-22 15:11:15 +03:00
str = g_strdup_vprintf ( format , ap ) ;
2015-06-15 19:53:58 +03:00
va_end ( ap ) ;
fprintf ( stderr , " %s \n " , NULLSTR ( str ) ) ;
fflush ( stderr ) ;
VIR_FREE ( str ) ;
}
void
vshEventLoop ( void * opaque )
{
vshControl * ctl = opaque ;
while ( 1 ) {
bool quit ;
virMutexLock ( & ctl - > lock ) ;
quit = ctl - > quit ;
virMutexUnlock ( & ctl - > lock ) ;
if ( quit )
break ;
if ( virEventRunDefaultImpl ( ) < 0 )
vshReportError ( ctl ) ;
}
}
/*
* Helpers for waiting for a libvirt event .
*/
/* We want to use SIGINT to cancel a wait; but as signal handlers
* don ' t have an opaque argument , we have to use static storage . */
static int vshEventFd = - 1 ;
static struct sigaction vshEventOldAction ;
/* Signal handler installed in vshEventStart, removed in vshEventCleanup. */
static void
2019-10-14 15:44:29 +03:00
vshEventInt ( int sig G_GNUC_UNUSED ,
siginfo_t * siginfo G_GNUC_UNUSED ,
void * context G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
char reason = VSH_EVENT_INTERRUPT ;
if ( vshEventFd > = 0 )
ignore_value ( safewrite ( vshEventFd , & reason , 1 ) ) ;
}
/* Event loop handler used to limit length of waiting for any other event. */
void
2019-10-14 15:44:29 +03:00
vshEventTimeout ( int timer G_GNUC_UNUSED ,
2015-06-15 19:53:58 +03:00
void * opaque )
{
vshControl * ctl = opaque ;
char reason = VSH_EVENT_TIMEOUT ;
if ( ctl - > eventPipe [ 1 ] > = 0 )
ignore_value ( safewrite ( ctl - > eventPipe [ 1 ] , & reason , 1 ) ) ;
}
/**
* vshEventStart :
* @ ctl vsh command struct
* @ timeout_ms max wait time in milliseconds , or 0 for indefinite
*
* Set up a wait for a libvirt event . The wait can be canceled by
* SIGINT or by calling vshEventDone ( ) in your event handler . If
* @ timeout_ms is positive , the wait will also end if the timeout
* expires . Call vshEventWait ( ) to block the main thread ( the event
* handler runs in the event loop thread ) . When done ( including if
* there was an error registering for an event ) , use vshEventCleanup ( )
* to quit waiting . Returns 0 on success , - 1 on failure . */
int
vshEventStart ( vshControl * ctl , int timeout_ms )
{
struct sigaction action ;
assert ( ctl - > eventPipe [ 0 ] = = - 1 & & ctl - > eventPipe [ 1 ] = = - 1 & &
vshEventFd = = - 1 & & ctl - > eventTimerId > = 0 ) ;
if ( pipe2 ( ctl - > eventPipe , O_CLOEXEC ) < 0 ) {
char ebuf [ 1024 ] ;
vshError ( ctl , _ ( " failed to create pipe: %s " ) ,
virStrerror ( errno , ebuf , sizeof ( ebuf ) ) ) ;
return - 1 ;
}
vshEventFd = ctl - > eventPipe [ 1 ] ;
action . sa_sigaction = vshEventInt ;
action . sa_flags = SA_SIGINFO ;
sigemptyset ( & action . sa_mask ) ;
sigaction ( SIGINT , & action , & vshEventOldAction ) ;
if ( timeout_ms )
virEventUpdateTimeout ( ctl - > eventTimerId , timeout_ms ) ;
return 0 ;
}
/**
* vshEventDone :
* @ ctl vsh command struct
*
* Call this from an event callback to let the main thread quit
* blocking on further events .
*/
void
vshEventDone ( vshControl * ctl )
{
char reason = VSH_EVENT_DONE ;
if ( ctl - > eventPipe [ 1 ] > = 0 )
ignore_value ( safewrite ( ctl - > eventPipe [ 1 ] , & reason , 1 ) ) ;
}
/**
* vshEventWait :
* @ ctl vsh command struct
*
* Call this in the main thread after calling vshEventStart ( ) then
* registering for one or more events . This call will block until
* SIGINT , the timeout registered at the start , or until one of your
* event handlers calls vshEventDone ( ) . Returns an enum VSH_EVENT_ *
* stating how the wait concluded , or - 1 on error .
*/
int
vshEventWait ( vshControl * ctl )
{
char buf ;
int rv ;
assert ( ctl - > eventPipe [ 0 ] > = 0 ) ;
while ( ( rv = read ( ctl - > eventPipe [ 0 ] , & buf , 1 ) ) < 0 & & errno = = EINTR ) ;
if ( rv ! = 1 ) {
char ebuf [ 1024 ] ;
if ( ! rv )
errno = EPIPE ;
vshError ( ctl , _ ( " failed to determine loop exit status: %s " ) ,
virStrerror ( errno , ebuf , sizeof ( ebuf ) ) ) ;
return - 1 ;
}
return buf ;
}
/**
* vshEventCleanup :
* @ ctl vsh control struct
*
* Call at the end of any function that has used vshEventStart ( ) , to
* tear down any remaining SIGINT or timeout handlers .
*/
void
vshEventCleanup ( vshControl * ctl )
{
if ( vshEventFd > = 0 ) {
sigaction ( SIGINT , & vshEventOldAction , NULL ) ;
vshEventFd = - 1 ;
}
VIR_FORCE_CLOSE ( ctl - > eventPipe [ 0 ] ) ;
VIR_FORCE_CLOSE ( ctl - > eventPipe [ 1 ] ) ;
virEventUpdateTimeout ( ctl - > eventTimerId , - 1 ) ;
}
# define LOGFILE_FLAGS (O_WRONLY | O_APPEND | O_CREAT | O_SYNC)
/**
* vshOpenLogFile :
*
* Open log file .
*/
void
vshOpenLogFile ( vshControl * ctl )
{
if ( ctl - > logfile = = NULL )
return ;
if ( ( ctl - > log_fd = open ( ctl - > logfile , LOGFILE_FLAGS , FILE_MODE ) ) < 0 ) {
vshError ( ctl , " %s " ,
_ ( " failed to open the log file. check the log file path " ) ) ;
exit ( EXIT_FAILURE ) ;
}
}
/**
* vshOutputLogFile :
*
* Outputting an error to log file .
*/
void
vshOutputLogFile ( vshControl * ctl , int log_level , const char * msg_format ,
va_list ap )
{
virBuffer buf = VIR_BUFFER_INITIALIZER ;
char * str = NULL ;
size_t len ;
const char * lvl = " " ;
time_t stTime ;
struct tm stTm ;
if ( ctl - > log_fd = = - 1 )
return ;
/**
* create log format
*
* [ YYYY . MM . DD HH : MM : SS SIGNATURE PID ] LOG_LEVEL message
*/
time ( & stTime ) ;
localtime_r ( & stTime , & stTm ) ;
virBufferAsprintf ( & buf , " [%d.%02d.%02d %02d:%02d:%02d %s %d] " ,
( 1900 + stTm . tm_year ) ,
( 1 + stTm . tm_mon ) ,
stTm . tm_mday ,
stTm . tm_hour ,
stTm . tm_min ,
stTm . tm_sec ,
ctl - > progname ,
( int ) getpid ( ) ) ;
switch ( log_level ) {
case VSH_ERR_DEBUG :
lvl = LVL_DEBUG ;
break ;
case VSH_ERR_INFO :
lvl = LVL_INFO ;
break ;
case VSH_ERR_NOTICE :
lvl = LVL_INFO ;
break ;
case VSH_ERR_WARNING :
lvl = LVL_WARNING ;
break ;
case VSH_ERR_ERROR :
lvl = LVL_ERROR ;
break ;
default :
lvl = LVL_DEBUG ;
break ;
}
virBufferAsprintf ( & buf , " %s " , lvl ) ;
virBufferVasprintf ( & buf , msg_format , ap ) ;
2016-02-12 15:49:18 +03:00
virBufferTrim ( & buf , " \n " , - 1 ) ;
2015-06-15 19:53:58 +03:00
virBufferAddChar ( & buf , ' \n ' ) ;
str = virBufferContentAndReset ( & buf ) ;
len = strlen ( str ) ;
/* write log */
if ( safewrite ( ctl - > log_fd , str , len ) < 0 )
goto error ;
VIR_FREE ( str ) ;
return ;
error :
vshCloseLogFile ( ctl ) ;
vshError ( ctl , " %s " , _ ( " failed to write the log file " ) ) ;
virBufferFreeAndReset ( & buf ) ;
VIR_FREE ( str ) ;
}
/**
* vshCloseLogFile :
*
* Close log file .
*/
void
vshCloseLogFile ( vshControl * ctl )
{
char ebuf [ 1024 ] ;
/* log file close */
if ( VIR_CLOSE ( ctl - > log_fd ) < 0 ) {
vshError ( ctl , _ ( " %s: failed to write log file: %s " ) ,
ctl - > logfile ? ctl - > logfile : " ? " ,
virStrerror ( errno , ebuf , sizeof ( ebuf ) ) ) ;
}
if ( ctl - > logfile ) {
VIR_FREE ( ctl - > logfile ) ;
ctl - > logfile = NULL ;
}
}
# ifndef WIN32
static void
vshPrintRaw ( vshControl * ctl , . . . )
{
va_list ap ;
char * key ;
va_start ( ap , ctl ) ;
while ( ( key = va_arg ( ap , char * ) ) ! = NULL )
vshPrint ( ctl , " %s \r \n " , key ) ;
va_end ( ap ) ;
}
/**
* vshAskReedit :
* @ msg : Question to ask user
*
* Ask user if he wants to return to previously
* edited file .
*
* Returns ' y ' if he wants to
* ' n ' if he doesn ' t want to
* ' i ' if he wants to try defining it again while ignoring validation
* ' f ' if he forcibly wants to
* - 1 on error
* 0 otherwise
*/
int
vshAskReedit ( vshControl * ctl , const char * msg , bool relax_avail )
{
int c = - 1 ;
if ( ! isatty ( STDIN_FILENO ) )
return - 1 ;
vshReportError ( ctl ) ;
if ( vshTTYMakeRaw ( ctl , false ) < 0 )
return - 1 ;
while ( true ) {
vshPrint ( ctl , " \r %s %s %s: " , msg , _ ( " Try again? " ) ,
relax_avail ? " [y,n,i,f,?] " : " [y,n,f,?] " ) ;
2019-11-18 17:15:31 +03:00
c = g_ascii_tolower ( getchar ( ) ) ;
2015-06-15 19:53:58 +03:00
if ( c = = ' ? ' ) {
vshPrintRaw ( ctl ,
" " ,
_ ( " y - yes, start editor again " ) ,
_ ( " n - no, throw away my changes " ) ,
NULL ) ;
if ( relax_avail ) {
vshPrintRaw ( ctl ,
_ ( " i - turn off validation and try to redefine "
" again " ) ,
NULL ) ;
}
vshPrintRaw ( ctl ,
_ ( " f - force, try to redefine again " ) ,
_ ( " ? - print this help " ) ,
NULL ) ;
continue ;
} else if ( c = = ' y ' | | c = = ' n ' | | c = = ' f ' | |
( relax_avail & & c = = ' i ' ) ) {
break ;
}
}
vshTTYRestore ( ctl ) ;
vshPrint ( ctl , " \r \n " ) ;
return c ;
}
# else /* WIN32 */
int
vshAskReedit ( vshControl * ctl ,
2019-10-14 15:44:29 +03:00
const char * msg G_GNUC_UNUSED ,
bool relax_avail G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
vshDebug ( ctl , VSH_ERR_WARNING , " %s " , _ ( " This function is not "
" supported on WIN32 platform " ) ) ;
return 0 ;
}
# endif /* WIN32 */
/* Common code for the edit / net-edit / pool-edit functions which follow. */
char *
vshEditWriteToTempFile ( vshControl * ctl , const char * doc )
{
char * ret ;
const char * tmpdir ;
int fd ;
char ebuf [ 1024 ] ;
2019-08-01 15:35:56 +03:00
tmpdir = getenv ( " TMPDIR " ) ;
2015-06-15 19:53:58 +03:00
if ( ! tmpdir ) tmpdir = " /tmp " ;
2019-10-22 16:26:14 +03:00
ret = g_strdup_printf ( " %s/virshXXXXXX.xml " , tmpdir ) ;
2019-11-18 19:45:54 +03:00
fd = g_mkstemp_full ( ret , O_RDWR | O_CLOEXEC , S_IRUSR | S_IWUSR ) ;
2015-06-15 19:53:58 +03:00
if ( fd = = - 1 ) {
2019-11-14 00:30:26 +03:00
vshError ( ctl , _ ( " g_mkstemp_full: failed to create temporary file: %s " ) ,
2015-06-15 19:53:58 +03:00
virStrerror ( errno , ebuf , sizeof ( ebuf ) ) ) ;
VIR_FREE ( ret ) ;
return NULL ;
}
if ( safewrite ( fd , doc , strlen ( doc ) ) = = - 1 ) {
vshError ( ctl , _ ( " write: %s: failed to write to temporary file: %s " ) ,
ret , virStrerror ( errno , ebuf , sizeof ( ebuf ) ) ) ;
VIR_FORCE_CLOSE ( fd ) ;
unlink ( ret ) ;
VIR_FREE ( ret ) ;
return NULL ;
}
if ( VIR_CLOSE ( fd ) < 0 ) {
vshError ( ctl , _ ( " close: %s: failed to write or close temporary file: %s " ) ,
ret , virStrerror ( errno , ebuf , sizeof ( ebuf ) ) ) ;
unlink ( ret ) ;
VIR_FREE ( ret ) ;
return NULL ;
}
/* Temporary filename: caller frees. */
return ret ;
}
/* Characters permitted in $EDITOR environment variable and temp filename. */
# define ACCEPTED_CHARS \
" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/_.:@ "
int
vshEditFile ( vshControl * ctl , const char * filename )
{
const char * editor ;
virCommandPtr cmd ;
int ret = - 1 ;
int outfd = STDOUT_FILENO ;
int errfd = STDERR_FILENO ;
2019-08-01 15:35:56 +03:00
editor = getenv ( " VISUAL " ) ;
2015-06-15 19:53:58 +03:00
if ( ! editor )
2019-08-01 15:35:56 +03:00
editor = getenv ( " EDITOR " ) ;
2015-06-15 19:53:58 +03:00
if ( ! editor )
editor = DEFAULT_EDITOR ;
/* Check that filename doesn't contain shell meta-characters, and
* if it does , refuse to run . Follow the Unix conventions for
* EDITOR : the user can intentionally specify command options , so
* we don ' t protect any shell metacharacters there . Lots more
* than virsh will misbehave if EDITOR has bogus contents ( which
* is why sudo scrubs it by default ) . Conversely , if the editor
* is safe , we can run it directly rather than wasting a shell .
*/
if ( strspn ( editor , ACCEPTED_CHARS ) ! = strlen ( editor ) ) {
if ( strspn ( filename , ACCEPTED_CHARS ) ! = strlen ( filename ) ) {
vshError ( ctl ,
_ ( " %s: temporary filename contains shell meta or other "
" unacceptable characters (is $TMPDIR wrong?) " ) ,
filename ) ;
return - 1 ;
}
cmd = virCommandNewArgList ( " sh " , " -c " , NULL ) ;
virCommandAddArgFormat ( cmd , " %s %s " , editor , filename ) ;
} else {
cmd = virCommandNewArgList ( editor , filename , NULL ) ;
}
virCommandSetInputFD ( cmd , STDIN_FILENO ) ;
virCommandSetOutputFD ( cmd , & outfd ) ;
virCommandSetErrorFD ( cmd , & errfd ) ;
if ( virCommandRunAsync ( cmd , NULL ) < 0 | |
virCommandWait ( cmd , NULL ) < 0 ) {
vshReportError ( ctl ) ;
goto cleanup ;
}
ret = 0 ;
cleanup :
virCommandFree ( cmd ) ;
return ret ;
}
char *
vshEditReadBackFile ( vshControl * ctl , const char * filename )
{
char * ret ;
char ebuf [ 1024 ] ;
if ( virFileReadAll ( filename , VSH_MAX_XML_FILE , & ret ) = = - 1 ) {
vshError ( ctl ,
_ ( " %s: failed to read temporary file: %s " ) ,
filename , virStrerror ( errno , ebuf , sizeof ( ebuf ) ) ) ;
return NULL ;
}
return ret ;
}
/* Tree listing helpers. */
static int
vshTreePrintInternal ( vshControl * ctl ,
vshTreeLookup lookup ,
void * opaque ,
int num_devices ,
int devid ,
int lastdev ,
bool root ,
virBufferPtr indent )
{
size_t i ;
int nextlastdev = - 1 ;
const char * dev = ( lookup ) ( devid , false , opaque ) ;
/* Print this device, with indent if not at root */
vshPrint ( ctl , " %s%s%s \n " , virBufferCurrentContent ( indent ) ,
root ? " " : " +- " , dev ) ;
/* Update indent to show '|' or ' ' for child devices */
if ( ! root ) {
virBufferAddChar ( indent , devid = = lastdev ? ' ' : ' | ' ) ;
virBufferAddChar ( indent , ' ' ) ;
}
/* Determine the index of the last child device */
for ( i = 0 ; i < num_devices ; i + + ) {
const char * parent = ( lookup ) ( i , true , opaque ) ;
if ( parent & & STREQ ( parent , dev ) )
nextlastdev = i ;
}
/* If there is a child device, then print another blank line */
if ( nextlastdev ! = - 1 )
vshPrint ( ctl , " %s | \n " , virBufferCurrentContent ( indent ) ) ;
/* Finally print all children */
virBufferAddLit ( indent , " " ) ;
for ( i = 0 ; i < num_devices ; i + + ) {
const char * parent = ( lookup ) ( i , true , opaque ) ;
if ( parent & & STREQ ( parent , dev ) & &
vshTreePrintInternal ( ctl , lookup , opaque ,
num_devices , i , nextlastdev ,
false , indent ) < 0 )
2019-10-21 21:19:09 +03:00
return - 1 ;
2015-06-15 19:53:58 +03:00
}
virBufferTrim ( indent , " " , - 1 ) ;
/* If there was no child device, and we're the last in
* a list of devices , then print another blank line */
if ( nextlastdev = = - 1 & & devid = = lastdev )
vshPrint ( ctl , " %s \n " , virBufferCurrentContent ( indent ) ) ;
if ( ! root )
virBufferTrim ( indent , NULL , 2 ) ;
2019-10-21 21:19:09 +03:00
return 0 ;
2015-06-15 19:53:58 +03:00
}
int
vshTreePrint ( vshControl * ctl , vshTreeLookup lookup , void * opaque ,
int num_devices , int devid )
{
int ret ;
virBuffer indent = VIR_BUFFER_INITIALIZER ;
ret = vshTreePrintInternal ( ctl , lookup , opaque , num_devices ,
devid , devid , true , & indent ) ;
if ( ret < 0 )
vshError ( ctl , " %s " , _ ( " Failed to complete tree listing " ) ) ;
virBufferFreeAndReset ( & indent ) ;
return ret ;
}
# if WITH_READLINE
/* -----------------
* Readline stuff
* - - - - - - - - - - - - - - - - -
*/
2017-11-21 19:23:11 +03:00
/**
* vshReadlineCommandGenerator :
* @ text : optional command prefix
*
* Generator function for command completion .
*
* Returns a string list of commands with @ text prefix ,
* NULL if there ' s no such command .
2015-06-15 19:53:58 +03:00
*/
2017-11-21 19:23:11 +03:00
static char * *
vshReadlineCommandGenerator ( const char * text )
2015-06-15 19:53:58 +03:00
{
2017-11-21 19:23:11 +03:00
size_t grp_list_index = 0 , cmd_list_index = 0 ;
size_t len = strlen ( text ) ;
2015-06-15 19:53:58 +03:00
const char * name ;
const vshCmdGrp * grp ;
const vshCmdDef * cmds ;
2017-11-21 19:23:11 +03:00
size_t ret_size = 0 ;
char * * ret = NULL ;
2015-06-15 19:53:58 +03:00
grp = cmdGroups ;
/* Return the next name which partially matches from the
* command list .
*/
while ( grp [ grp_list_index ] . name ) {
cmds = grp [ grp_list_index ] . commands ;
if ( cmds [ cmd_list_index ] . name ) {
while ( ( name = cmds [ cmd_list_index ] . name ) ) {
2016-09-07 18:30:48 +03:00
if ( cmds [ cmd_list_index + + ] . flags & VSH_CMD_FLAG_ALIAS )
continue ;
2015-06-15 19:53:58 +03:00
2017-11-21 19:23:11 +03:00
if ( STREQLEN ( name , text , len ) ) {
if ( VIR_REALLOC_N ( ret , ret_size + 2 ) < 0 ) {
virStringListFree ( ret ) ;
return NULL ;
}
2019-10-18 18:24:02 +03:00
ret [ ret_size ] = g_strdup ( name ) ;
2017-11-21 19:23:11 +03:00
ret_size + + ;
/* Terminate the string list properly. */
ret [ ret_size ] = NULL ;
}
2015-06-15 19:53:58 +03:00
}
} else {
cmd_list_index = 0 ;
grp_list_index + + ;
}
}
2017-11-21 19:23:11 +03:00
return ret ;
2015-06-15 19:53:58 +03:00
}
2017-11-21 19:23:11 +03:00
static char * *
2018-01-12 19:05:33 +03:00
vshReadlineOptionsGenerator ( const char * text ,
const vshCmdDef * cmd ,
vshCmd * last )
2015-06-15 19:53:58 +03:00
{
2017-11-21 19:23:11 +03:00
size_t list_index = 0 ;
size_t len = strlen ( text ) ;
2015-06-15 19:53:58 +03:00
const char * name ;
2017-11-21 19:23:11 +03:00
size_t ret_size = 0 ;
char * * ret = NULL ;
2015-06-15 19:53:58 +03:00
if ( ! cmd )
return NULL ;
if ( ! cmd - > opts )
return NULL ;
while ( ( name = cmd - > opts [ list_index ] . name ) ) {
2018-01-12 19:05:33 +03:00
bool exists = false ;
vshCmdOpt * opt = last - > opts ;
2017-11-21 19:23:11 +03:00
size_t name_len ;
2015-06-15 19:53:58 +03:00
list_index + + ;
if ( len > 2 ) {
2016-07-08 15:56:05 +03:00
/* provide auto-complete only when the text starts with -- */
if ( STRNEQLEN ( text , " -- " , 2 ) )
return NULL ;
2015-06-15 19:53:58 +03:00
if ( STRNEQLEN ( name , text + 2 , len - 2 ) )
continue ;
2016-07-08 15:56:05 +03:00
} else if ( STRNEQLEN ( text , " -- " , len ) ) {
return NULL ;
2015-06-15 19:53:58 +03:00
}
2017-11-21 19:23:11 +03:00
2018-01-12 19:05:33 +03:00
while ( opt ) {
2018-05-08 17:20:37 +03:00
if ( STREQ ( opt - > def - > name , name ) & & opt - > def - > type ! = VSH_OT_ARGV ) {
2018-01-12 19:05:33 +03:00
exists = true ;
break ;
}
opt = opt - > next ;
}
if ( exists )
continue ;
2017-11-21 19:23:11 +03:00
if ( VIR_REALLOC_N ( ret , ret_size + 2 ) < 0 ) {
virStringListFree ( ret ) ;
return NULL ;
}
name_len = strlen ( name ) ;
ret [ ret_size ] = vshMalloc ( NULL , name_len + 3 ) ;
2019-11-13 16:53:42 +03:00
g_snprintf ( ret [ ret_size ] , name_len + 3 , " --%s " , name ) ;
2017-11-21 19:23:11 +03:00
ret_size + + ;
/* Terminate the string list properly. */
ret [ ret_size ] = NULL ;
2015-06-15 19:53:58 +03:00
}
2017-11-21 19:23:11 +03:00
return ret ;
2015-06-15 19:53:58 +03:00
}
2017-11-21 19:45:50 +03:00
static const vshCmdOptDef *
vshReadlineCommandFindOpt ( const vshCmd * partial ,
const char * text )
{
const vshCmd * tmp = partial ;
while ( tmp & & tmp - > next ) {
if ( tmp - > def = = tmp - > next - > def & &
! tmp - > next - > opts )
break ;
tmp = tmp - > next ;
}
if ( tmp & & tmp - > opts ) {
const vshCmdOpt * opt = tmp - > opts ;
while ( opt ) {
if ( STREQ_NULLABLE ( opt - > data , text ) | |
STREQ_NULLABLE ( opt - > data , " " ) )
return opt - > def ;
opt = opt - > next ;
}
}
return NULL ;
}
2017-12-28 14:26:41 +03:00
static int
vshCompleterFilter ( char * * * list ,
const char * text )
{
char * * newList = NULL ;
size_t newList_len = 0 ;
size_t list_len ;
size_t i ;
if ( ! list | | ! * list )
return - 1 ;
list_len = virStringListLength ( ( const char * * ) * list ) ;
if ( VIR_ALLOC_N ( newList , list_len + 1 ) < 0 )
return - 1 ;
for ( i = 0 ; i < list_len ; i + + ) {
if ( ! STRPREFIX ( ( * list ) [ i ] , text ) ) {
VIR_FREE ( ( * list ) [ i ] ) ;
continue ;
}
2019-10-16 14:43:36 +03:00
newList [ newList_len ] = g_steal_pointer ( & ( * list ) [ i ] ) ;
2017-12-28 14:26:41 +03:00
newList_len + + ;
}
ignore_value ( VIR_REALLOC_N_QUIET ( newList , newList_len + 1 ) ) ;
VIR_FREE ( * list ) ;
* list = newList ;
return 0 ;
}
2016-07-08 15:56:05 +03:00
static char *
vshReadlineParse ( const char * text , int state )
{
2017-11-21 17:07:34 +03:00
static vshCmd * partial ;
2017-11-21 19:23:11 +03:00
static char * * list ;
static size_t list_index ;
const vshCmdDef * cmd = NULL ;
2017-11-21 19:45:50 +03:00
const vshCmdOptDef * opt = NULL ;
2017-11-21 19:23:11 +03:00
char * ret = NULL ;
2016-07-08 15:56:05 +03:00
if ( ! state ) {
2019-10-18 18:24:02 +03:00
char * buf = g_strdup ( rl_line_buffer ) ;
2016-07-08 15:56:05 +03:00
2017-11-21 17:07:34 +03:00
vshCommandFree ( partial ) ;
partial = NULL ;
2017-11-21 19:23:11 +03:00
virStringListFree ( list ) ;
list = NULL ;
list_index = 0 ;
2016-09-05 12:45:09 +03:00
2017-11-21 17:07:34 +03:00
* ( buf + rl_point ) = ' \0 ' ;
2016-07-08 15:56:05 +03:00
2017-11-21 17:07:34 +03:00
vshCommandStringParse ( NULL , buf , & partial ) ;
2016-07-08 15:56:05 +03:00
2017-11-21 17:07:34 +03:00
VIR_FREE ( buf ) ;
2016-07-08 15:56:05 +03:00
2017-11-21 19:45:50 +03:00
if ( partial ) {
2017-11-21 17:07:34 +03:00
cmd = partial - > def ;
2017-11-21 19:45:50 +03:00
partial - > skipChecks = true ;
}
2016-07-08 15:56:05 +03:00
2017-11-21 17:07:34 +03:00
if ( cmd & & STREQ ( cmd - > name , text ) ) {
/* Corner case - some commands share prefix (e.g.
* dump and dumpxml ) . If user typed ' dump < TAB > < TAB > ' ,
* then @ text = " dump " and we want to offer command
* completion . If they typed ' dump < TAB > < TAB > ' then
* @ text = " " ( the space after the command ) and we
* want to offer options completion for dump command .
*/
cmd = NULL ;
2016-09-05 12:45:09 +03:00
}
2017-11-21 19:45:50 +03:00
opt = vshReadlineCommandFindOpt ( partial , text ) ;
2016-09-05 12:45:09 +03:00
}
2016-07-08 15:56:05 +03:00
2017-11-21 19:23:11 +03:00
if ( ! list ) {
if ( ! cmd ) {
list = vshReadlineCommandGenerator ( text ) ;
} else {
2018-05-08 17:20:34 +03:00
if ( ! opt | | ( opt - > type ! = VSH_OT_DATA & &
opt - > type ! = VSH_OT_STRING & &
opt - > type ! = VSH_OT_ARGV ) )
2018-01-12 19:05:33 +03:00
list = vshReadlineOptionsGenerator ( text , cmd , partial ) ;
2017-12-29 13:25:38 +03:00
2017-11-21 19:45:50 +03:00
if ( opt & & opt - > completer ) {
char * * completer_list = opt - > completer ( autoCompleteOpaque ,
partial ,
opt - > completer_flags ) ;
2017-12-28 14:26:41 +03:00
/* For string list returned by completer we have to do
* filtering based on @ text because completer returns all
* possible strings . */
if ( completer_list & &
( vshCompleterFilter ( & completer_list , text ) < 0 | |
virStringListMerge ( & list , & completer_list ) < 0 ) ) {
2017-11-21 19:45:50 +03:00
virStringListFree ( completer_list ) ;
goto cleanup ;
}
}
2017-11-21 19:23:11 +03:00
}
}
if ( list ) {
2019-10-18 18:24:02 +03:00
ret = g_strdup ( list [ list_index ] ) ;
2017-11-21 19:23:11 +03:00
list_index + + ;
2017-11-21 17:07:34 +03:00
}
2017-11-21 19:23:11 +03:00
if ( ret & &
2017-11-13 15:34:54 +03:00
! rl_completion_quote_character ) {
virBuffer buf = VIR_BUFFER_INITIALIZER ;
2017-11-21 19:23:11 +03:00
virBufferEscapeShell ( & buf , ret ) ;
VIR_FREE ( ret ) ;
ret = virBufferContentAndReset ( & buf ) ;
2017-11-13 15:34:54 +03:00
}
2017-11-21 19:45:50 +03:00
cleanup :
2017-11-21 19:23:11 +03:00
if ( ! ret ) {
2017-11-21 17:07:34 +03:00
vshCommandFree ( partial ) ;
partial = NULL ;
2017-11-21 19:23:11 +03:00
virStringListFree ( list ) ;
list = NULL ;
list_index = 0 ;
2016-07-08 15:56:05 +03:00
}
2017-11-21 19:23:11 +03:00
return ret ;
2016-07-08 15:56:05 +03:00
}
2015-06-15 19:53:58 +03:00
static char * *
2016-12-09 12:52:29 +03:00
vshReadlineCompletion ( const char * text ,
2019-10-14 15:44:29 +03:00
int start G_GNUC_UNUSED ,
int end G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
2019-10-17 11:10:10 +03:00
return rl_completion_matches ( text , vshReadlineParse ) ;
2015-06-15 19:53:58 +03:00
}
2017-11-13 15:34:54 +03:00
static int
vshReadlineCharIsQuoted ( char * line , int idx )
{
return idx > 0 & &
line [ idx - 1 ] = = ' \\ ' & &
! vshReadlineCharIsQuoted ( line , idx - 1 ) ;
}
2015-06-15 19:53:58 +03:00
# define HISTSIZE_MAX 500000
static int
vshReadlineInit ( vshControl * ctl )
{
char * userdir = NULL ;
int max_history = 500 ;
int ret = - 1 ;
2015-09-03 15:37:14 +03:00
char * histsize_env = NULL ;
2015-06-15 19:53:58 +03:00
const char * histsize_str = NULL ;
2017-11-23 20:00:33 +03:00
const char * break_characters = " \t \n \\ `@$><=;|&{( " ;
2017-11-13 15:34:54 +03:00
const char * quote_characters = " \" ' " ;
2015-06-15 19:53:58 +03:00
2016-09-06 15:04:37 +03:00
/* Opaque data for autocomplete callbacks. */
autoCompleteOpaque = ctl ;
2015-06-15 19:53:58 +03:00
rl_readline_name = ctl - > name ;
/* Tell the completer that we want a crack first. */
rl_attempted_completion_function = vshReadlineCompletion ;
2017-11-23 20:00:33 +03:00
rl_basic_word_break_characters = break_characters ;
2016-07-08 15:56:05 +03:00
2017-11-13 15:34:54 +03:00
rl_completer_quote_characters = quote_characters ;
rl_char_is_quoted_p = vshReadlineCharIsQuoted ;
2019-10-22 16:26:14 +03:00
histsize_env = g_strdup_printf ( " %s_HISTSIZE " , ctl - > env_prefix ) ;
2015-06-15 19:53:58 +03:00
/* Limit the total size of the history buffer */
2019-08-01 15:35:56 +03:00
if ( ( histsize_str = getenv ( histsize_env ) ) ) {
2015-06-15 19:53:58 +03:00
if ( virStrToLong_i ( histsize_str , NULL , 10 , & max_history ) < 0 ) {
vshError ( ctl , _ ( " Bad $%s value. " ) , histsize_env ) ;
goto cleanup ;
} else if ( max_history > HISTSIZE_MAX | | max_history < 0 ) {
vshError ( ctl , _ ( " $%s value should be between 0 "
" and %d " ) ,
histsize_env , HISTSIZE_MAX ) ;
goto cleanup ;
}
}
stifle_history ( max_history ) ;
/* Prepare to read/write history from/to the
* $ XDG_CACHE_HOME / virtshell / history file
*/
userdir = virGetUserCacheDirectory ( ) ;
2019-10-22 16:26:14 +03:00
ctl - > historydir = g_strdup_printf ( " %s/%s " , userdir , ctl - > name ) ;
2015-06-15 19:53:58 +03:00
2019-10-22 16:26:14 +03:00
ctl - > historyfile = g_strdup_printf ( " %s/history " , ctl - > historydir ) ;
2015-06-15 19:53:58 +03:00
read_history ( ctl - > historyfile ) ;
ret = 0 ;
cleanup :
VIR_FREE ( userdir ) ;
2015-09-03 15:37:14 +03:00
VIR_FREE ( histsize_env ) ;
2015-06-15 19:53:58 +03:00
return ret ;
}
static void
vshReadlineDeinit ( vshControl * ctl )
{
if ( ctl - > historyfile ! = NULL ) {
if ( virFileMakePathWithMode ( ctl - > historydir , 0755 ) < 0 & &
errno ! = EEXIST ) {
char ebuf [ 1024 ] ;
vshError ( ctl , _ ( " Failed to create '%s': %s " ) ,
ctl - > historydir , virStrerror ( errno , ebuf , sizeof ( ebuf ) ) ) ;
} else {
write_history ( ctl - > historyfile ) ;
}
}
VIR_FREE ( ctl - > historydir ) ;
VIR_FREE ( ctl - > historyfile ) ;
}
char *
2019-10-14 15:44:29 +03:00
vshReadline ( vshControl * ctl G_GNUC_UNUSED , const char * prompt )
2015-06-15 19:53:58 +03:00
{
return readline ( prompt ) ;
}
# else /* !WITH_READLINE */
static int
2019-10-14 15:44:29 +03:00
vshReadlineInit ( vshControl * ctl G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
/* empty */
return 0 ;
}
static void
2019-10-14 15:44:29 +03:00
vshReadlineDeinit ( vshControl * ctl G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
/* empty */
}
2015-08-15 10:59:41 +03:00
char *
2019-10-21 12:11:04 +03:00
vshReadline ( vshControl * ctl G_GNUC_UNUSED ,
const char * prompt )
2015-06-15 19:53:58 +03:00
{
char line [ 1024 ] ;
char * r ;
int len ;
fputs ( prompt , stdout ) ;
r = fgets ( line , sizeof ( line ) , stdin ) ;
if ( r = = NULL ) return NULL ; /* EOF */
/* Chomp trailing \n */
len = strlen ( r ) ;
if ( len > 0 & & r [ len - 1 ] = = ' \n ' )
r [ len - 1 ] = ' \0 ' ;
2019-10-18 18:24:02 +03:00
return g_strdup ( r ) ;
2015-06-15 19:53:58 +03:00
}
# endif /* !WITH_READLINE */
/*
* Initialize debug settings .
*/
2016-07-28 13:33:21 +03:00
static int
2015-06-15 19:53:58 +03:00
vshInitDebug ( vshControl * ctl )
{
const char * debugEnv ;
2016-07-28 13:54:16 +03:00
char * env = NULL ;
2015-06-15 19:53:58 +03:00
if ( ctl - > debug = = VSH_DEBUG_DEFAULT ) {
2019-10-22 16:26:14 +03:00
env = g_strdup_printf ( " %s_DEBUG " , ctl - > env_prefix ) ;
2016-07-28 13:54:16 +03:00
2015-06-15 19:53:58 +03:00
/* log level not set from commandline, check env variable */
2019-08-01 15:35:56 +03:00
debugEnv = getenv ( env ) ;
2015-06-15 19:53:58 +03:00
if ( debugEnv ) {
int debug ;
if ( virStrToLong_i ( debugEnv , NULL , 10 , & debug ) < 0 | |
debug < VSH_ERR_DEBUG | | debug > VSH_ERR_ERROR ) {
2016-11-11 15:39:45 +03:00
vshError ( ctl , _ ( " %s_DEBUG not set with a valid numeric value " ) ,
ctl - > env_prefix ) ;
2015-06-15 19:53:58 +03:00
} else {
ctl - > debug = debug ;
}
}
2016-07-28 13:54:16 +03:00
VIR_FREE ( env ) ;
2015-06-15 19:53:58 +03:00
}
if ( ctl - > logfile = = NULL ) {
2019-10-22 16:26:14 +03:00
env = g_strdup_printf ( " %s_LOG_FILE " , ctl - > env_prefix ) ;
2016-07-28 13:54:16 +03:00
2015-06-15 19:53:58 +03:00
/* log file not set from cmdline */
2019-08-01 15:35:56 +03:00
debugEnv = getenv ( env ) ;
2015-06-15 19:53:58 +03:00
if ( debugEnv & & * debugEnv ) {
2019-10-18 18:24:02 +03:00
ctl - > logfile = g_strdup ( debugEnv ) ;
2015-06-15 19:53:58 +03:00
vshOpenLogFile ( ctl ) ;
}
2016-07-28 13:54:16 +03:00
VIR_FREE ( env ) ;
2015-06-15 19:53:58 +03:00
}
2016-07-28 13:33:21 +03:00
return 0 ;
2015-06-15 19:53:58 +03:00
}
/*
* Initialize global data
*/
2015-09-03 17:52:44 +03:00
bool
2015-06-15 19:53:58 +03:00
vshInit ( vshControl * ctl , const vshCmdGrp * groups , const vshCmdDef * set )
{
if ( ! ctl - > hooks ) {
vshError ( ctl , " %s " , _ ( " client hooks cannot be NULL " ) ) ;
2015-09-03 17:52:44 +03:00
return false ;
2015-06-15 19:53:58 +03:00
}
if ( ! groups & & ! set ) {
vshError ( ctl , " %s " , _ ( " command groups and command set "
" cannot both be NULL " ) ) ;
2015-09-03 17:52:44 +03:00
return false ;
2015-06-15 19:53:58 +03:00
}
cmdGroups = groups ;
cmdSet = set ;
2016-07-28 13:33:21 +03:00
if ( vshInitDebug ( ctl ) < 0 | |
( ctl - > imode & & vshReadlineInit ( ctl ) < 0 ) )
2015-09-03 17:52:44 +03:00
return false ;
2015-06-15 19:53:58 +03:00
2015-09-03 17:52:44 +03:00
return true ;
2015-06-15 19:53:58 +03:00
}
2015-09-03 17:59:01 +03:00
bool
vshInitReload ( vshControl * ctl )
{
if ( ! cmdGroups & & ! cmdSet ) {
vshError ( ctl , " %s " , _ ( " command groups and command are both NULL "
" run vshInit before reloading " ) ) ;
return false ;
}
2016-07-28 13:33:21 +03:00
if ( vshInitDebug ( ctl ) < 0 )
return false ;
2015-09-03 17:59:01 +03:00
if ( ctl - > imode )
vshReadlineDeinit ( ctl ) ;
if ( ctl - > imode & & vshReadlineInit ( ctl ) < 0 )
return false ;
return true ;
}
2015-06-15 19:53:58 +03:00
void
vshDeinit ( vshControl * ctl )
{
2016-09-28 22:01:55 +03:00
/* NB: Don't make calling of vshReadlineDeinit conditional on active
* interactive mode . */
vshReadlineDeinit ( ctl ) ;
2015-06-15 19:53:58 +03:00
vshCloseLogFile ( ctl ) ;
}
/* -----------------------------------------------
* Generic commands available to use by any client
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
const vshCmdOptDef opts_help [ ] = {
{ . name = " command " ,
. type = VSH_OT_STRING ,
. help = N_ ( " Prints global help, command specific help, or help for a group of related commands " )
} ,
{ . name = NULL }
} ;
const vshCmdInfo info_help [ ] = {
{ . name = " help " ,
. data = N_ ( " print help " )
} ,
{ . name = " desc " ,
. data = N_ ( " Prints global help, command specific help, or help for a \n "
" group of related commands " )
} ,
{ . name = NULL }
} ;
bool
cmdHelp ( vshControl * ctl , const vshCmd * cmd )
2016-06-17 20:27:08 +03:00
{
2018-01-25 18:08:46 +03:00
const vshCmdDef * def = NULL ;
const vshCmdGrp * grp = NULL ;
2015-06-15 19:53:58 +03:00
const char * name = NULL ;
2015-12-03 15:47:56 +03:00
if ( vshCommandOptStringQuiet ( ctl , cmd , " command " , & name ) < = 0 ) {
2015-06-15 19:53:58 +03:00
vshPrint ( ctl , " %s " , _ ( " Grouped commands: \n \n " ) ) ;
for ( grp = cmdGroups ; grp - > name ; grp + + ) {
vshPrint ( ctl , _ ( " %s (help keyword '%s'): \n " ) , grp - > name ,
grp - > keyword ) ;
for ( def = grp - > commands ; def - > name ; def + + ) {
if ( def - > flags & VSH_CMD_FLAG_ALIAS )
continue ;
vshPrint ( ctl , " %-30s %s \n " , def - > name ,
_ ( vshCmddefGetInfo ( def , " help " ) ) ) ;
}
vshPrint ( ctl , " \n " ) ;
}
return true ;
}
2018-01-25 18:08:46 +03:00
if ( ( def = vshCmddefSearch ( name ) ) ) {
2018-01-25 18:13:37 +03:00
if ( def - > flags & VSH_CMD_FLAG_ALIAS )
def = vshCmddefSearch ( def - > alias ) ;
2018-01-25 18:08:46 +03:00
return vshCmddefHelp ( ctl , def ) ;
} else if ( ( grp = vshCmdGrpSearch ( name ) ) ) {
return vshCmdGrpHelp ( ctl , grp ) ;
2015-06-15 19:53:58 +03:00
} else {
vshError ( ctl , _ ( " command or command group '%s' doesn't exist " ) , name ) ;
return false ;
}
}
const vshCmdOptDef opts_cd [ ] = {
{ . name = " dir " ,
. type = VSH_OT_STRING ,
. help = N_ ( " directory to switch to (default: home or else root) " )
} ,
{ . name = NULL }
} ;
const vshCmdInfo info_cd [ ] = {
{ . name = " help " ,
. data = N_ ( " change the current directory " )
} ,
{ . name = " desc " ,
. data = N_ ( " Change the current directory. " )
} ,
{ . name = NULL }
} ;
bool
cmdCd ( vshControl * ctl , const vshCmd * cmd )
{
const char * dir = NULL ;
2019-12-19 11:09:02 +03:00
g_autofree char * dir_malloced = NULL ;
2015-06-15 19:53:58 +03:00
char ebuf [ 1024 ] ;
if ( ! ctl - > imode ) {
vshError ( ctl , " %s " , _ ( " cd: command valid only in interactive mode " ) ) ;
return false ;
}
2015-12-03 15:47:56 +03:00
if ( vshCommandOptStringQuiet ( ctl , cmd , " dir " , & dir ) < = 0 )
2015-06-15 19:53:58 +03:00
dir = dir_malloced = virGetUserDirectory ( ) ;
if ( ! dir )
dir = " / " ;
if ( chdir ( dir ) = = - 1 ) {
vshError ( ctl , _ ( " cd: %s: %s " ) ,
virStrerror ( errno , ebuf , sizeof ( ebuf ) ) , dir ) ;
2019-12-19 11:09:02 +03:00
return false ;
2015-06-15 19:53:58 +03:00
}
2019-12-19 11:09:02 +03:00
return true ;
2015-06-15 19:53:58 +03:00
}
const vshCmdOptDef opts_echo [ ] = {
{ . name = " shell " ,
. type = VSH_OT_BOOL ,
. help = N_ ( " escape for shell use " )
} ,
{ . name = " xml " ,
. type = VSH_OT_BOOL ,
. help = N_ ( " escape for XML use " )
} ,
2019-03-24 03:02:13 +03:00
{ . name = " err " ,
. type = VSH_OT_BOOL ,
. help = N_ ( " output to stderr " ) ,
} ,
2015-06-15 19:53:58 +03:00
{ . name = " str " ,
. type = VSH_OT_ALIAS ,
. help = " string "
} ,
{ . name = " hi " ,
. type = VSH_OT_ALIAS ,
. help = " string=hello "
} ,
{ . name = " string " ,
. type = VSH_OT_ARGV ,
. help = N_ ( " arguments to echo " )
} ,
{ . name = NULL }
} ;
const vshCmdInfo info_echo [ ] = {
{ . name = " help " ,
. data = N_ ( " echo arguments " )
} ,
{ . name = " desc " ,
. data = N_ ( " Echo back arguments, possibly with quoting. " )
} ,
{ . name = NULL }
} ;
/* Exists mainly for debugging virsh, but also handy for adding back
* quotes for later evaluation .
*/
bool
cmdEcho ( vshControl * ctl , const vshCmd * cmd )
{
bool shell = false ;
bool xml = false ;
2019-03-24 03:02:13 +03:00
bool err = false ;
2015-06-15 19:53:58 +03:00
int count = 0 ;
const vshCmdOpt * opt = NULL ;
char * arg ;
virBuffer buf = VIR_BUFFER_INITIALIZER ;
if ( vshCommandOptBool ( cmd , " shell " ) )
shell = true ;
if ( vshCommandOptBool ( cmd , " xml " ) )
xml = true ;
2019-03-24 03:02:13 +03:00
if ( vshCommandOptBool ( cmd , " err " ) )
err = true ;
2015-06-15 19:53:58 +03:00
while ( ( opt = vshCommandOptArgv ( ctl , cmd , opt ) ) ) {
char * str ;
virBuffer xmlbuf = VIR_BUFFER_INITIALIZER ;
arg = opt - > data ;
if ( count )
virBufferAddChar ( & buf , ' ' ) ;
if ( xml ) {
virBufferEscapeString ( & xmlbuf , " %s " , arg ) ;
str = virBufferContentAndReset ( & xmlbuf ) ;
} else {
2019-10-18 18:24:02 +03:00
str = g_strdup ( arg ) ;
2015-06-15 19:53:58 +03:00
}
if ( shell )
virBufferEscapeShell ( & buf , str ) ;
else
virBufferAdd ( & buf , str , - 1 ) ;
count + + ;
VIR_FREE ( str ) ;
}
arg = virBufferContentAndReset ( & buf ) ;
2019-03-24 03:02:13 +03:00
if ( arg ) {
if ( err )
vshError ( ctl , " %s " , arg ) ;
else
vshPrint ( ctl , " %s " , arg ) ;
}
2015-06-15 19:53:58 +03:00
VIR_FREE ( arg ) ;
return true ;
}
const vshCmdInfo info_pwd [ ] = {
{ . name = " help " ,
. data = N_ ( " print the current directory " )
} ,
{ . name = " desc " ,
. data = N_ ( " Print the current directory. " )
} ,
{ . name = NULL }
} ;
bool
2019-10-14 15:44:29 +03:00
cmdPwd ( vshControl * ctl , const vshCmd * cmd G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
2019-12-20 19:51:16 +03:00
g_autofree char * cwd = g_get_current_dir ( ) ;
2015-06-15 19:53:58 +03:00
2019-12-20 19:51:16 +03:00
vshPrint ( ctl , _ ( " %s \n " ) , cwd ) ;
2015-06-15 19:53:58 +03:00
2019-12-20 19:51:16 +03:00
return true ;
2015-06-15 19:53:58 +03:00
}
const vshCmdInfo info_quit [ ] = {
{ . name = " help " ,
. data = N_ ( " quit this interactive terminal " )
} ,
{ . name = " desc " ,
. data = " "
} ,
{ . name = NULL }
} ;
bool
2019-10-14 15:44:29 +03:00
cmdQuit ( vshControl * ctl , const vshCmd * cmd G_GNUC_UNUSED )
2015-06-15 19:53:58 +03:00
{
ctl - > imode = false ;
return true ;
}
2016-09-14 10:50:22 +03:00
/* -----------------
* Command self - test
* - - - - - - - - - - - - - - - - - */
const vshCmdInfo info_selftest [ ] = {
{ . name = " help " ,
. data = N_ ( " internal command for testing virt shells " )
} ,
{ . name = " desc " ,
. data = N_ ( " internal use only " )
} ,
{ . name = NULL }
} ;
/* Prints help for every command.
* That runs vshCmddefOptParse which validates
* the per - command options structure . */
bool
2019-03-12 05:17:33 +03:00
cmdSelfTest ( vshControl * ctl ,
2019-10-14 15:44:29 +03:00
const vshCmd * cmd G_GNUC_UNUSED )
2016-09-14 10:50:22 +03:00
{
const vshCmdGrp * grp ;
const vshCmdDef * def ;
for ( grp = cmdGroups ; grp - > name ; grp + + ) {
for ( def = grp - > commands ; def - > name ; def + + ) {
2019-03-12 05:17:33 +03:00
if ( vshCmddefCheckInternals ( ctl , def ) < 0 )
2016-09-14 10:50:22 +03:00
return false ;
}
}
return true ;
}
2017-11-01 17:34:14 +03:00
/* ----------------------
* Autocompletion command
* - - - - - - - - - - - - - - - - - - - - - - */
const vshCmdOptDef opts_complete [ ] = {
{ . name = " string " ,
. type = VSH_OT_ARGV ,
. flags = VSH_OFLAG_EMPTY_OK ,
. help = N_ ( " partial string to autocomplete " )
} ,
{ . name = NULL }
} ;
const vshCmdInfo info_complete [ ] = {
{ . name = " help " ,
. data = N_ ( " internal command for autocompletion " )
} ,
{ . name = " desc " ,
. data = N_ ( " internal use only " )
} ,
{ . name = NULL }
} ;
2018-01-14 13:43:51 +03:00
# ifdef WITH_READLINE
2017-11-01 17:34:14 +03:00
bool
cmdComplete ( vshControl * ctl , const vshCmd * cmd )
{
bool ret = false ;
const vshClientHooks * hooks = ctl - > hooks ;
int stdin_fileno = STDIN_FILENO ;
const char * arg = " " ;
const vshCmdOpt * opt = NULL ;
char * * matches = NULL , * * iter ;
virBuffer buf = VIR_BUFFER_INITIALIZER ;
if ( vshCommandOptStringQuiet ( ctl , cmd , " string " , & arg ) < = 0 )
goto cleanup ;
/* This command is flagged VSH_CMD_FLAG_NOCONNECT because we
* need to prevent auth hooks reading any input . Therefore , we
* have to close stdin and then connect ourselves . */
VIR_FORCE_CLOSE ( stdin_fileno ) ;
if ( ! ( hooks & & hooks - > connHandler & & hooks - > connHandler ( ctl ) ) )
goto cleanup ;
while ( ( opt = vshCommandOptArgv ( ctl , cmd , opt ) ) ) {
if ( virBufferUse ( & buf ) ! = 0 )
virBufferAddChar ( & buf , ' ' ) ;
virBufferAddStr ( & buf , opt - > data ) ;
arg = opt - > data ;
}
vshReadlineInit ( ctl ) ;
2019-10-20 14:49:46 +03:00
if ( ! ( rl_line_buffer = virBufferContentAndReset ( & buf ) ) )
rl_line_buffer = g_strdup ( " " ) ;
2017-11-01 17:34:14 +03:00
/* rl_point is current cursor position in rl_line_buffer.
* In our case it ' s at the end of the whole line . */
rl_point = strlen ( rl_line_buffer ) ;
if ( ! ( matches = vshReadlineCompletion ( arg , 0 , 0 ) ) )
goto cleanup ;
2018-05-11 12:13:10 +03:00
for ( iter = matches ; * iter ; iter + + ) {
if ( iter = = matches & & matches [ 1 ] )
continue ;
2017-11-01 17:34:14 +03:00
printf ( " %s \n " , * iter ) ;
2018-05-11 12:13:10 +03:00
}
2017-11-01 17:34:14 +03:00
ret = true ;
cleanup :
virBufferFreeAndReset ( & buf ) ;
virStringListFree ( matches ) ;
return ret ;
}
2018-01-14 13:43:51 +03:00
# else /* !WITH_READLINE */
bool
2019-10-14 15:44:29 +03:00
cmdComplete ( vshControl * ctl G_GNUC_UNUSED ,
const vshCmd * cmd G_GNUC_UNUSED )
2018-01-14 13:43:51 +03:00
{
return false ;
}
# endif /* !WITH_READLINE */