2010-01-11 18:16:57 +03:00
// #include <config.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <signal.h>
2010-01-11 21:26:43 +03:00
# include <errno.h>
2010-01-13 10:19:24 +03:00
# include <pthread.h>
2012-02-07 20:14:25 +04:00
# include <unistd.h>
# include <fcntl.h>
2010-01-11 18:16:57 +03:00
# include <sys/types.h>
# include <sys/poll.h>
# include <libvirt/libvirt.h>
# include <libxml/xmlreader.h>
2010-01-13 18:53:55 +03:00
# include <simpleconfig.h>
2010-01-13 18:04:31 +03:00
# include "debug.h"
2010-01-13 10:19:24 +03:00
2010-01-13 18:04:31 +03:00
# define DEBUG0(fmt) dbg_printf(5,"%s:%d :: " fmt "\n", \
2010-01-11 18:16:57 +03:00
__func__ , __LINE__ )
2010-01-13 18:04:31 +03:00
# define DEBUG1(fmt, ...) dbg_printf(5, "%s:%d: " fmt "\n", \
2010-01-11 18:16:57 +03:00
__func__ , __LINE__ , __VA_ARGS__ )
2010-01-13 10:19:24 +03:00
2010-01-13 18:53:55 +03:00
# include "serial.h"
2010-01-13 10:19:24 +03:00
2010-01-11 18:16:57 +03:00
# define STREQ(a,b) (strcmp((a),(b)) == 0)
2010-01-13 10:19:24 +03:00
static pthread_t event_tid = 0 ;
static int run = 0 ;
2010-01-11 18:16:57 +03:00
/* Prototypes */
const char * eventToString ( int event ) ;
2010-01-11 21:26:43 +03:00
int myDomainEventCallback1 ( virConnectPtr conn , virDomainPtr dom ,
int event , int detail , void * opaque ) ;
2010-01-11 18:16:57 +03:00
void usage ( const char * pname ) ;
2010-01-11 21:26:43 +03:00
struct domain_info {
virDomainPtr dom ;
virDomainEventType event ;
} ;
2010-01-11 18:16:57 +03:00
2010-01-14 16:56:45 +03:00
static int
is_in_directory ( const char * dir , const char * pathspec )
{
char * last_slash = NULL ;
size_t dirlen , pathlen ;
if ( ! dir | | ! pathspec )
return 0 ;
dirlen = strlen ( dir ) ;
pathlen = strlen ( pathspec ) ;
/*
printf ( " dirlen = %d pathlen = %d \n " ,
dirlen , pathlen ) ;
*/
/* chop off trailing slashes */
while ( dirlen & & dir [ dirlen - 1 ] = = ' / ' )
- - dirlen ;
/* chop off leading slashes */
while ( dirlen & & dir [ 0 ] = = ' / ' ) {
+ + dir ;
- - dirlen ;
}
/* chop off leading slashes */
while ( pathlen & & pathspec [ 0 ] = = ' / ' ) {
+ + pathspec ;
- - pathlen ;
}
if ( ! dirlen | | ! pathlen )
return 0 ;
if ( pathlen < = dirlen )
return 0 ;
last_slash = strrchr ( pathspec , ' / ' ) ;
if ( ! last_slash )
return 0 ;
while ( * last_slash = = ' / ' & & last_slash > pathspec )
- - last_slash ;
if ( last_slash = = pathspec )
return 0 ;
pathlen = last_slash - pathspec + 1 ;
/*printf("real dirlen = %d real pathlen = %d\n",
dirlen , pathlen ) ; */
if ( pathlen ! = dirlen )
return 0 ;
/* todo - intelligently skip multiple slashes mid-path */
return ! strncmp ( dir , pathspec , dirlen ) ;
}
2010-01-11 21:26:43 +03:00
static int
2010-01-14 07:34:35 +03:00
domainStarted ( virDomainPtr mojaDomain , const char * path , int mode )
2010-01-11 18:16:57 +03:00
{
2010-01-11 21:26:43 +03:00
char dom_uuid [ 42 ] ;
char * xml ;
xmlDocPtr doc ;
xmlNodePtr cur , devices , child , serial ;
xmlAttrPtr attr , attr_mode , attr_path ;
if ( ! mojaDomain )
return - 1 ;
virDomainGetUUIDString ( mojaDomain , dom_uuid ) ;
xml = virDomainGetXMLDesc ( mojaDomain , 0 ) ;
// printf("%s\n", xml);
// @todo: free mojaDomain
// parseXML output
doc = xmlParseMemory ( xml , strlen ( xml ) ) ;
2010-01-12 16:35:47 +03:00
xmlFree ( xml ) ;
2010-01-11 21:26:43 +03:00
cur = xmlDocGetRootElement ( doc ) ;
if ( cur = = NULL ) {
fprintf ( stderr , " Empty doc \n " ) ;
xmlFreeDoc ( doc ) ;
return - 1 ;
}
if ( xmlStrcmp ( cur - > name , ( const xmlChar * ) " domain " ) ) {
2010-01-13 18:04:31 +03:00
fprintf ( stderr , " no domain? \n " ) ;
2010-01-11 21:26:43 +03:00
xmlFreeDoc ( doc ) ;
return - 1 ;
}
devices = cur - > xmlChildrenNode ;
for ( devices = cur - > xmlChildrenNode ; devices ! = NULL ;
devices = devices - > next ) {
if ( xmlStrcmp ( devices - > name , ( const xmlChar * ) " devices " ) ) {
continue ;
}
for ( child = devices - > xmlChildrenNode ; child ! = NULL ;
child = child - > next ) {
2010-01-14 07:34:35 +03:00
if ( ( ! mode & & xmlStrcmp ( child - > name , ( const xmlChar * ) " serial " ) ) | |
( mode & & xmlStrcmp ( child - > name , ( const xmlChar * ) " channel " ) ) ) {
2010-01-11 21:26:43 +03:00
continue ;
}
2010-01-13 01:36:03 +03:00
attr = xmlHasProp ( child , ( const xmlChar * ) " type " ) ;
2010-01-11 21:26:43 +03:00
if ( attr = = NULL )
continue ;
2010-01-12 16:35:47 +03:00
if ( xmlStrcmp ( attr - > children - > content ,
( const xmlChar * ) " unix " ) ) {
2010-01-11 21:26:43 +03:00
continue ;
}
for ( serial = child - > xmlChildrenNode ; serial ! = NULL ;
serial = serial - > next ) {
2010-01-12 16:35:47 +03:00
if ( xmlStrcmp ( serial - > name ,
( const xmlChar * ) " source " ) ) {
2010-01-11 21:26:43 +03:00
continue ;
}
2010-01-13 01:36:03 +03:00
attr_mode = xmlHasProp ( serial , ( const xmlChar * ) " mode " ) ;
attr_path = xmlHasProp ( serial , ( const xmlChar * ) " path " ) ;
2010-01-11 21:26:43 +03:00
2010-01-14 07:34:35 +03:00
if ( ! attr_path | | ! attr_mode )
continue ;
if ( xmlStrcmp ( attr_mode - > children - > content ,
( const xmlChar * ) " bind " ) )
continue ;
2010-01-15 01:55:00 +03:00
if ( path & & ! is_in_directory ( path , ( const char * )
2010-01-14 16:56:45 +03:00
attr_path - > children - > content ) )
continue ;
2010-01-14 07:34:35 +03:00
domain_sock_setup ( dom_uuid , ( const char * )
attr_path - > children - > content ) ;
2010-01-11 21:26:43 +03:00
}
}
}
xmlFreeDoc ( doc ) ;
return 0 ;
2010-01-11 18:16:57 +03:00
}
2010-01-11 21:26:43 +03:00
static int
2010-01-14 07:34:35 +03:00
registerExisting ( virConnectPtr vp , const char * path , int mode )
2010-01-11 21:26:43 +03:00
{
int * d_ids = NULL ;
int d_count , x ;
virDomainPtr dom ;
virDomainInfo d_info ;
errno = EINVAL ;
if ( ! vp )
2010-01-13 01:36:03 +03:00
return - 1 ;
2010-01-11 21:26:43 +03:00
d_count = virConnectNumOfDomains ( vp ) ;
if ( d_count < = 0 ) {
if ( d_count = = 0 ) {
/* Successful, but no domains running */
errno = 0 ;
2010-01-13 01:36:03 +03:00
return 0 ;
2010-01-11 21:26:43 +03:00
}
goto out_fail ;
}
d_ids = malloc ( sizeof ( int ) * d_count ) ;
if ( ! d_ids )
goto out_fail ;
if ( virConnectListDomains ( vp , d_ids , d_count ) < 0 )
goto out_fail ;
/* Ok, we have the domain IDs - let's get their names and states */
for ( x = 0 ; x < d_count ; x + + ) {
dom = virDomainLookupByID ( vp , d_ids [ x ] ) ;
if ( ! dom ) {
/* XXX doom */
goto out_fail ;
}
if ( virDomainGetInfo ( dom , & d_info ) < 0 ) {
/* XXX no info for the domain?!! */
virDomainFree ( dom ) ;
goto out_fail ;
}
if ( d_info . state ! = VIR_DOMAIN_SHUTOFF & &
d_info . state ! = VIR_DOMAIN_CRASHED )
2010-01-14 07:34:35 +03:00
domainStarted ( dom , path , mode ) ;
2010-01-11 21:26:43 +03:00
virDomainFree ( dom ) ;
}
out_fail :
free ( d_ids ) ;
return 0 ;
}
static int
domainStopped ( virDomainPtr mojaDomain )
{
char dom_uuid [ 42 ] ;
if ( ! mojaDomain )
return - 1 ;
virDomainGetUUIDString ( mojaDomain , dom_uuid ) ;
2010-01-13 10:19:24 +03:00
domain_sock_close ( dom_uuid ) ;
2010-01-11 21:26:43 +03:00
return 0 ;
}
2010-01-11 18:16:57 +03:00
2010-01-13 10:19:24 +03:00
2010-01-14 07:34:35 +03:00
struct event_args {
char * uri ;
char * path ;
int mode ;
2011-10-26 03:29:24 +04:00
int wake_fd ;
2010-01-14 07:34:35 +03:00
} ;
2016-04-25 21:03:05 +03:00
void
connectClose ( virConnectPtr conn ATTRIBUTE_UNUSED ,
int reason ,
void * opaque ATTRIBUTE_UNUSED )
{
switch ( reason ) {
case VIR_CONNECT_CLOSE_REASON_ERROR :
dbg_printf ( 2 , " Connection closed due to I/O error \n " ) ;
break ;
case VIR_CONNECT_CLOSE_REASON_EOF :
dbg_printf ( 2 , " Connection closed due to end of file \n " ) ;
break ;
case VIR_CONNECT_CLOSE_REASON_KEEPALIVE :
dbg_printf ( 2 , " Connection closed due to keepalive timeout \n " ) ;
break ;
case VIR_CONNECT_CLOSE_REASON_CLIENT :
dbg_printf ( 2 , " Connection closed due to client request \n " ) ;
break ;
default :
dbg_printf ( 2 , " Connection closed due to unknown reason \n " ) ;
break ;
} ;
run = 0 ;
}
2010-01-14 07:34:35 +03:00
2011-10-26 03:29:24 +04:00
int
myDomainEventCallback1 ( virConnectPtr conn ,
virDomainPtr dom , int event , int detail , void * opaque )
{
struct event_args * args = ( struct event_args * ) opaque ;
if ( event = = VIR_DOMAIN_EVENT_STARTED | |
event = = VIR_DOMAIN_EVENT_STOPPED ) {
virDomainRef ( dom ) ;
if ( event = = VIR_DOMAIN_EVENT_STARTED ) {
domainStarted ( dom , args - > path , args - > mode ) ;
virDomainFree ( dom ) ;
write ( args - > wake_fd , " x " , 1 ) ;
} else if ( event = = VIR_DOMAIN_EVENT_STOPPED ) {
domainStopped ( dom ) ;
virDomainFree ( dom ) ;
}
}
return 0 ;
}
2010-01-13 10:19:24 +03:00
static void *
event_thread ( void * arg )
2010-01-11 18:16:57 +03:00
{
2010-01-14 07:34:35 +03:00
struct event_args * args = ( struct event_args * ) arg ;
2010-01-11 21:26:43 +03:00
virConnectPtr dconn = NULL ;
int callback1ret = - 1 ;
2010-01-14 07:34:35 +03:00
dbg_printf ( 3 , " Libvirt event listener starting \n " ) ;
if ( args - > uri )
dbg_printf ( 3 , " * URI: %s \n " , args - > uri ) ;
if ( args - > path )
dbg_printf ( 3 , " * Socket path: %s \n " , args - > path ) ;
2013-09-13 23:12:00 +04:00
dbg_printf ( 3 , " * Mode: %s \n " , args - > mode ? " VMChannel " : " Serial " ) ;
2010-01-11 21:26:43 +03:00
2016-04-25 21:03:05 +03:00
if ( virEventRegisterDefaultImpl ( ) < 0 ) {
dbg_printf ( 1 , " Failed to register default event impl \n " ) ;
goto out ;
}
2010-01-11 21:26:43 +03:00
2010-01-14 07:34:35 +03:00
dconn = virConnectOpen ( args - > uri ) ;
2010-01-11 21:26:43 +03:00
if ( ! dconn ) {
2010-01-13 18:04:31 +03:00
dbg_printf ( 1 , " Error connecting to libvirt \n " ) ;
2010-01-14 07:34:35 +03:00
goto out ;
2010-01-11 21:26:43 +03:00
}
2016-04-25 21:03:05 +03:00
virConnectRegisterCloseCallback ( dconn , connectClose , NULL , NULL ) ;
2010-01-11 21:26:43 +03:00
DEBUG0 ( " Registering domain event cbs " ) ;
2010-01-14 07:34:35 +03:00
registerExisting ( dconn , args - > path , args - > mode ) ;
2010-01-11 21:26:43 +03:00
callback1ret =
2013-09-13 23:12:00 +04:00
virConnectDomainEventRegister ( dconn , myDomainEventCallback1 , arg , NULL ) ;
2010-01-11 21:26:43 +03:00
2016-04-25 21:03:05 +03:00
if ( callback1ret ! = - 1 ) {
if ( virConnectSetKeepAlive ( dconn , 5 , 5 ) < 0 ) {
dbg_printf ( 1 , " Failed to start keepalive protocol \n " ) ;
run = 0 ;
}
2010-01-11 21:26:43 +03:00
while ( run ) {
2016-04-25 21:03:05 +03:00
if ( virEventRunDefaultImpl ( ) < 0 ) {
dbg_printf ( 1 , " RunDefaultImpl Failed \n " ) ;
2010-01-11 21:26:43 +03:00
}
}
DEBUG0 ( " Deregistering event handlers " ) ;
virConnectDomainEventDeregister ( dconn , myDomainEventCallback1 ) ;
}
DEBUG0 ( " Closing connection " ) ;
if ( dconn & & virConnectClose ( dconn ) < 0 ) {
2010-01-13 18:04:31 +03:00
dbg_printf ( 1 , " error closing libvirt connection \n " ) ;
2010-01-11 21:26:43 +03:00
}
2010-01-14 07:34:35 +03:00
out :
free ( args - > uri ) ;
free ( args - > path ) ;
free ( args ) ;
2010-01-13 10:19:24 +03:00
return NULL ;
}
int
2011-10-26 03:29:24 +04:00
start_event_listener ( const char * uri , const char * path , int mode , int * wake_fd )
2010-01-13 10:19:24 +03:00
{
2010-01-14 07:34:35 +03:00
struct event_args * args = NULL ;
2011-10-26 03:29:24 +04:00
int wake_pipe [ 2 ] ;
2010-01-13 10:19:24 +03:00
virInitialize ( ) ;
2010-01-14 07:34:35 +03:00
args = malloc ( sizeof ( * args ) ) ;
if ( ! args )
return - 1 ;
memset ( args , 0 , sizeof ( * args ) ) ;
2010-01-13 10:19:24 +03:00
2011-10-26 03:29:24 +04:00
if ( pipe2 ( wake_pipe , O_CLOEXEC ) < 0 ) {
goto out_fail ;
}
2010-01-13 10:19:24 +03:00
if ( uri ) {
2010-01-14 07:34:35 +03:00
args - > uri = strdup ( uri ) ;
if ( args - > uri = = NULL )
goto out_fail ;
2010-01-13 10:19:24 +03:00
}
2010-01-14 07:34:35 +03:00
if ( path ) {
args - > path = strdup ( path ) ;
if ( args - > path = = NULL )
goto out_fail ;
}
args - > mode = mode ;
2011-10-26 03:29:24 +04:00
//args->p_tid = pthread_self();
* wake_fd = wake_pipe [ 0 ] ;
args - > wake_fd = wake_pipe [ 1 ] ;
2010-01-13 10:19:24 +03:00
run = 1 ;
2010-01-14 07:34:35 +03:00
return pthread_create ( & event_tid , NULL , event_thread , args ) ;
out_fail :
free ( args - > uri ) ;
free ( args - > path ) ;
free ( args ) ;
return - 1 ;
2010-01-13 10:19:24 +03:00
}
int
stop_event_listener ( void )
{
run = 0 ;
2010-01-15 02:46:21 +03:00
//pthread_cancel(event_tid);
2010-01-13 10:19:24 +03:00
pthread_join ( event_tid , NULL ) ;
event_tid = 0 ;
2010-01-11 21:26:43 +03:00
return 0 ;
2010-01-11 18:16:57 +03:00
}
2010-01-13 10:19:24 +03:00