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"
2012-08-20 17:06:21 +04:00
# include "qemu/qemu_monitor.h"
# 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
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 ;
char * incoming ;
size_t incomingLength ;
size_t incomingCapacity ;
char * outgoing ;
size_t outgoingLength ;
size_t outgoingCapacity ;
virNetSocketPtr server ;
virNetSocketPtr client ;
qemuMonitorPtr mon ;
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 ;
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-18 18:17:31 +04:00
static int
2013-07-25 13:48:11 +04:00
qemuMonitorTestAddUnexpectedErrorResponse ( qemuMonitorTestPtr test )
2012-08-20 17:06:21 +04:00
{
2013-07-25 13:48:11 +04:00
if ( test - > json ) {
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-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-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 ;
cleanup :
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 ) ;
}
virObjectUnref ( test - > vm ) ;
2013-07-22 18:57:08 +04:00
if ( test - > running )
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 ;
2013-05-03 16:52:21 +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 ;
}
struct qemuMonitorTestDefaultHandlerData {
const char * command_name ;
const char * response ;
} ;
static int
qemuMonitorTestProcessCommandDefault ( qemuMonitorTestPtr test ,
qemuMonitorTestItemPtr item ,
const char * cmdstr )
{
struct qemuMonitorTestDefaultHandlerData * data = item - > opaque ;
virJSONValuePtr val = NULL ;
char * cmdcopy = NULL ;
const char * cmdname ;
char * tmp ;
int ret = - 1 ;
if ( test - > json ) {
if ( ! ( val = virJSONValueFromString ( cmdstr ) ) )
return - 1 ;
if ( ! ( cmdname = virJSONValueObjectGetString ( val , " execute " ) ) ) {
virReportError ( VIR_ERR_INTERNAL_ERROR ,
" Missing command name in %s " , cmdstr ) ;
goto cleanup ;
}
} else {
if ( VIR_STRDUP ( cmdcopy , cmdstr ) < 0 )
return - 1 ;
cmdname = cmdcopy ;
if ( ! ( tmp = strchr ( cmdcopy , ' ' ) ) ) {
virReportError ( VIR_ERR_INTERNAL_ERROR ,
" Cannot find command name in '%s' " , cmdstr ) ;
goto cleanup ;
}
* tmp = ' \0 ' ;
}
if ( STRNEQ ( data - > command_name , cmdname ) )
ret = qemuMonitorTestAddUnexpectedErrorResponse ( test ) ;
else
ret = qemuMonitorTestAddReponse ( test , data - > response ) ;
cleanup :
VIR_FREE ( cmdcopy ) ;
virJSONValueFree ( val ) ;
return ret ;
}
int
qemuMonitorTestAddItem ( qemuMonitorTestPtr test ,
const char * command_name ,
const char * response )
{
struct qemuMonitorTestDefaultHandlerData * data ;
if ( VIR_ALLOC ( data ) < 0 )
return - 1 ;
data - > command_name = command_name ;
data - > response = response ;
return qemuMonitorTestAddHandler ( test ,
qemuMonitorTestProcessCommandDefault ,
data ,
free ) ;
}
2012-08-20 17:06:21 +04:00
2013-07-18 18:17:31 +04:00
static void
qemuMonitorTestEOFNotify ( qemuMonitorPtr mon ATTRIBUTE_UNUSED ,
virDomainObjPtr vm 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 ,
virDomainObjPtr vm 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-18 18:17:31 +04:00
2013-07-22 18:59:22 +04:00
static qemuMonitorTestPtr
qemuMonitorCommonTestNew ( virDomainXMLOptionPtr xmlopt ,
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-03-31 22:03:42 +04:00
if ( ! ( test - > vm = virDomainObjNew ( xmlopt ) ) )
2012-08-20 17:06:21 +04:00
goto error ;
2013-07-25 13:02:00 +04:00
if ( virNetSocketNewListenUNIX ( path , 0700 , getuid ( ) , getgid ( ) ,
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 ;
2012-08-20 17:06:21 +04:00
if ( virNetSocketListen ( test - > server , 1 ) < 0 )
goto error ;
2013-07-22 18:59:22 +04:00
cleanup :
return test ;
error :
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 ;
}
test - > running = true ;
virMutexUnlock ( & test - > lock ) ;
2013-07-22 18:59:22 +04:00
return 0 ;
error :
qemuMonitorTestFree ( test ) ;
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
qemuMonitorTestNew ( bool json , virDomainXMLOptionPtr xmlopt )
{
qemuMonitorTestPtr test = NULL ;
virDomainChrSourceDef src ;
if ( ! ( test = qemuMonitorCommonTestNew ( xmlopt , & src ) ) )
goto error ;
test - > json = json ;
if ( ! ( test - > mon = qemuMonitorOpen ( test - > vm ,
& src ,
json ,
2013-07-25 13:48:11 +04:00
& qemuMonitorTestCallbacks ) ) )
2013-07-22 18:59:22 +04:00
goto error ;
virObjectLock ( test - > mon ) ;
if ( qemuMonitorTestAddReponse ( test , json ?
QEMU_JSON_GREETING :
QEMU_TEXT_GREETING ) < 0 )
goto error ;
if ( qemuMonitorCommonTestInit ( test ) < 0 )
goto error ;
virDomainChrSourceDefClear ( & src ) ;
2012-08-20 17:06:21 +04:00
return test ;
error :
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 ;
}