2012-08-20 17:06:21 +04:00
/*
2013-01-31 05:50:09 +04:00
* Copyright ( C ) 2011 - 2013 Red Hat , Inc .
2012-08-20 17:06:21 +04: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
2012-09-21 02:30:55 +04:00
* License along with this library . If not , see
2012-08-20 17:06:21 +04:00
* < http : //www.gnu.org/licenses/>.
*
*/
# include <config.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
# include "qemumonitortestutils.h"
2012-12-13 19:49:48 +04:00
# include "virthread.h"
2013-07-26 16:22:10 +04:00
# include "qemu/qemu_processpriv.h"
2012-08-20 17:06:21 +04:00
# include "qemu/qemu_monitor.h"
2013-07-25 14:08:25 +04:00
# include "qemu/qemu_agent.h"
2012-08-20 17:06:21 +04:00
# include "rpc/virnetsocket.h"
2012-12-12 22:06:53 +04:00
# include "viralloc.h"
2012-12-12 21:59:27 +04:00
# include "virlog.h"
2012-12-13 22:21:53 +04:00
# include "virerror.h"
2013-04-03 14:36:23 +04:00
# include "virstring.h"
2012-08-20 17:06:21 +04:00
# define VIR_FROM_THIS VIR_FROM_NONE
2014-02-28 16:16:17 +04:00
VIR_LOG_INIT ( " tests.qemumonitortestutils " ) ;
2012-08-20 17:06:21 +04:00
struct _qemuMonitorTestItem {
2013-07-25 13:48:11 +04:00
qemuMonitorTestResponseCallback cb ;
void * opaque ;
virFreeCallback freecb ;
2012-08-20 17:06:21 +04:00
} ;
struct _qemuMonitorTest {
virMutex lock ;
virThread thread ;
bool json ;
bool quit ;
bool running ;
2013-10-02 20:20:18 +04:00
bool started ;
2012-08-20 17:06:21 +04:00
char * incoming ;
size_t incomingLength ;
size_t incomingCapacity ;
char * outgoing ;
size_t outgoingLength ;
size_t outgoingCapacity ;
virNetSocketPtr server ;
virNetSocketPtr client ;
qemuMonitorPtr mon ;
2013-07-25 14:08:25 +04:00
qemuAgentPtr agent ;
2012-08-20 17:06:21 +04:00
2012-11-12 18:33:55 +04:00
char * tmpdir ;
2012-08-20 17:06:21 +04:00
size_t nitems ;
qemuMonitorTestItemPtr * items ;
virDomainObjPtr vm ;
} ;
2013-07-25 13:48:11 +04:00
static void
qemuMonitorTestItemFree ( qemuMonitorTestItemPtr item )
{
if ( ! item )
return ;
if ( item - > freecb )
( item - > freecb ) ( item - > opaque ) ;
VIR_FREE ( item ) ;
}
2012-08-20 17:06:21 +04:00
2013-07-18 18:17:31 +04:00
2012-08-20 17:06:21 +04:00
/*
2013-04-20 00:18:14 +04:00
* Appends data for a reply to the outgoing buffer
2012-08-20 17:06:21 +04:00
*/
2013-07-25 13:48:11 +04:00
int
2013-07-18 18:17:31 +04:00
qemuMonitorTestAddReponse ( qemuMonitorTestPtr test ,
const char * response )
2012-08-20 17:06:21 +04:00
{
size_t want = strlen ( response ) + 2 ;
size_t have = test - > outgoingCapacity - test - > outgoingLength ;
2013-07-29 16:29:15 +04:00
VIR_DEBUG ( " Adding response to monitor command: '%s " , response ) ;
2012-08-20 17:06:21 +04:00
if ( have < want ) {
size_t need = want - have ;
2013-07-04 14:20:21 +04:00
if ( VIR_EXPAND_N ( test - > outgoing , test - > outgoingCapacity , need ) < 0 )
2012-08-20 17:06:21 +04:00
return - 1 ;
}
want - = 2 ;
2013-07-25 13:02:00 +04:00
memcpy ( test - > outgoing + test - > outgoingLength , response , want ) ;
memcpy ( test - > outgoing + test - > outgoingLength + want , " \r \n " , 2 ) ;
2012-08-20 17:06:21 +04:00
test - > outgoingLength + = want + 2 ;
return 0 ;
}
2013-07-29 16:29:15 +04:00
int
2013-07-25 13:48:11 +04:00
qemuMonitorTestAddUnexpectedErrorResponse ( qemuMonitorTestPtr test )
2012-08-20 17:06:21 +04:00
{
2013-07-25 14:08:25 +04:00
if ( test - > agent | | test - > json ) {
2013-07-25 13:48:11 +04:00
return qemuMonitorTestAddReponse ( test ,
" { \" error \" : "
" { \" desc \" : \" Unexpected command \" , "
" \" class \" : \" UnexpectedCommand \" } } " ) ;
2012-08-20 17:06:21 +04:00
} else {
2013-07-25 13:48:11 +04:00
return qemuMonitorTestAddReponse ( test , " unexpected command " ) ;
2012-08-20 17:06:21 +04:00
}
}
2013-07-29 16:29:15 +04:00
int ATTRIBUTE_FMT_PRINTF ( 2 , 3 )
qemuMonitorReportError ( qemuMonitorTestPtr test , const char * errmsg , . . . )
{
va_list msgargs ;
char * msg = NULL ;
char * jsonmsg = NULL ;
int ret = - 1 ;
va_start ( msgargs , errmsg ) ;
if ( virVasprintf ( & msg , errmsg , msgargs ) < 0 )
goto cleanup ;
if ( test - > agent | | test - > json ) {
tests: monitor: json: Fix error message when returning json in json
The qemu JSON monitor test allows to test also expected command
arguments. As the error from the monitor simulator is returned as a
simulated qemu error (in JSON) all other JSON contained in the error
message needs to be escaped. This will happen if the monitor command
under test receives a JSON array as an argument.
This will improve the error message from:
libvirt: error : internal error: cannot parse json { "error": { "desc":
"Invalid value of argument 'keys' of command 'send-key': expected 'ble'
got '[{"type":"number","data":43},{"type":"number","data":26},
{"type":"number","data":46},{"type":"number","data":32}]'",
"class": "UnexpectedCommand" } }: lexical error: invalid string in json text.
To:
libvirt: QEMU Driver error : internal error: unable to execute QEMU
command 'send-key': Invalid value of argument 'keys' of command
'send-key': expected 'ble' got '[{"type":"number","data":43},
{"type":"number","data":26},{"type":"number","data":46},
{"type":"number","data":32}]'
This improvement will not have any effect on tests executing as
expected, but it will help test development.
2014-06-03 19:12:48 +04:00
char * tmp = msg ;
msg = qemuMonitorEscapeArg ( tmp ) ;
VIR_FREE ( tmp ) ;
if ( ! msg )
goto cleanup ;
2013-07-29 16:29:15 +04:00
if ( virAsprintf ( & jsonmsg , " { \" error \" : "
" { \" desc \" : \" %s \" , "
" \" class \" : \" UnexpectedCommand \" } } " ,
msg ) < 0 )
goto cleanup ;
} else {
if ( virAsprintf ( & jsonmsg , " error: '%s' " , msg ) < 0 )
goto cleanup ;
}
ret = qemuMonitorTestAddReponse ( test , jsonmsg ) ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-07-29 16:29:15 +04:00
va_end ( msgargs ) ;
VIR_FREE ( msg ) ;
VIR_FREE ( jsonmsg ) ;
return ret ;
}
2013-07-18 18:17:31 +04:00
static int
2013-07-25 13:48:11 +04:00
qemuMonitorTestProcessCommand ( qemuMonitorTestPtr test ,
const char * cmdstr )
2012-08-20 17:06:21 +04:00
{
2013-07-25 13:48:11 +04:00
int ret ;
2012-08-20 17:06:21 +04:00
2013-07-29 16:29:15 +04:00
VIR_DEBUG ( " Processing string from monitor handler: '%s " , cmdstr ) ;
2013-07-25 13:48:11 +04:00
if ( test - > nitems = = 0 ) {
return qemuMonitorTestAddUnexpectedErrorResponse ( test ) ;
2012-08-20 17:06:21 +04:00
} else {
2013-07-25 13:48:11 +04:00
qemuMonitorTestItemPtr item = test - > items [ 0 ] ;
ret = ( item - > cb ) ( test , item , cmdstr ) ;
qemuMonitorTestItemFree ( item ) ;
if ( VIR_DELETE_ELEMENT ( test - > items , 0 , test - > nitems ) < 0 )
return - 1 ;
2012-08-20 17:06:21 +04:00
}
return ret ;
}
2013-07-18 18:17:31 +04:00
2012-08-20 17:06:21 +04:00
/*
* Handles read / write of monitor data on the monitor server side
*/
2013-07-18 18:17:31 +04:00
static void
qemuMonitorTestIO ( virNetSocketPtr sock ,
int events ,
void * opaque )
2012-08-20 17:06:21 +04:00
{
qemuMonitorTestPtr test = opaque ;
bool err = false ;
virMutexLock ( & test - > lock ) ;
2013-02-23 01:56:21 +04:00
if ( test - > quit ) {
virMutexUnlock ( & test - > lock ) ;
return ;
}
2012-08-20 17:06:21 +04:00
if ( events & VIR_EVENT_HANDLE_WRITABLE ) {
ssize_t ret ;
if ( ( ret = virNetSocketWrite ( sock ,
test - > outgoing ,
test - > outgoingLength ) ) < 0 ) {
err = true ;
goto cleanup ;
}
memmove ( test - > outgoing ,
test - > outgoing + ret ,
test - > outgoingLength - ret ) ;
test - > outgoingLength - = ret ;
if ( ( test - > outgoingCapacity - test - > outgoingLength ) > 1024 )
VIR_SHRINK_N ( test - > outgoing , test - > outgoingCapacity , 1024 ) ;
}
if ( events & VIR_EVENT_HANDLE_READABLE ) {
ssize_t ret , used ;
char * t1 , * t2 ;
if ( ( test - > incomingCapacity - test - > incomingLength ) < 1024 ) {
if ( VIR_EXPAND_N ( test - > incoming , test - > incomingCapacity , 1024 ) < 0 ) {
err = true ;
goto cleanup ;
}
}
if ( ( ret = virNetSocketRead ( sock ,
test - > incoming + test - > incomingLength ,
( test - > incomingCapacity - test - > incomingLength ) - 1 ) ) < 0 ) {
err = true ;
goto cleanup ;
}
test - > incomingLength + = ret ;
test - > incoming [ test - > incomingLength ] = ' \0 ' ;
/* Look to see if we've got a complete line, and
* if so , handle that command
*/
t1 = test - > incoming ;
2013-07-25 14:03:29 +04:00
while ( ( t2 = strstr ( t1 , " \n " ) ) ) {
2012-08-20 17:06:21 +04:00
* t2 = ' \0 ' ;
if ( qemuMonitorTestProcessCommand ( test , t1 ) < 0 ) {
err = true ;
goto cleanup ;
}
2013-07-25 14:03:29 +04:00
t1 = t2 + 1 ;
2012-08-20 17:06:21 +04:00
}
used = t1 - test - > incoming ;
memmove ( test - > incoming , t1 , test - > incomingLength - used ) ;
test - > incomingLength - = used ;
if ( ( test - > incomingCapacity - test - > incomingLength ) > 1024 ) {
VIR_SHRINK_N ( test - > incoming ,
test - > incomingCapacity ,
1024 ) ;
}
}
if ( events & ( VIR_EVENT_HANDLE_HANGUP |
VIR_EVENT_HANDLE_ERROR ) )
err = true ;
2014-03-25 10:53:44 +04:00
cleanup :
2012-08-20 17:06:21 +04:00
if ( err ) {
virNetSocketRemoveIOCallback ( sock ) ;
virNetSocketClose ( sock ) ;
virObjectUnref ( test - > client ) ;
test - > client = NULL ;
} else {
events = VIR_EVENT_HANDLE_READABLE ;
if ( test - > outgoingLength )
events | = VIR_EVENT_HANDLE_WRITABLE ;
virNetSocketUpdateIOCallback ( sock , events ) ;
}
virMutexUnlock ( & test - > lock ) ;
}
2013-07-18 18:17:31 +04:00
static void
qemuMonitorTestWorker ( void * opaque )
2012-08-20 17:06:21 +04:00
{
qemuMonitorTestPtr test = opaque ;
virMutexLock ( & test - > lock ) ;
while ( ! test - > quit ) {
virMutexUnlock ( & test - > lock ) ;
if ( virEventRunDefaultImpl ( ) < 0 ) {
2013-02-04 22:31:46 +04:00
virMutexLock ( & test - > lock ) ;
2012-08-20 17:06:21 +04:00
test - > quit = true ;
break ;
}
virMutexLock ( & test - > lock ) ;
}
test - > running = false ;
virMutexUnlock ( & test - > lock ) ;
return ;
}
2013-07-18 18:17:31 +04:00
2012-11-12 14:34:41 +04:00
static void
2013-07-18 18:17:31 +04:00
qemuMonitorTestFreeTimer ( int timer ATTRIBUTE_UNUSED ,
void * opaque ATTRIBUTE_UNUSED )
2012-11-12 14:34:41 +04:00
{
/* nothing to be done here */
}
2013-07-18 18:17:31 +04:00
void
qemuMonitorTestFree ( qemuMonitorTestPtr test )
2012-08-20 17:06:21 +04:00
{
size_t i ;
2012-11-12 14:34:41 +04:00
int timer = - 1 ;
2012-08-20 17:06:21 +04:00
if ( ! test )
return ;
virMutexLock ( & test - > lock ) ;
if ( test - > running ) {
test - > quit = true ;
2012-11-12 14:34:41 +04:00
/* HACK: Add a dummy timeout to break event loop */
timer = virEventAddTimeout ( 0 , qemuMonitorTestFreeTimer , NULL , NULL ) ;
2012-08-20 17:06:21 +04:00
}
virMutexUnlock ( & test - > lock ) ;
if ( test - > client ) {
virNetSocketRemoveIOCallback ( test - > client ) ;
virNetSocketClose ( test - > client ) ;
virObjectUnref ( test - > client ) ;
}
virObjectUnref ( test - > server ) ;
if ( test - > mon ) {
2013-01-10 01:00:32 +04:00
virObjectUnlock ( test - > mon ) ;
2012-08-20 17:06:21 +04:00
qemuMonitorClose ( test - > mon ) ;
}
2013-07-25 14:08:25 +04:00
if ( test - > agent ) {
virObjectUnlock ( test - > agent ) ;
qemuAgentClose ( test - > agent ) ;
}
2012-08-20 17:06:21 +04:00
virObjectUnref ( test - > vm ) ;
2013-10-02 20:20:18 +04:00
if ( test - > started )
2013-07-22 18:57:08 +04:00
virThreadJoin ( & test - > thread ) ;
2012-08-20 17:06:21 +04:00
2012-11-12 14:34:41 +04:00
if ( timer ! = - 1 )
virEventRemoveTimeout ( timer ) ;
2013-02-04 22:31:46 +04:00
VIR_FREE ( test - > incoming ) ;
VIR_FREE ( test - > outgoing ) ;
2013-05-21 11:53:48 +04:00
for ( i = 0 ; i < test - > nitems ; i + + )
2012-08-20 17:06:21 +04:00
qemuMonitorTestItemFree ( test - > items [ i ] ) ;
VIR_FREE ( test - > items ) ;
2012-11-12 18:33:55 +04:00
if ( test - > tmpdir & & rmdir ( test - > tmpdir ) < 0 )
VIR_WARN ( " Failed to remove tempdir: %s " , strerror ( errno ) ) ;
VIR_FREE ( test - > tmpdir ) ;
2012-08-20 17:06:21 +04:00
virMutexDestroy ( & test - > lock ) ;
VIR_FREE ( test ) ;
}
int
2013-07-25 13:48:11 +04:00
qemuMonitorTestAddHandler ( qemuMonitorTestPtr test ,
qemuMonitorTestResponseCallback cb ,
void * opaque ,
virFreeCallback freecb )
2012-08-20 17:06:21 +04:00
{
qemuMonitorTestItemPtr item ;
if ( VIR_ALLOC ( item ) < 0 )
2013-07-04 14:20:21 +04:00
goto error ;
2012-08-20 17:06:21 +04:00
2013-07-25 13:48:11 +04:00
item - > cb = cb ;
item - > freecb = freecb ;
item - > opaque = opaque ;
2012-08-20 17:06:21 +04:00
virMutexLock ( & test - > lock ) ;
2013-07-18 19:09:41 +04:00
if ( VIR_APPEND_ELEMENT ( test - > items , test - > nitems , item ) < 0 ) {
2012-08-20 17:06:21 +04:00
virMutexUnlock ( & test - > lock ) ;
2013-07-04 14:20:21 +04:00
goto error ;
2012-08-20 17:06:21 +04:00
}
virMutexUnlock ( & test - > lock ) ;
return 0 ;
2014-03-25 10:53:44 +04:00
error :
2013-07-25 13:48:11 +04:00
if ( freecb )
( freecb ) ( opaque ) ;
VIR_FREE ( item ) ;
2012-08-20 17:06:21 +04:00
return - 1 ;
}
2013-07-25 13:48:11 +04:00
void *
qemuMonitorTestItemGetPrivateData ( qemuMonitorTestItemPtr item )
{
return item ? item - > opaque : NULL ;
}
2013-07-25 17:42:38 +04:00
typedef struct _qemuMonitorTestCommandArgs qemuMonitorTestCommandArgs ;
typedef qemuMonitorTestCommandArgs * qemuMonitorTestCommandArgsPtr ;
struct _qemuMonitorTestCommandArgs {
char * argname ;
char * argval ;
2013-07-25 13:48:11 +04:00
} ;
2013-07-25 17:42:38 +04:00
struct qemuMonitorTestHandlerData {
char * command_name ;
char * response ;
size_t nargs ;
qemuMonitorTestCommandArgsPtr args ;
} ;
static void
qemuMonitorTestHandlerDataFree ( void * opaque )
{
struct qemuMonitorTestHandlerData * data = opaque ;
size_t i ;
if ( ! data )
return ;
for ( i = 0 ; i < data - > nargs ; i + + ) {
VIR_FREE ( data - > args [ i ] . argname ) ;
VIR_FREE ( data - > args [ i ] . argval ) ;
}
VIR_FREE ( data - > command_name ) ;
VIR_FREE ( data - > response ) ;
VIR_FREE ( data - > args ) ;
VIR_FREE ( data ) ;
}
2013-07-25 13:48:11 +04:00
static int
qemuMonitorTestProcessCommandDefault ( qemuMonitorTestPtr test ,
qemuMonitorTestItemPtr item ,
const char * cmdstr )
{
2013-07-25 17:42:38 +04:00
struct qemuMonitorTestHandlerData * data = item - > opaque ;
2013-07-25 13:48:11 +04:00
virJSONValuePtr val = NULL ;
char * cmdcopy = NULL ;
const char * cmdname ;
char * tmp ;
int ret = - 1 ;
2013-07-25 14:08:25 +04:00
if ( test - > agent | | test - > json ) {
2013-07-25 13:48:11 +04:00
if ( ! ( val = virJSONValueFromString ( cmdstr ) ) )
return - 1 ;
if ( ! ( cmdname = virJSONValueObjectGetString ( val , " execute " ) ) ) {
2013-07-29 16:29:15 +04:00
ret = qemuMonitorReportError ( test , " Missing command name in %s " , cmdstr ) ;
2013-07-25 13:48:11 +04:00
goto cleanup ;
}
} else {
if ( VIR_STRDUP ( cmdcopy , cmdstr ) < 0 )
return - 1 ;
cmdname = cmdcopy ;
if ( ! ( tmp = strchr ( cmdcopy , ' ' ) ) ) {
2013-07-29 16:29:15 +04:00
ret = qemuMonitorReportError ( test ,
" Cannot find command name in '%s' " ,
cmdstr ) ;
2013-07-25 13:48:11 +04:00
goto cleanup ;
}
* tmp = ' \0 ' ;
}
2013-09-19 17:19:08 +04:00
if ( data - > command_name & & STRNEQ ( data - > command_name , cmdname ) )
2013-07-25 13:48:11 +04:00
ret = qemuMonitorTestAddUnexpectedErrorResponse ( test ) ;
else
ret = qemuMonitorTestAddReponse ( test , data - > response ) ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-07-25 13:48:11 +04:00
VIR_FREE ( cmdcopy ) ;
virJSONValueFree ( val ) ;
return ret ;
}
int
qemuMonitorTestAddItem ( qemuMonitorTestPtr test ,
const char * command_name ,
const char * response )
{
2013-07-25 17:42:38 +04:00
struct qemuMonitorTestHandlerData * data ;
2013-07-25 13:48:11 +04:00
if ( VIR_ALLOC ( data ) < 0 )
return - 1 ;
2013-07-25 17:42:38 +04:00
if ( VIR_STRDUP ( data - > command_name , command_name ) < 0 | |
VIR_STRDUP ( data - > response , response ) < 0 ) {
qemuMonitorTestHandlerDataFree ( data ) ;
return - 1 ;
}
2013-07-25 13:48:11 +04:00
return qemuMonitorTestAddHandler ( test ,
qemuMonitorTestProcessCommandDefault ,
2013-07-25 17:42:38 +04:00
data , qemuMonitorTestHandlerDataFree ) ;
2013-07-25 13:48:11 +04:00
}
2012-08-20 17:06:21 +04:00
2013-07-25 14:08:25 +04:00
static int
qemuMonitorTestProcessGuestAgentSync ( qemuMonitorTestPtr test ,
qemuMonitorTestItemPtr item ATTRIBUTE_UNUSED ,
const char * cmdstr )
{
virJSONValuePtr val = NULL ;
virJSONValuePtr args ;
unsigned long long id ;
const char * cmdname ;
char * retmsg = NULL ;
int ret = - 1 ;
if ( ! ( val = virJSONValueFromString ( cmdstr ) ) )
return - 1 ;
if ( ! ( cmdname = virJSONValueObjectGetString ( val , " execute " ) ) ) {
2013-07-29 16:29:15 +04:00
ret = qemuMonitorReportError ( test , " Missing guest-sync command name " ) ;
2013-07-25 14:08:25 +04:00
goto cleanup ;
}
if ( STRNEQ ( cmdname , " guest-sync " ) ) {
ret = qemuMonitorTestAddUnexpectedErrorResponse ( test ) ;
goto cleanup ;
}
if ( ! ( args = virJSONValueObjectGet ( val , " arguments " ) ) ) {
2013-07-29 16:29:15 +04:00
ret = qemuMonitorReportError ( test , " Missing arguments for guest-sync " ) ;
2013-07-25 14:08:25 +04:00
goto cleanup ;
}
if ( virJSONValueObjectGetNumberUlong ( args , " id " , & id ) ) {
2013-07-29 16:29:15 +04:00
ret = qemuMonitorReportError ( test , " Missing id for guest sync " ) ;
2013-07-25 14:08:25 +04:00
goto cleanup ;
}
if ( virAsprintf ( & retmsg , " { \" return \" :%llu} " , id ) < 0 )
goto cleanup ;
ret = qemuMonitorTestAddReponse ( test , retmsg ) ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-07-25 14:08:25 +04:00
virJSONValueFree ( val ) ;
VIR_FREE ( retmsg ) ;
return ret ;
}
int
qemuMonitorTestAddAgentSyncResponse ( qemuMonitorTestPtr test )
{
if ( ! test - > agent ) {
virReportError ( VIR_ERR_INTERNAL_ERROR , " %s " ,
" This test is not an agent test " ) ;
return - 1 ;
}
return qemuMonitorTestAddHandler ( test ,
qemuMonitorTestProcessGuestAgentSync ,
NULL , NULL ) ;
}
2013-07-25 17:42:38 +04:00
static int
qemuMonitorTestProcessCommandWithArgs ( qemuMonitorTestPtr test ,
qemuMonitorTestItemPtr item ,
const char * cmdstr )
{
struct qemuMonitorTestHandlerData * data = item - > opaque ;
virJSONValuePtr val = NULL ;
virJSONValuePtr args ;
virJSONValuePtr argobj ;
char * argstr = NULL ;
const char * cmdname ;
size_t i ;
int ret = - 1 ;
if ( ! ( val = virJSONValueFromString ( cmdstr ) ) )
return - 1 ;
if ( ! ( cmdname = virJSONValueObjectGetString ( val , " execute " ) ) ) {
ret = qemuMonitorReportError ( test , " Missing command name in %s " , cmdstr ) ;
goto cleanup ;
}
2013-09-19 17:19:08 +04:00
if ( data - > command_name & &
STRNEQ ( data - > command_name , cmdname ) ) {
2013-07-25 17:42:38 +04:00
ret = qemuMonitorTestAddUnexpectedErrorResponse ( test ) ;
goto cleanup ;
}
if ( ! ( args = virJSONValueObjectGet ( val , " arguments " ) ) ) {
ret = qemuMonitorReportError ( test ,
" Missing arguments section for command '%s' " ,
2013-09-19 17:19:08 +04:00
NULLSTR ( data - > command_name ) ) ;
2013-07-25 17:42:38 +04:00
goto cleanup ;
}
/* validate the args */
for ( i = 0 ; i < data - > nargs ; i + + ) {
qemuMonitorTestCommandArgsPtr arg = & data - > args [ i ] ;
if ( ! ( argobj = virJSONValueObjectGet ( args , arg - > argname ) ) ) {
ret = qemuMonitorReportError ( test ,
" Missing argument '%s' for command '%s' " ,
2013-09-19 17:19:08 +04:00
arg - > argname ,
NULLSTR ( data - > command_name ) ) ;
2013-07-25 17:42:38 +04:00
goto cleanup ;
}
/* convert the argument to string */
if ( ! ( argstr = virJSONValueToString ( argobj , false ) ) )
goto cleanup ;
/* verify that the argument value is expected */
if ( STRNEQ ( argstr , arg - > argval ) ) {
ret = qemuMonitorReportError ( test ,
" Invalid value of argument '%s' "
" of command '%s': "
" expected '%s' got '%s' " ,
2013-09-19 17:19:08 +04:00
arg - > argname ,
NULLSTR ( data - > command_name ) ,
2013-07-25 17:42:38 +04:00
arg - > argval , argstr ) ;
goto cleanup ;
}
VIR_FREE ( argstr ) ;
}
/* arguments checked out, return the response */
ret = qemuMonitorTestAddReponse ( test , data - > response ) ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-07-25 17:42:38 +04:00
VIR_FREE ( argstr ) ;
virJSONValueFree ( val ) ;
return ret ;
}
/* this allows to add a responder that is able to check
* a ( shallow ) structure of arguments for a command */
int
qemuMonitorTestAddItemParams ( qemuMonitorTestPtr test ,
const char * cmdname ,
const char * response ,
. . . )
{
struct qemuMonitorTestHandlerData * data ;
const char * argname ;
const char * argval ;
va_list args ;
va_start ( args , response ) ;
if ( VIR_ALLOC ( data ) < 0 )
2013-08-01 18:33:23 +04:00
goto error ;
2013-07-25 17:42:38 +04:00
if ( VIR_STRDUP ( data - > command_name , cmdname ) < 0 | |
VIR_STRDUP ( data - > response , response ) < 0 )
goto error ;
while ( ( argname = va_arg ( args , char * ) ) ) {
size_t i ;
if ( ! ( argval = va_arg ( args , char * ) ) ) {
virReportError ( VIR_ERR_INTERNAL_ERROR ,
" Missing argument value for argument '%s' " ,
argname ) ;
goto error ;
}
i = data - > nargs ;
if ( VIR_EXPAND_N ( data - > args , data - > nargs , 1 ) )
goto error ;
if ( VIR_STRDUP ( data - > args [ i ] . argname , argname ) < 0 | |
VIR_STRDUP ( data - > args [ i ] . argval , argval ) < 0 )
goto error ;
}
va_end ( args ) ;
return qemuMonitorTestAddHandler ( test ,
qemuMonitorTestProcessCommandWithArgs ,
data , qemuMonitorTestHandlerDataFree ) ;
2014-03-25 10:53:44 +04:00
error :
2013-07-25 17:42:38 +04:00
va_end ( args ) ;
qemuMonitorTestHandlerDataFree ( data ) ;
return - 1 ;
}
2013-07-18 18:17:31 +04:00
static void
qemuMonitorTestEOFNotify ( qemuMonitorPtr mon ATTRIBUTE_UNUSED ,
2013-07-25 21:26:15 +04:00
virDomainObjPtr vm ATTRIBUTE_UNUSED ,
void * opaque ATTRIBUTE_UNUSED )
2012-08-20 17:06:21 +04:00
{
}
2013-07-18 18:17:31 +04:00
static void
qemuMonitorTestErrorNotify ( qemuMonitorPtr mon ATTRIBUTE_UNUSED ,
2013-07-25 21:26:15 +04:00
virDomainObjPtr vm ATTRIBUTE_UNUSED ,
void * opaque ATTRIBUTE_UNUSED )
2012-08-20 17:06:21 +04:00
{
}
2013-07-25 13:48:11 +04:00
static qemuMonitorCallbacks qemuMonitorTestCallbacks = {
2012-08-20 17:06:21 +04:00
. eofNotify = qemuMonitorTestEOFNotify ,
. errorNotify = qemuMonitorTestErrorNotify ,
2013-07-26 16:22:10 +04:00
. domainDeviceDeleted = qemuProcessHandleDeviceDeleted ,
2012-08-20 17:06:21 +04:00
} ;
2013-07-18 18:17:31 +04:00
2013-07-25 14:08:25 +04:00
static void
qemuMonitorTestAgentNotify ( qemuAgentPtr agent ATTRIBUTE_UNUSED ,
virDomainObjPtr vm ATTRIBUTE_UNUSED )
{
}
static qemuAgentCallbacks qemuMonitorTestAgentCallbacks = {
. eofNotify = qemuMonitorTestAgentNotify ,
. errorNotify = qemuMonitorTestAgentNotify ,
} ;
2013-07-22 18:59:22 +04:00
static qemuMonitorTestPtr
qemuMonitorCommonTestNew ( virDomainXMLOptionPtr xmlopt ,
2013-07-25 21:17:44 +04:00
virDomainObjPtr vm ,
2013-07-22 18:59:22 +04:00
virDomainChrSourceDefPtr src )
2012-08-20 17:06:21 +04:00
{
2012-10-29 12:28:15 +04:00
qemuMonitorTestPtr test = NULL ;
2012-11-12 18:33:55 +04:00
char * path = NULL ;
char * tmpdir_template = NULL ;
2012-08-20 17:06:21 +04:00
2012-11-12 18:33:55 +04:00
if ( VIR_ALLOC ( test ) < 0 )
2013-07-04 14:20:21 +04:00
goto error ;
2012-08-20 17:06:21 +04:00
if ( virMutexInit ( & test - > lock ) < 0 ) {
virReportError ( VIR_ERR_INTERNAL_ERROR , " %s " ,
" Cannot initialize mutex " ) ;
VIR_FREE ( test ) ;
return NULL ;
}
2013-05-03 16:52:21 +04:00
if ( VIR_STRDUP ( tmpdir_template , " /tmp/libvirt_XXXXXX " ) < 0 )
goto error ;
2012-11-12 18:33:55 +04:00
if ( ! ( test - > tmpdir = mkdtemp ( tmpdir_template ) ) ) {
virReportSystemError ( errno , " %s " ,
" Failed to create temporary directory " ) ;
goto error ;
}
tmpdir_template = NULL ;
if ( virAsprintf ( & path , " %s/qemumonitorjsontest.sock " , test - > tmpdir ) < 0 )
2013-07-04 14:20:21 +04:00
goto error ;
2012-11-12 18:33:55 +04:00
2013-07-25 21:17:44 +04:00
if ( vm ) {
virObjectRef ( vm ) ;
test - > vm = vm ;
} else {
test - > vm = virDomainObjNew ( xmlopt ) ;
if ( ! test - > vm )
goto error ;
}
2012-08-20 17:06:21 +04:00
2013-10-09 15:13:45 +04:00
if ( virNetSocketNewListenUNIX ( path , 0700 , geteuid ( ) , getegid ( ) ,
2012-08-20 17:06:21 +04:00
& test - > server ) < 0 )
goto error ;
2013-07-22 18:59:22 +04:00
memset ( src , 0 , sizeof ( * src ) ) ;
src - > type = VIR_DOMAIN_CHR_TYPE_UNIX ;
src - > data . nix . path = ( char * ) path ;
src - > data . nix . listen = false ;
2013-09-25 18:28:55 +04:00
path = NULL ;
2012-08-20 17:06:21 +04:00
if ( virNetSocketListen ( test - > server , 1 ) < 0 )
goto error ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-07-22 18:59:22 +04:00
return test ;
2014-03-25 10:53:44 +04:00
error :
2013-09-25 18:28:55 +04:00
VIR_FREE ( path ) ;
2013-07-22 18:59:22 +04:00
VIR_FREE ( tmpdir_template ) ;
qemuMonitorTestFree ( test ) ;
test = NULL ;
goto cleanup ;
}
static int
qemuMonitorCommonTestInit ( qemuMonitorTestPtr test )
{
2013-07-25 13:48:11 +04:00
int events = VIR_EVENT_HANDLE_READABLE ;
2013-07-22 18:59:22 +04:00
if ( ! test )
return - 1 ;
2012-08-20 17:06:21 +04:00
if ( virNetSocketAccept ( test - > server , & test - > client ) < 0 )
goto error ;
2013-07-22 18:59:22 +04:00
if ( ! test - > client )
2012-09-06 19:14:25 +04:00
goto error ;
2013-07-25 13:48:11 +04:00
if ( test - > outgoingLength > 0 )
events = VIR_EVENT_HANDLE_WRITABLE ;
2012-08-20 17:06:21 +04:00
if ( virNetSocketAddIOCallback ( test - > client ,
2013-07-25 13:48:11 +04:00
events ,
2012-08-20 17:06:21 +04:00
qemuMonitorTestIO ,
test ,
NULL ) < 0 )
goto error ;
virMutexLock ( & test - > lock ) ;
if ( virThreadCreate ( & test - > thread ,
true ,
qemuMonitorTestWorker ,
test ) < 0 ) {
virMutexUnlock ( & test - > lock ) ;
goto error ;
}
2013-10-02 20:20:18 +04:00
test - > started = test - > running = true ;
2012-08-20 17:06:21 +04:00
virMutexUnlock ( & test - > lock ) ;
2013-07-22 18:59:22 +04:00
return 0 ;
2014-03-25 10:53:44 +04:00
error :
2013-07-22 18:59:22 +04:00
return - 1 ;
}
# define QEMU_JSON_GREETING "{\"QMP\":"\
" { \" version \" : " \
" { \" qemu \" : " \
" { \" micro \" : 1, " \
" \" minor \" : 0, " \
" \" major \" : 1 " \
" }, " \
" \" package \" : \" (qemu-kvm-1.0.1) " \
" \" }, " \
" \" capabilities \" : [] " \
" } " \
" } "
/* We skip the normal handshake reply of "{\"execute\":\"qmp_capabilities\"}" */
# define QEMU_TEXT_GREETING "QEMU 1.0,1 monitor - type 'help' for more information"
qemuMonitorTestPtr
2013-07-25 21:17:44 +04:00
qemuMonitorTestNew ( bool json ,
virDomainXMLOptionPtr xmlopt ,
2013-07-25 21:28:51 +04:00
virDomainObjPtr vm ,
2013-09-19 15:56:30 +04:00
virQEMUDriverPtr driver ,
const char * greeting )
2013-07-22 18:59:22 +04:00
{
qemuMonitorTestPtr test = NULL ;
virDomainChrSourceDef src ;
2013-09-25 18:30:27 +04:00
memset ( & src , 0 , sizeof ( src ) ) ;
2013-07-25 21:17:44 +04:00
if ( ! ( test = qemuMonitorCommonTestNew ( xmlopt , vm , & src ) ) )
2013-07-22 18:59:22 +04:00
goto error ;
test - > json = json ;
if ( ! ( test - > mon = qemuMonitorOpen ( test - > vm ,
& src ,
json ,
2013-07-25 21:26:15 +04:00
& qemuMonitorTestCallbacks ,
2013-07-25 21:28:51 +04:00
driver ) ) )
2013-07-22 18:59:22 +04:00
goto error ;
virObjectLock ( test - > mon ) ;
2013-09-19 15:56:30 +04:00
if ( ! greeting )
greeting = json ? QEMU_JSON_GREETING : QEMU_TEXT_GREETING ;
if ( qemuMonitorTestAddReponse ( test , greeting ) < 0 )
2013-07-22 18:59:22 +04:00
goto error ;
if ( qemuMonitorCommonTestInit ( test ) < 0 )
goto error ;
virDomainChrSourceDefClear ( & src ) ;
2012-08-20 17:06:21 +04:00
return test ;
2014-03-25 10:53:44 +04:00
error :
2013-07-25 14:08:25 +04:00
virDomainChrSourceDefClear ( & src ) ;
qemuMonitorTestFree ( test ) ;
return NULL ;
}
qemuMonitorTestPtr
qemuMonitorTestNewAgent ( virDomainXMLOptionPtr xmlopt )
{
qemuMonitorTestPtr test = NULL ;
virDomainChrSourceDef src ;
2013-09-25 18:30:27 +04:00
memset ( & src , 0 , sizeof ( src ) ) ;
2013-07-25 21:17:44 +04:00
if ( ! ( test = qemuMonitorCommonTestNew ( xmlopt , NULL , & src ) ) )
2013-07-25 14:08:25 +04:00
goto error ;
if ( ! ( test - > agent = qemuAgentOpen ( test - > vm ,
& src ,
& qemuMonitorTestAgentCallbacks ) ) )
goto error ;
virObjectLock ( test - > agent ) ;
if ( qemuMonitorCommonTestInit ( test ) < 0 )
goto error ;
virDomainChrSourceDefClear ( & src ) ;
return test ;
2014-03-25 10:53:44 +04:00
error :
2013-07-25 14:08:25 +04:00
virDomainChrSourceDefClear ( & src ) ;
2012-08-20 17:06:21 +04:00
qemuMonitorTestFree ( test ) ;
2013-07-22 18:59:22 +04:00
return NULL ;
2012-08-20 17:06:21 +04:00
}
2013-07-18 18:17:31 +04:00
qemuMonitorPtr
qemuMonitorTestGetMonitor ( qemuMonitorTestPtr test )
2012-08-20 17:06:21 +04:00
{
return test - > mon ;
}
2013-07-25 14:08:25 +04:00
qemuAgentPtr
qemuMonitorTestGetAgent ( qemuMonitorTestPtr test )
{
return test - > agent ;
}