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"
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 )
2023-03-09 17:54:58 +03:00
vshError ( ctl , _ ( " failed to get domain '%1$s' " ) , name ) ;
2017-04-11 11:18:06 +03:00
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
2021-09-26 09:20:54 +03:00
void
virshInterfaceFree ( virInterfacePtr iface )
{
if ( ! iface )
return ;
vshSaveLibvirtHelperError ( ) ;
virInterfaceFree ( iface ) ; /* sc_prohibit_obj_free_apis_in_virsh */
}
2021-09-26 11:57:38 +03:00
void
virshNetworkFree ( virNetworkPtr network )
{
if ( ! network )
return ;
vshSaveLibvirtHelperError ( ) ;
virNetworkFree ( network ) ; /* sc_prohibit_obj_free_apis_in_virsh */
}
2021-09-26 12:44:30 +03:00
void
virshNodeDeviceFree ( virNodeDevicePtr device )
{
if ( ! device )
return ;
vshSaveLibvirtHelperError ( ) ;
virNodeDeviceFree ( device ) ; /* sc_prohibit_obj_free_apis_in_virsh */
}
2021-09-26 14:18:17 +03:00
void
virshNWFilterFree ( virNWFilterPtr nwfilter )
{
if ( ! nwfilter )
return ;
vshSaveLibvirtHelperError ( ) ;
virNWFilterFree ( nwfilter ) ; /* sc_prohibit_obj_free_apis_in_virsh */
}
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 */
}
2021-09-26 10:25:41 +03:00
void
virshStoragePoolFree ( virStoragePoolPtr pool )
{
if ( ! pool )
return ;
vshSaveLibvirtHelperError ( ) ;
virStoragePoolFree ( pool ) ; /* sc_prohibit_obj_free_apis_in_virsh */
}
2021-09-26 11:28:06 +03:00
void
virshStorageVolFree ( virStorageVolPtr vol )
{
if ( ! vol )
return ;
vshSaveLibvirtHelperError ( ) ;
virStorageVolFree ( vol ) ; /* sc_prohibit_obj_free_apis_in_virsh */
}
2021-09-26 14:27:26 +03:00
void
virshStreamFree ( virStreamPtr stream )
{
if ( ! stream )
return ;
vshSaveLibvirtHelperError ( ) ;
virStreamFree ( stream ) ; /* 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 ;
}
2023-08-16 21:47:12 +03:00
int
virshNetworkGetXMLFromNet ( vshControl * ctl ,
virNetworkPtr net ,
unsigned int flags ,
xmlDocPtr * xml ,
xmlXPathContextPtr * ctxt )
{
g_autofree char * desc = NULL ;
if ( ! ( desc = virNetworkGetXMLDesc ( net , flags ) ) ) {
vshError ( ctl , _ ( " Failed to get network description xml " ) ) ;
return - 1 ;
}
* xml = virXMLParseStringCtxt ( desc , _ ( " (network_definition) " ) , ctxt ) ;
if ( ! ( * xml ) ) {
vshError ( ctl , _ ( " Failed to parse network description xml " ) ) ;
return - 1 ;
}
return 0 ;
}
2017-04-11 17:51:32 +03:00
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 ;
}
2022-02-21 18:12:59 +03:00
VIR_ENUM_IMPL ( virshDomainBlockJob ,
VIR_DOMAIN_BLOCK_JOB_TYPE_LAST ,
N_ ( " Unknown job " ) ,
N_ ( " Block Pull " ) ,
N_ ( " Block Copy " ) ,
N_ ( " Block Commit " ) ,
N_ ( " Active Block Commit " ) ,
N_ ( " Backup " ) ,
) ;
const char *
virshDomainBlockJobToString ( int type )
{
const char * str = virshDomainBlockJobTypeToString ( type ) ;
return str ? _ ( str ) : _ ( " Unknown job " ) ;
}
2022-06-16 17:59:03 +03:00
bool
virshDumpXML ( vshControl * ctl ,
const char * xml ,
const char * url ,
const char * xpath ,
bool wrap )
{
g_autoptr ( xmlDoc ) doc = NULL ;
g_autoptr ( xmlXPathContext ) ctxt = NULL ;
g_autofree xmlNodePtr * nodes = NULL ;
int nnodes = 0 ;
size_t i ;
if ( xpath = = NULL ) {
vshPrint ( ctl , " %s " , xml ) ;
return true ;
}
2023-11-20 18:20:51 +03:00
doc = virXMLParseStringCtxtWithIndent ( xml , url , & ctxt ) ;
2022-06-16 17:59:03 +03:00
if ( ! doc )
return false ;
if ( ( nnodes = virXPathNodeSet ( xpath , ctxt , & nodes ) ) < 0 ) {
return false ;
}
if ( wrap ) {
g_autoptr ( xmlDoc ) newdoc = xmlNewDoc ( ( xmlChar * ) " 1.0 " ) ;
xmlNodePtr newroot = xmlNewNode ( NULL , ( xmlChar * ) " nodes " ) ;
g_autofree char * xmlbit = NULL ;
xmlDocSetRootElement ( newdoc , newroot ) ;
for ( i = 0 ; i < nnodes ; i + + ) {
g_autoptr ( xmlNode ) copy = xmlDocCopyNode ( nodes [ i ] , newdoc , 1 ) ;
if ( ! xmlAddChild ( newroot , copy ) )
return false ;
copy = NULL ;
}
xmlbit = virXMLNodeToString ( doc , newroot ) ;
vshPrint ( ctl , " %s \n " , xmlbit ) ;
} else {
for ( i = 0 ; i < nnodes ; i + + ) {
g_autofree char * xmlbit = virXMLNodeToString ( doc , nodes [ i ] ) ;
vshPrint ( ctl , " %s \n " , xmlbit ) ;
}
}
return true ;
}