libvirt/examples/object-events/event-test.c

826 lines
31 KiB
C
Raw Normal View History

#include <config.h>
2008-10-24 13:10:30 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <inttypes.h>
2008-10-24 13:10:30 +00:00
#include <libvirt/libvirt.h>
#include <libvirt/virterror.h>
#define STREQ(a, b) (strcmp(a, b) == 0)
#ifndef ATTRIBUTE_UNUSED
# define ATTRIBUTE_UNUSED __attribute__((__unused__))
#endif
int run = 1;
/* Callback functions */
static void connectClose(virConnectPtr conn ATTRIBUTE_UNUSED,
int reason,
void *opaque ATTRIBUTE_UNUSED)
{
switch (reason) {
case VIR_CONNECT_CLOSE_REASON_ERROR:
fprintf(stderr, "Connection closed due to I/O error\n");
break;
case VIR_CONNECT_CLOSE_REASON_EOF:
fprintf(stderr, "Connection closed due to end of file\n");
break;
case VIR_CONNECT_CLOSE_REASON_KEEPALIVE:
fprintf(stderr, "Connection closed due to keepalive timeout\n");
break;
case VIR_CONNECT_CLOSE_REASON_CLIENT:
fprintf(stderr, "Connection closed due to client request\n");
break;
default:
fprintf(stderr, "Connection closed due to unknown reason\n");
break;
};
run = 0;
}
const char *eventToString(int event) {
2008-11-17 16:43:00 +00:00
const char *ret = "";
switch ((virDomainEventType) event) {
2008-11-17 16:43:00 +00:00
case VIR_DOMAIN_EVENT_DEFINED:
ret = "Defined";
break;
2008-11-17 16:43:00 +00:00
case VIR_DOMAIN_EVENT_UNDEFINED:
ret = "Undefined";
break;
case VIR_DOMAIN_EVENT_STARTED:
ret = "Started";
break;
case VIR_DOMAIN_EVENT_SUSPENDED:
ret = "Suspended";
break;
case VIR_DOMAIN_EVENT_RESUMED:
ret = "Resumed";
break;
case VIR_DOMAIN_EVENT_STOPPED:
ret = "Stopped";
break;
case VIR_DOMAIN_EVENT_SHUTDOWN:
ret = "Shutdown";
break;
case VIR_DOMAIN_EVENT_PMSUSPENDED:
ret = "PMSuspended";
break;
case VIR_DOMAIN_EVENT_CRASHED:
ret = "Crashed";
break;
2008-11-17 16:43:00 +00:00
}
return ret;
}
static const char *eventDetailToString(int event, int detail) {
const char *ret = "";
switch ((virDomainEventType) event) {
2008-11-17 16:43:00 +00:00
case VIR_DOMAIN_EVENT_DEFINED:
if (detail == VIR_DOMAIN_EVENT_DEFINED_ADDED)
ret = "Added";
else if (detail == VIR_DOMAIN_EVENT_DEFINED_UPDATED)
ret = "Updated";
else if (detail == VIR_DOMAIN_EVENT_DEFINED_RENAMED)
ret = "Renamed";
else if (detail == VIR_DOMAIN_EVENT_DEFINED_FROM_SNAPSHOT)
ret = "Snapshot";
2008-11-17 16:43:00 +00:00
break;
case VIR_DOMAIN_EVENT_UNDEFINED:
if (detail == VIR_DOMAIN_EVENT_UNDEFINED_REMOVED)
ret = "Removed";
else if (detail == VIR_DOMAIN_EVENT_UNDEFINED_RENAMED)
ret = "Renamed";
break;
2008-11-17 16:43:00 +00:00
case VIR_DOMAIN_EVENT_STARTED:
switch ((virDomainEventStartedDetailType) detail) {
2008-11-17 16:43:00 +00:00
case VIR_DOMAIN_EVENT_STARTED_BOOTED:
ret = "Booted";
break;
case VIR_DOMAIN_EVENT_STARTED_MIGRATED:
ret = "Migrated";
break;
case VIR_DOMAIN_EVENT_STARTED_RESTORED:
ret = "Restored";
break;
case VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT:
ret = "Snapshot";
break;
case VIR_DOMAIN_EVENT_STARTED_WAKEUP:
ret = "Event wakeup";
break;
2008-11-17 16:43:00 +00:00
}
break;
case VIR_DOMAIN_EVENT_SUSPENDED:
switch ((virDomainEventSuspendedDetailType) detail) {
case VIR_DOMAIN_EVENT_SUSPENDED_PAUSED:
ret = "Paused";
break;
case VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED:
2008-11-17 16:43:00 +00:00
ret = "Migrated";
break;
case VIR_DOMAIN_EVENT_SUSPENDED_IOERROR:
ret = "I/O Error";
break;
case VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG:
ret = "Watchdog";
break;
case VIR_DOMAIN_EVENT_SUSPENDED_RESTORED:
ret = "Restored";
break;
case VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT:
ret = "Snapshot";
break;
case VIR_DOMAIN_EVENT_SUSPENDED_API_ERROR:
ret = "API error";
break;
case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY:
ret = "Post-copy";
break;
case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY_FAILED:
ret = "Post-copy Error";
break;
}
2008-11-17 16:43:00 +00:00
break;
case VIR_DOMAIN_EVENT_RESUMED:
switch ((virDomainEventResumedDetailType) detail) {
case VIR_DOMAIN_EVENT_RESUMED_UNPAUSED:
2008-11-17 16:43:00 +00:00
ret = "Unpaused";
break;
case VIR_DOMAIN_EVENT_RESUMED_MIGRATED:
2008-11-17 16:43:00 +00:00
ret = "Migrated";
break;
case VIR_DOMAIN_EVENT_RESUMED_FROM_SNAPSHOT:
ret = "Snapshot";
break;
case VIR_DOMAIN_EVENT_RESUMED_POSTCOPY:
ret = "Post-copy";
break;
}
2008-11-17 16:43:00 +00:00
break;
case VIR_DOMAIN_EVENT_STOPPED:
switch ((virDomainEventStoppedDetailType) detail) {
2008-11-17 16:43:00 +00:00
case VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN:
ret = "Shutdown";
break;
case VIR_DOMAIN_EVENT_STOPPED_DESTROYED:
ret = "Destroyed";
break;
case VIR_DOMAIN_EVENT_STOPPED_CRASHED:
ret = "Crashed";
break;
case VIR_DOMAIN_EVENT_STOPPED_MIGRATED:
ret = "Migrated";
break;
case VIR_DOMAIN_EVENT_STOPPED_SAVED:
ret = "Saved";
2008-11-17 16:43:00 +00:00
break;
case VIR_DOMAIN_EVENT_STOPPED_FAILED:
ret = "Failed";
break;
case VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT:
ret = "Snapshot";
break;
2008-11-17 16:43:00 +00:00
}
break;
case VIR_DOMAIN_EVENT_SHUTDOWN:
switch ((virDomainEventShutdownDetailType) detail) {
case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED:
ret = "Finished";
break;
}
break;
case VIR_DOMAIN_EVENT_PMSUSPENDED:
switch ((virDomainEventPMSuspendedDetailType) detail) {
case VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY:
ret = "Memory";
break;
case VIR_DOMAIN_EVENT_PMSUSPENDED_DISK:
ret = "Disk";
break;
}
break;
case VIR_DOMAIN_EVENT_CRASHED:
switch ((virDomainEventCrashedDetailType) detail) {
case VIR_DOMAIN_EVENT_CRASHED_PANICKED:
ret = "Panicked";
break;
}
break;
}
return ret;
}
static const char *
networkEventToString(int event)
{
const char *ret = "";
switch ((virNetworkEventLifecycleType) event) {
case VIR_NETWORK_EVENT_DEFINED:
ret = "Defined";
break;
case VIR_NETWORK_EVENT_UNDEFINED:
ret = "Undefined";
break;
case VIR_NETWORK_EVENT_STARTED:
ret = "Started";
break;
case VIR_NETWORK_EVENT_STOPPED:
ret = "Stopped";
break;
}
return ret;
}
static const char *
guestAgentLifecycleEventStateToString(int event)
{
const char *ret = "";
switch ((virConnectDomainEventAgentLifecycleState) event) {
case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_DISCONNECTED:
ret = "Disconnected";
break;
case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_CONNECTED:
ret = "Connected";
break;
}
return ret;
}
static const char *
guestAgentLifecycleEventReasonToString(int event)
{
const char *ret = "";
switch ((virConnectDomainEventAgentLifecycleReason) event) {
case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_UNKNOWN:
ret = "Unknown";
break;
case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_STARTED:
ret = "Domain started";
break;
case VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL:
ret = "Channel event";
break;
}
return ret;
}
static int myDomainEventCallback1(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int event,
int detail,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) %s %s\n", __func__, virDomainGetName(dom),
2008-11-17 16:43:00 +00:00
virDomainGetID(dom), eventToString(event),
eventDetailToString(event, detail));
return 0;
}
static int myDomainEventCallback2(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int event,
int detail,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) %s %s\n", __func__, virDomainGetName(dom),
2008-11-17 16:43:00 +00:00
virDomainGetID(dom), eventToString(event),
eventDetailToString(event, detail));
return 0;
}
static int myDomainEventRebootCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) rebooted\n", __func__, virDomainGetName(dom),
virDomainGetID(dom));
return 0;
}
static int myDomainEventRTCChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
long long offset,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) rtc change %" PRIdMAX "\n",
__func__, virDomainGetName(dom), virDomainGetID(dom),
(intmax_t)offset);
return 0;
}
static int myDomainEventBalloonChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
unsigned long long actual,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) balloon change %" PRIuMAX "KB\n",
__func__, virDomainGetName(dom), virDomainGetID(dom), (uintmax_t)actual);
return 0;
}
static int myDomainEventWatchdogCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int action,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) watchdog action=%d\n", __func__, virDomainGetName(dom),
virDomainGetID(dom), action);
return 0;
}
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
static int myDomainEventIOErrorCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
const char *srcPath,
const char *devAlias,
int action,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) io error path=%s alias=%s action=%d\n", __func__, virDomainGetName(dom),
virDomainGetID(dom), srcPath, devAlias, action);
return 0;
}
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
static int myDomainEventGraphicsCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int phase,
virDomainEventGraphicsAddressPtr local,
virDomainEventGraphicsAddressPtr remote,
const char *authScheme,
virDomainEventGraphicsSubjectPtr subject,
void *opaque ATTRIBUTE_UNUSED)
{
size_t i;
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
printf("%s EVENT: Domain %s(%d) graphics ", __func__, virDomainGetName(dom),
virDomainGetID(dom));
switch (phase) {
case VIR_DOMAIN_EVENT_GRAPHICS_CONNECT:
printf("connected ");
break;
case VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE:
printf("initialized ");
break;
case VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT:
printf("disconnected ");
break;
}
printf("local: family=%d node=%s service=%s ",
local->family, local->node, local->service);
printf("remote: family=%d node=%s service=%s ",
remote->family, remote->node, remote->service);
printf("auth: %s ", authScheme);
for (i = 0; i < subject->nidentity; i++) {
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
printf(" identity: %s=%s",
subject->identities[i].type,
subject->identities[i].name);
}
printf("\n");
return 0;
}
static int myDomainEventControlErrorCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) control error\n", __func__, virDomainGetName(dom),
virDomainGetID(dom));
return 0;
}
const char *diskChangeReasonStrings[] = {
"startupPolicy", /* 0 */
/* add new reason here */
};
static int myDomainEventDiskChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
const char *oldSrcPath,
const char *newSrcPath,
const char *devAlias,
int reason,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) disk change oldSrcPath: %s newSrcPath: %s devAlias: %s reason: %s\n",
__func__, virDomainGetName(dom), virDomainGetID(dom),
oldSrcPath, newSrcPath, devAlias, diskChangeReasonStrings[reason]);
return 0;
}
const char *trayChangeReasonStrings[] = {
"open",
"close",
};
static int myDomainEventTrayChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
const char *devAlias,
int reason,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) removable disk's tray change devAlias: %s reason: %s\n",
__func__, virDomainGetName(dom), virDomainGetID(dom),
devAlias, trayChangeReasonStrings[reason]);
return 0;
}
static int myDomainEventPMWakeupCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int reason ATTRIBUTE_UNUSED,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) system pmwakeup\n",
__func__, virDomainGetName(dom), virDomainGetID(dom));
return 0;
}
static int myDomainEventPMSuspendCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int reason ATTRIBUTE_UNUSED,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) system pmsuspend\n",
__func__, virDomainGetName(dom), virDomainGetID(dom));
return 0;
}
static int myDomainEventPMSuspendDiskCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int reason ATTRIBUTE_UNUSED,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) system pmsuspend-disk\n",
__func__, virDomainGetName(dom), virDomainGetID(dom));
return 0;
}
static int
myDomainEventDeviceRemovedCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
const char *devAlias,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) device removed: %s\n",
__func__, virDomainGetName(dom), virDomainGetID(dom), devAlias);
return 0;
}
static int myNetworkEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virNetworkPtr dom,
int event,
int detail,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Network %s %s %d\n", __func__, virNetworkGetName(dom),
networkEventToString(event), detail);
return 0;
}
static int
myDomainEventTunableCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
virTypedParameterPtr params,
int nparams,
void *opaque ATTRIBUTE_UNUSED)
{
size_t i;
printf("%s EVENT: Domain %s(%d) tunable updated:\n",
__func__, virDomainGetName(dom), virDomainGetID(dom));
for (i = 0; i < nparams; i++) {
switch (params[i].type) {
case VIR_TYPED_PARAM_INT:
printf("\t%s: %d\n", params[i].field, params[i].value.i);
break;
case VIR_TYPED_PARAM_UINT:
printf("\t%s: %u\n", params[i].field, params[i].value.ui);
break;
case VIR_TYPED_PARAM_LLONG:
printf("\t%s: %" PRId64 "\n", params[i].field,
(int64_t) params[i].value.l);
break;
case VIR_TYPED_PARAM_ULLONG:
printf("\t%s: %" PRIu64 "\n", params[i].field,
(uint64_t) params[i].value.ul);
break;
case VIR_TYPED_PARAM_DOUBLE:
printf("\t%s: %g\n", params[i].field, params[i].value.d);
break;
case VIR_TYPED_PARAM_BOOLEAN:
printf("\t%s: %d\n", params[i].field, params[i].value.b);
break;
case VIR_TYPED_PARAM_STRING:
printf("\t%s: %s\n", params[i].field, params[i].value.s);
break;
default:
printf("\t%s: unknown type\n", params[i].field);
}
}
return 0;
}
static int
myDomainEventAgentLifecycleCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int state,
int reason,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) guest agent state changed: %s reason: %s\n",
__func__, virDomainGetName(dom), virDomainGetID(dom),
guestAgentLifecycleEventStateToString(state),
guestAgentLifecycleEventReasonToString(reason));
return 0;
}
static int
myDomainEventDeviceAddedCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
const char *devAlias,
void *opaque ATTRIBUTE_UNUSED)
{
printf("%s EVENT: Domain %s(%d) device added: %s\n",
__func__, virDomainGetName(dom), virDomainGetID(dom), devAlias);
return 0;
}
static void myFreeFunc(void *opaque)
{
char *str = opaque;
printf("%s: Freeing [%s]\n", __func__, str);
free(str);
}
/* main test functions */
void usage(const char *pname)
{
printf("%s uri\n", pname);
}
static void stop(int sig)
{
printf("Exiting on signal %d\n", sig);
run = 0;
}
int main(int argc, char **argv)
{
int ret = EXIT_FAILURE;
virConnectPtr dconn = NULL;
int callback1ret = -1;
int callback2ret = -1;
int callback3ret = -1;
int callback4ret = -1;
int callback5ret = -1;
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
int callback6ret = -1;
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
int callback7ret = -1;
int callback8ret = -1;
int callback9ret = -1;
int callback10ret = -1;
int callback11ret = -1;
int callback12ret = -1;
int callback13ret = -1;
int callback14ret = -1;
int callback15ret = -1;
int callback16ret = -1;
int callback17ret = -1;
int callback18ret = -1;
int callback19ret = -1;
struct sigaction action_stop;
memset(&action_stop, 0, sizeof(action_stop));
action_stop.sa_handler = stop;
if (argc > 1 && STREQ(argv[1], "--help")) {
usage(argv[0]);
goto cleanup;
}
if (virInitialize() < 0) {
fprintf(stderr, "Failed to initialize libvirt");
goto cleanup;
}
if (virEventRegisterDefaultImpl() < 0) {
virErrorPtr err = virGetLastError();
fprintf(stderr, "Failed to register event implementation: %s\n",
err && err->message ? err->message: "Unknown error");
goto cleanup;
}
dconn = virConnectOpenAuth(argc > 1 ? argv[1] : NULL,
virConnectAuthPtrDefault,
VIR_CONNECT_RO);
if (!dconn) {
printf("error opening\n");
goto cleanup;
}
if (virConnectRegisterCloseCallback(dconn,
connectClose, NULL, NULL) < 0) {
fprintf(stderr, "Unable to register close callback\n");
goto cleanup;
}
sigaction(SIGTERM, &action_stop, NULL);
sigaction(SIGINT, &action_stop, NULL);
printf("Registering event callbacks\n");
/* Add 2 callbacks to prove this works with more than just one */
callback1ret = virConnectDomainEventRegister(dconn, myDomainEventCallback1,
strdup("callback 1"), myFreeFunc);
callback2ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_LIFECYCLE,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventCallback2),
strdup("callback 2"), myFreeFunc);
callback3ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_REBOOT,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventRebootCallback),
strdup("callback reboot"), myFreeFunc);
callback4ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_RTC_CHANGE,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventRTCChangeCallback),
strdup("callback rtcchange"), myFreeFunc);
callback5ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_WATCHDOG,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventWatchdogCallback),
strdup("callback watchdog"), myFreeFunc);
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
callback6ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_IO_ERROR,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventIOErrorCallback),
strdup("callback io error"), myFreeFunc);
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
callback7ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_GRAPHICS,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventGraphicsCallback),
strdup("callback graphics"), myFreeFunc);
callback8ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_CONTROL_ERROR,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventControlErrorCallback),
strdup("callback control error"), myFreeFunc);
callback9ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_DISK_CHANGE,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventDiskChangeCallback),
strdup("disk change"), myFreeFunc);
callback10ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_TRAY_CHANGE,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventTrayChangeCallback),
strdup("tray change"), myFreeFunc);
callback11ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_PMWAKEUP,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventPMWakeupCallback),
strdup("pmwakeup"), myFreeFunc);
callback12ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_PMSUSPEND,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventPMSuspendCallback),
strdup("pmsuspend"), myFreeFunc);
callback13ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventBalloonChangeCallback),
strdup("callback balloonchange"), myFreeFunc);
callback14ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_PMSUSPEND_DISK,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventPMSuspendDiskCallback),
strdup("pmsuspend-disk"), myFreeFunc);
callback15ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventDeviceRemovedCallback),
strdup("device removed"), myFreeFunc);
callback16ret = virConnectNetworkEventRegisterAny(dconn,
NULL,
VIR_NETWORK_EVENT_ID_LIFECYCLE,
VIR_NETWORK_EVENT_CALLBACK(myNetworkEventCallback),
strdup("net callback"), myFreeFunc);
callback17ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_TUNABLE,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventTunableCallback),
strdup("tunable"), myFreeFunc);
callback18ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventAgentLifecycleCallback),
strdup("guest agent lifecycle"), myFreeFunc);
callback19ret = virConnectDomainEventRegisterAny(dconn,
NULL,
VIR_DOMAIN_EVENT_ID_DEVICE_ADDED,
VIR_DOMAIN_EVENT_CALLBACK(myDomainEventDeviceAddedCallback),
strdup("device added"), myFreeFunc);
if ((callback1ret == -1) ||
(callback2ret == -1) ||
(callback3ret == -1) ||
(callback4ret == -1) ||
(callback5ret == -1) ||
(callback6ret == -1) ||
(callback7ret == -1) ||
(callback9ret == -1) ||
(callback10ret == -1) ||
(callback11ret == -1) ||
(callback12ret == -1) ||
(callback13ret == -1) ||
(callback14ret == -1) ||
(callback15ret == -1) ||
(callback16ret == -1) ||
(callback17ret == -1) ||
(callback18ret == -1) ||
(callback19ret == -1))
goto cleanup;
if (virConnectSetKeepAlive(dconn, 5, 3) < 0) {
virErrorPtr err = virGetLastError();
fprintf(stderr, "Failed to start keepalive protocol: %s\n",
err && err->message ? err->message : "Unknown error");
run = 0;
}
while (run) {
if (virEventRunDefaultImpl() < 0) {
virErrorPtr err = virGetLastError();
fprintf(stderr, "Failed to run event loop: %s\n",
err && err->message ? err->message : "Unknown error");
}
}
printf("Deregistering event callbacks\n");
virConnectDomainEventDeregister(dconn, myDomainEventCallback1);
virConnectDomainEventDeregisterAny(dconn, callback2ret);
virConnectDomainEventDeregisterAny(dconn, callback3ret);
virConnectDomainEventDeregisterAny(dconn, callback4ret);
virConnectDomainEventDeregisterAny(dconn, callback5ret);
virConnectDomainEventDeregisterAny(dconn, callback6ret);
virConnectDomainEventDeregisterAny(dconn, callback7ret);
virConnectDomainEventDeregisterAny(dconn, callback9ret);
virConnectDomainEventDeregisterAny(dconn, callback10ret);
virConnectDomainEventDeregisterAny(dconn, callback11ret);
virConnectDomainEventDeregisterAny(dconn, callback12ret);
virConnectDomainEventDeregisterAny(dconn, callback13ret);
virConnectDomainEventDeregisterAny(dconn, callback14ret);
virConnectDomainEventDeregisterAny(dconn, callback15ret);
virConnectNetworkEventDeregisterAny(dconn, callback16ret);
virConnectDomainEventDeregisterAny(dconn, callback17ret);
virConnectDomainEventDeregisterAny(dconn, callback18ret);
virConnectDomainEventDeregisterAny(dconn, callback19ret);
if (callback8ret != -1)
virConnectDomainEventDeregisterAny(dconn, callback8ret);
virConnectUnregisterCloseCallback(dconn, connectClose);
ret = EXIT_SUCCESS;
cleanup:
if (dconn) {
printf("Closing connection: ");
if (virConnectClose(dconn) < 0)
printf("failed\n");
printf("done\n");
}
return ret;
}