2017-04-10 18:06:15 +03:00
/*
* virsh - util . c : helpers for virsh
*
* 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 "virsh-util.h"
# include "virfile.h"
2017-04-11 11:18:06 +03:00
# include "virstring.h"
2017-04-11 17:51:32 +03:00
# include "viralloc.h"
2021-01-06 14:56:11 +03:00
# include "virxml.h"
2017-04-11 11:18:06 +03:00
static virDomainPtr
virshLookupDomainInternal ( vshControl * ctl ,
const char * cmdname ,
const char * name ,
unsigned int flags )
{
virDomainPtr dom = NULL ;
int id ;
2021-03-11 10:16:13 +03:00
virshControl * priv = ctl - > privData ;
2017-04-11 11:18:06 +03:00
2020-08-03 18:27:58 +03:00
virCheckFlags ( VIRSH_BYID | VIRSH_BYUUID | VIRSH_BYNAME , NULL ) ;
2017-04-11 11:18:06 +03:00
/* try it by ID */
if ( flags & VIRSH_BYID ) {
if ( virStrToLong_i ( name , NULL , 10 , & id ) = = 0 & & id > = 0 ) {
vshDebug ( ctl , VSH_ERR_DEBUG , " %s: <domain> looks like ID \n " ,
cmdname ) ;
dom = virDomainLookupByID ( priv - > conn , id ) ;
}
}
/* try it by UUID */
if ( ! dom & & ( flags & VIRSH_BYUUID ) & &
strlen ( name ) = = VIR_UUID_STRING_BUFLEN - 1 ) {
vshDebug ( ctl , VSH_ERR_DEBUG , " %s: <domain> trying as domain UUID \n " ,
cmdname ) ;
dom = virDomainLookupByUUIDString ( priv - > conn , name ) ;
}
/* try it by NAME */
if ( ! dom & & ( flags & VIRSH_BYNAME ) ) {
vshDebug ( ctl , VSH_ERR_DEBUG , " %s: <domain> trying as domain NAME \n " ,
cmdname ) ;
dom = virDomainLookupByName ( priv - > conn , name ) ;
}
vshResetLibvirtError ( ) ;
if ( ! dom )
vshError ( ctl , _ ( " failed to get domain '%s' " ) , name ) ;
return dom ;
}
virDomainPtr
virshLookupDomainBy ( vshControl * ctl ,
const char * name ,
unsigned int flags )
{
return virshLookupDomainInternal ( ctl , " unknown " , name , flags ) ;
}
virDomainPtr
virshCommandOptDomainBy ( vshControl * ctl ,
const vshCmd * cmd ,
const char * * name ,
unsigned int flags )
{
const char * n = NULL ;
const char * optname = " domain " ;
if ( vshCommandOptStringReq ( ctl , cmd , optname , & n ) < 0 )
return NULL ;
vshDebug ( ctl , VSH_ERR_INFO , " %s: found option <%s>: %s \n " ,
cmd - > def - > name , optname , n ) ;
if ( name )
* name = n ;
return virshLookupDomainInternal ( ctl , cmd - > def - > name , n , flags ) ;
}
virDomainPtr
virshCommandOptDomain ( vshControl * ctl ,
const vshCmd * cmd ,
const char * * name )
{
return virshCommandOptDomainBy ( ctl , cmd , name ,
VIRSH_BYID | VIRSH_BYUUID | VIRSH_BYNAME ) ;
}
2017-04-10 18:06:15 +03:00
int
virshDomainState ( vshControl * ctl ,
virDomainPtr dom ,
int * reason )
{
virDomainInfo info ;
2021-03-11 10:16:13 +03:00
virshControl * priv = ctl - > privData ;
2017-04-10 18:06:15 +03:00
if ( reason )
* reason = - 1 ;
if ( ! priv - > useGetInfo ) {
int state ;
if ( virDomainGetState ( dom , & state , reason , 0 ) < 0 ) {
2018-05-05 15:04:21 +03:00
if ( virGetLastErrorCode ( ) = = VIR_ERR_NO_SUPPORT )
2017-04-10 18:06:15 +03:00
priv - > useGetInfo = true ;
else
return - 1 ;
} else {
return state ;
}
}
/* fall back to virDomainGetInfo if virDomainGetState is not supported */
if ( virDomainGetInfo ( dom , & info ) < 0 )
return - 1 ;
2021-09-24 02:49:11 +03:00
return info . state ;
2017-04-10 18:06:15 +03:00
}
int
2019-10-14 15:44:29 +03:00
virshStreamSink ( virStreamPtr st G_GNUC_UNUSED ,
2017-04-10 18:06:15 +03:00
const char * bytes ,
size_t nbytes ,
void * opaque )
{
2021-03-11 10:16:13 +03:00
virshStreamCallbackData * cbData = opaque ;
2017-04-10 18:06:15 +03:00
2020-07-02 09:42:44 +03:00
return safewrite ( cbData - > fd , bytes , nbytes ) ;
2017-04-10 18:06:15 +03:00
}
2017-04-11 13:16:52 +03:00
2016-04-27 15:21:10 +03:00
int
2019-10-14 15:44:29 +03:00
virshStreamSource ( virStreamPtr st G_GNUC_UNUSED ,
2016-04-27 15:21:10 +03:00
char * bytes ,
size_t nbytes ,
void * opaque )
{
2021-03-11 10:16:13 +03:00
virshStreamCallbackData * cbData = opaque ;
2016-04-27 15:21:10 +03:00
int fd = cbData - > fd ;
return saferead ( fd , bytes , nbytes ) ;
}
int
2019-10-14 15:44:29 +03:00
virshStreamSourceSkip ( virStreamPtr st G_GNUC_UNUSED ,
2016-04-27 15:21:10 +03:00
long long offset ,
void * opaque )
{
2021-03-11 10:16:13 +03:00
virshStreamCallbackData * cbData = opaque ;
2016-04-27 15:21:10 +03:00
int fd = cbData - > fd ;
2020-09-23 20:57:09 +03:00
if ( lseek ( fd , offset , SEEK_CUR ) = = ( off_t ) - 1 )
2016-04-27 15:21:10 +03:00
return - 1 ;
return 0 ;
}
2016-04-12 16:35:04 +03:00
int
2019-10-14 15:44:29 +03:00
virshStreamSkip ( virStreamPtr st G_GNUC_UNUSED ,
2016-04-12 16:35:04 +03:00
long long offset ,
void * opaque )
{
2021-03-11 10:16:13 +03:00
virshStreamCallbackData * cbData = opaque ;
2016-04-12 16:35:04 +03:00
off_t cur ;
2020-07-02 16:49:33 +03:00
if ( cbData - > isBlock ) {
g_autofree char * buf = NULL ;
const size_t buflen = 1 * 1024 * 1024 ; /* 1MiB */
2016-04-12 16:35:04 +03:00
2020-07-02 16:49:33 +03:00
/* While for files it's enough to lseek() and ftruncate() to create
* a hole which would emulate zeroes on read ( ) , for block devices
* we have to write zeroes to read ( ) zeroes . And we have to write
* @ got bytes of zeroes . Do that in smaller chunks though . */
buf = g_new0 ( char , buflen ) ;
while ( offset ) {
size_t count = MIN ( offset , buflen ) ;
ssize_t r ;
if ( ( r = safewrite ( cbData - > fd , buf , count ) ) < 0 )
return - 1 ;
offset - = r ;
}
} else {
if ( ( cur = lseek ( cbData - > fd , offset , SEEK_CUR ) ) = = ( off_t ) - 1 )
return - 1 ;
if ( ftruncate ( cbData - > fd , cur ) < 0 )
return - 1 ;
}
2016-04-12 16:35:04 +03:00
return 0 ;
}
2016-04-27 15:21:10 +03:00
int
2019-10-14 15:44:29 +03:00
virshStreamInData ( virStreamPtr st G_GNUC_UNUSED ,
2016-04-27 15:21:10 +03:00
int * inData ,
long long * offset ,
void * opaque )
{
2021-03-11 10:16:13 +03:00
virshStreamCallbackData * cbData = opaque ;
2016-04-27 15:21:10 +03:00
vshControl * ctl = cbData - > ctl ;
int fd = cbData - > fd ;
2020-07-02 16:51:20 +03:00
if ( cbData - > isBlock ) {
/* Block devices are always in data section by definition. The
* @ sectionLen is slightly more tricky . While we could try and get
* how much bytes is there left until EOF , we can pretend there is
* always X bytes left and let the saferead ( ) below hit EOF ( which
* is then handled gracefully anyway ) . Worst case scenario , this
* branch is called more than once .
* X was chosen to be 1 MiB but it has ho special meaning . */
* inData = 1 ;
* offset = 1 * 1024 * 1024 ;
} else {
if ( virFileInData ( fd , inData , offset ) < 0 ) {
vshError ( ctl , " %s " , _ ( " Unable to get current position in stream " ) ) ;
return - 1 ;
}
}
2016-04-27 15:21:10 +03:00
2020-07-02 16:51:20 +03:00
return 0 ;
2016-04-27 15:21:10 +03:00
}
2017-04-11 13:16:52 +03:00
void
virshDomainFree ( virDomainPtr dom )
{
if ( ! dom )
return ;
2017-04-11 18:23:23 +03:00
vshSaveLibvirtHelperError ( ) ;
2017-04-11 13:16:52 +03:00
virDomainFree ( dom ) ; /* sc_prohibit_obj_free_apis_in_virsh */
}
2017-04-11 18:21:05 +03:00
2019-03-14 00:04:51 +03:00
void
virshDomainCheckpointFree ( virDomainCheckpointPtr chk )
{
if ( ! chk )
return ;
vshSaveLibvirtHelperError ( ) ;
virDomainCheckpointFree ( chk ) ; /* sc_prohibit_obj_free_apis_in_virsh */
}
2017-04-11 18:21:05 +03:00
void
virshDomainSnapshotFree ( virDomainSnapshotPtr snap )
{
if ( ! snap )
return ;
2017-04-11 18:23:23 +03:00
vshSaveLibvirtHelperError ( ) ;
2017-04-11 18:21:05 +03:00
virDomainSnapshotFree ( snap ) ; /* sc_prohibit_obj_free_apis_in_virsh */
}
2017-04-11 17:51:32 +03:00
2020-01-24 17:24:49 +03:00
void
virshSecretFree ( virSecretPtr secret )
{
if ( ! secret )
return ;
vshSaveLibvirtHelperError ( ) ;
virSecretFree ( secret ) ; /* sc_prohibit_obj_free_apis_in_virsh */
}
2017-04-11 17:51:32 +03:00
int
virshDomainGetXMLFromDom ( vshControl * ctl ,
virDomainPtr dom ,
unsigned int flags ,
xmlDocPtr * xml ,
xmlXPathContextPtr * ctxt )
{
2021-08-11 16:25:15 +03:00
g_autofree char * desc = NULL ;
2017-04-11 17:51:32 +03:00
if ( ! ( desc = virDomainGetXMLDesc ( dom , flags ) ) ) {
vshError ( ctl , _ ( " Failed to get domain description xml " ) ) ;
return - 1 ;
}
* xml = virXMLParseStringCtxt ( desc , _ ( " (domain_definition) " ) , ctxt ) ;
if ( ! ( * xml ) ) {
vshError ( ctl , _ ( " Failed to parse domain description xml " ) ) ;
return - 1 ;
}
return 0 ;
}
int
virshDomainGetXML ( vshControl * ctl ,
const vshCmd * cmd ,
unsigned int flags ,
xmlDocPtr * xml ,
xmlXPathContextPtr * ctxt )
{
virDomainPtr dom ;
int ret ;
if ( ! ( dom = virshCommandOptDomain ( ctl , cmd , NULL ) ) )
return - 1 ;
ret = virshDomainGetXMLFromDom ( ctl , dom , flags , xml , ctxt ) ;
virshDomainFree ( dom ) ;
return ret ;
}