libvirt/daemon/remote.c

4710 lines
146 KiB
C
Raw Normal View History

/*
* remote.c: handlers for RPC method calls
*
* Copyright (C) 2007-2012 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Richard W.M. Jones <rjones@redhat.com>
*/
#include <config.h>
#include "virterror_internal.h"
#if HAVE_POLKIT0
# include <polkit/polkit.h>
# include <polkit-dbus/polkit-dbus.h>
2007-12-05 18:21:27 +00:00
#endif
#include "remote.h"
#include "libvirtd.h"
#include "libvirt_internal.h"
#include "datatypes.h"
#include "memory.h"
#include "logging.h"
#include "util.h"
#include "stream.h"
#include "uuid.h"
#include "libvirt/libvirt-qemu.h"
#include "command.h"
#include "intprops.h"
#include "virnetserverservice.h"
#include "virnetserver.h"
#include "virfile.h"
#include "virtypedparam.h"
#include "virdbus.h"
#include "remote_protocol.h"
#include "qemu_protocol.h"
#define VIR_FROM_THIS VIR_FROM_RPC
#if SIZEOF_LONG < 8
# define HYPER_TO_TYPE(_type, _to, _from) \
do { \
if ((_from) != (_type)(_from)) { \
virReportError(VIR_ERR_OVERFLOW, \
_("conversion from hyper to %s overflowed"), \
#_type); \
goto cleanup; \
} \
(_to) = (_from); \
} while (0)
# define HYPER_TO_LONG(_to, _from) HYPER_TO_TYPE(long, _to, _from)
# define HYPER_TO_ULONG(_to, _from) HYPER_TO_TYPE(unsigned long, _to, _from)
#else
# define HYPER_TO_LONG(_to, _from) (_to) = (_from)
# define HYPER_TO_ULONG(_to, _from) (_to) = (_from)
#endif
static virDomainPtr get_nonnull_domain(virConnectPtr conn, remote_nonnull_domain domain);
static virNetworkPtr get_nonnull_network(virConnectPtr conn, remote_nonnull_network network);
static virInterfacePtr get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface);
static virStoragePoolPtr get_nonnull_storage_pool(virConnectPtr conn, remote_nonnull_storage_pool pool);
static virStorageVolPtr get_nonnull_storage_vol(virConnectPtr conn, remote_nonnull_storage_vol vol);
static virSecretPtr get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret secret);
static virNWFilterPtr get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nwfilter nwfilter);
static virDomainSnapshotPtr get_nonnull_domain_snapshot(virDomainPtr dom, remote_nonnull_domain_snapshot snapshot);
static void make_nonnull_domain(remote_nonnull_domain *dom_dst, virDomainPtr dom_src);
static void make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr net_src);
static void make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src);
static void make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src);
static void make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src);
static void make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src);
static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src);
static void make_nonnull_nwfilter(remote_nonnull_nwfilter *net_dst, virNWFilterPtr nwfilter_src);
static void make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src);
static virTypedParameterPtr
remoteDeserializeTypedParameters(remote_typed_param *args_params_val,
u_int args_params_len,
int limit,
int *nparams);
static int
remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors,
int nerrors,
remote_domain_disk_error **ret_errors_val,
u_int *ret_errors_len);
#include "remote_dispatch.h"
#include "qemu_dispatch.h"
/* Prototypes */
static void
remoteDispatchDomainEventSend(virNetServerClientPtr client,
virNetServerProgramPtr program,
int procnr,
xdrproc_t proc,
void *data);
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
static int remoteRelayDomainEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int event,
int detail,
void *opaque)
{
virNetServerClientPtr client = opaque;
remote_domain_event_lifecycle_msg data;
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
if (!client)
return -1;
VIR_DEBUG("Relaying domain lifecycle event %d %d", event, detail);
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
/* build return data */
memset(&data, 0, sizeof(data));
make_nonnull_domain(&data.dom, dom);
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
data.event = event;
data.detail = detail;
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE,
(xdrproc_t)xdr_remote_domain_event_lifecycle_msg, &data);
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
return 0;
}
static int remoteRelayDomainEventReboot(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
void *opaque)
{
virNetServerClientPtr client = opaque;
remote_domain_event_reboot_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain reboot event %s %d", dom->name, dom->id);
/* build return data */
memset(&data, 0, sizeof(data));
make_nonnull_domain(&data.dom, dom);
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_REBOOT,
(xdrproc_t)xdr_remote_domain_event_reboot_msg, &data);
return 0;
}
static int remoteRelayDomainEventRTCChange(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
long long offset,
void *opaque)
{
virNetServerClientPtr client = opaque;
remote_domain_event_rtc_change_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain rtc change event %s %d %lld", dom->name, dom->id, offset);
/* build return data */
memset(&data, 0, sizeof(data));
make_nonnull_domain(&data.dom, dom);
data.offset = offset;
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE,
(xdrproc_t)xdr_remote_domain_event_rtc_change_msg, &data);
return 0;
}
static int remoteRelayDomainEventWatchdog(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int action,
void *opaque)
{
virNetServerClientPtr client = opaque;
remote_domain_event_watchdog_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain watchdog event %s %d %d", dom->name, dom->id, action);
/* build return data */
memset(&data, 0, sizeof(data));
make_nonnull_domain(&data.dom, dom);
data.action = action;
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_WATCHDOG,
(xdrproc_t)xdr_remote_domain_event_watchdog_msg, &data);
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 remoteRelayDomainEventIOError(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
const char *srcPath,
const char *devAlias,
int action,
void *opaque)
{
virNetServerClientPtr client = opaque;
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
remote_domain_event_io_error_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain io error %s %d %s %s %d", dom->name, dom->id, srcPath, devAlias, action);
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
/* build return data */
memset(&data, 0, sizeof(data));
data.srcPath = strdup(srcPath);
if (data.srcPath == NULL)
goto mem_error;
data.devAlias = strdup(devAlias);
if (data.devAlias == NULL)
goto mem_error;
make_nonnull_domain(&data.dom, dom);
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
data.action = action;
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_IO_ERROR,
(xdrproc_t)xdr_remote_domain_event_io_error_msg, &data);
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
return 0;
mem_error:
virReportOOMError();
VIR_FREE(data.srcPath);
VIR_FREE(data.devAlias);
return -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
}
static int remoteRelayDomainEventIOErrorReason(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
const char *srcPath,
const char *devAlias,
int action,
const char *reason,
void *opaque)
{
virNetServerClientPtr client = opaque;
remote_domain_event_io_error_reason_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain io error %s %d %s %s %d %s",
dom->name, dom->id, srcPath, devAlias, action, reason);
/* build return data */
memset(&data, 0, sizeof(data));
data.srcPath = strdup(srcPath);
if (data.srcPath == NULL)
goto mem_error;
data.devAlias = strdup(devAlias);
if (data.devAlias == NULL)
goto mem_error;
data.action = action;
data.reason = strdup(reason);
if (data.reason == NULL)
goto mem_error;
make_nonnull_domain(&data.dom, dom);
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_IO_ERROR_REASON,
(xdrproc_t)xdr_remote_domain_event_io_error_reason_msg, &data);
return 0;
mem_error:
virReportOOMError();
VIR_FREE(data.srcPath);
VIR_FREE(data.devAlias);
VIR_FREE(data.reason);
return -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
static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int phase,
virDomainEventGraphicsAddressPtr local,
virDomainEventGraphicsAddressPtr remote,
const char *authScheme,
virDomainEventGraphicsSubjectPtr subject,
void *opaque)
{
virNetServerClientPtr client = opaque;
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
remote_domain_event_graphics_msg data;
int i;
if (!client)
return -1;
VIR_DEBUG("Relaying domain graphics event %s %d %d - %d %s %s - %d %s %s - %s", dom->name, dom->id, phase,
local->family, local->service, local->node,
remote->family, remote->service, remote->node,
authScheme);
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
VIR_DEBUG("Subject %d", subject->nidentity);
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
for (i = 0 ; i < subject->nidentity ; i++) {
VIR_DEBUG(" %s=%s", subject->identities[i].type, subject->identities[i].name);
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
}
/* build return data */
memset(&data, 0, sizeof(data));
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
data.phase = phase;
data.local.family = local->family;
data.remote.family = remote->family;
data.authScheme = strdup(authScheme);
if (data.authScheme == NULL)
goto mem_error;
data.local.node = strdup(local->node);
if (data.local.node == NULL)
goto mem_error;
data.local.service = strdup(local->service);
if (data.local.service == NULL)
goto mem_error;
data.remote.node = strdup(remote->node);
if (data.remote.node == NULL)
goto mem_error;
data.remote.service = strdup(remote->service);
if (data.remote.service == NULL)
goto mem_error;
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
data.subject.subject_len = subject->nidentity;
if (VIR_ALLOC_N(data.subject.subject_val, data.subject.subject_len) < 0)
goto mem_error;
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
for (i = 0 ; i < data.subject.subject_len ; i++) {
data.subject.subject_val[i].type = strdup(subject->identities[i].type);
if (data.subject.subject_val[i].type == NULL)
goto mem_error;
data.subject.subject_val[i].name = strdup(subject->identities[i].name);
if (data.subject.subject_val[i].name == NULL)
goto mem_error;
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
}
make_nonnull_domain(&data.dom, dom);
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
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_GRAPHICS,
(xdrproc_t)xdr_remote_domain_event_graphics_msg, &data);
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
return 0;
mem_error:
virReportOOMError();
VIR_FREE(data.authScheme);
VIR_FREE(data.local.node);
VIR_FREE(data.local.service);
VIR_FREE(data.remote.node);
VIR_FREE(data.remote.service);
if (data.subject.subject_val != NULL) {
for (i = 0 ; i < data.subject.subject_len ; i++) {
VIR_FREE(data.subject.subject_val[i].type);
VIR_FREE(data.subject.subject_val[i].name);
}
VIR_FREE(data.subject.subject_val);
}
return -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
}
static int remoteRelayDomainEventBlockJob(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
const char *path,
int type,
int status,
void *opaque)
{
virNetServerClientPtr client = opaque;
remote_domain_event_block_job_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain block job event %s %d %s %i, %i",
dom->name, dom->id, path, type, status);
/* build return data */
memset(&data, 0, sizeof(data));
data.path = strdup(path);
if (data.path == NULL)
goto mem_error;
data.type = type;
data.status = status;
make_nonnull_domain(&data.dom, dom);
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB,
(xdrproc_t)xdr_remote_domain_event_block_job_msg, &data);
return 0;
mem_error:
virReportOOMError();
VIR_FREE(data.path);
return -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
static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
void *opaque)
{
virNetServerClientPtr client = opaque;
remote_domain_event_control_error_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain control error %s %d", dom->name, dom->id);
/* build return data */
memset(&data, 0, sizeof(data));
make_nonnull_domain(&data.dom, dom);
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR,
(xdrproc_t)xdr_remote_domain_event_control_error_msg, &data);
return 0;
}
static int remoteRelayDomainEventDiskChange(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
const char *oldSrcPath,
const char *newSrcPath,
const char *devAlias,
int reason,
void *opaque)
{
virNetServerClientPtr client = opaque;
remote_domain_event_disk_change_msg data;
char **oldSrcPath_p = NULL, **newSrcPath_p = NULL;
if (!client)
return -1;
VIR_DEBUG("Relaying domain %s %d disk change %s %s %s %d",
dom->name, dom->id, oldSrcPath, newSrcPath, devAlias, reason);
/* build return data */
memset(&data, 0, sizeof(data));
if (oldSrcPath &&
((VIR_ALLOC(oldSrcPath_p) < 0) ||
!(*oldSrcPath_p = strdup(oldSrcPath))))
goto mem_error;
if (newSrcPath &&
((VIR_ALLOC(newSrcPath_p) < 0) ||
!(*newSrcPath_p = strdup(newSrcPath))))
goto mem_error;
data.oldSrcPath = oldSrcPath_p;
data.newSrcPath = newSrcPath_p;
if (!(data.devAlias = strdup(devAlias)))
goto mem_error;
data.reason = reason;
make_nonnull_domain(&data.dom, dom);
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_DISK_CHANGE,
(xdrproc_t)xdr_remote_domain_event_disk_change_msg, &data);
return 0;
mem_error:
VIR_FREE(oldSrcPath_p);
VIR_FREE(newSrcPath_p);
virReportOOMError();
return -1;
}
static int remoteRelayDomainEventTrayChange(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
const char *devAlias,
int reason,
void *opaque) {
virNetServerClientPtr client = opaque;
remote_domain_event_tray_change_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain %s %d tray change devAlias: %s reason: %d",
dom->name, dom->id, devAlias, reason);
/* build return data */
memset(&data, 0, sizeof(data));
if (!(data.devAlias = strdup(devAlias))) {
virReportOOMError();
return -1;
}
data.reason = reason;
make_nonnull_domain(&data.dom, dom);
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_TRAY_CHANGE,
(xdrproc_t)xdr_remote_domain_event_tray_change_msg, &data);
return 0;
}
static int remoteRelayDomainEventPMWakeup(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int reason ATTRIBUTE_UNUSED,
void *opaque) {
virNetServerClientPtr client = opaque;
remote_domain_event_pmwakeup_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain %s %d system pmwakeup", dom->name, dom->id);
/* build return data */
memset(&data, 0, sizeof(data));
make_nonnull_domain(&data.dom, dom);
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_PMWAKEUP,
(xdrproc_t)xdr_remote_domain_event_pmwakeup_msg, &data);
return 0;
}
static int remoteRelayDomainEventPMSuspend(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int reason ATTRIBUTE_UNUSED,
void *opaque) {
virNetServerClientPtr client = opaque;
remote_domain_event_pmsuspend_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain %s %d system pmsuspend", dom->name, dom->id);
/* build return data */
memset(&data, 0, sizeof(data));
make_nonnull_domain(&data.dom, dom);
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_PMSUSPEND,
(xdrproc_t)xdr_remote_domain_event_pmsuspend_msg, &data);
return 0;
}
static int
remoteRelayDomainEventBalloonChange(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
unsigned long long actual,
void *opaque)
{
virNetServerClientPtr client = opaque;
remote_domain_event_balloon_change_msg data;
if (!client)
return -1;
VIR_DEBUG("Relaying domain balloon change event %s %d %lld", dom->name, dom->id, actual);
/* build return data */
memset(&data, 0, sizeof(data));
make_nonnull_domain(&data.dom, dom);
data.actual = actual;
remoteDispatchDomainEventSend(client, remoteProgram,
REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE,
(xdrproc_t)xdr_remote_domain_event_balloon_change_msg, &data);
return 0;
}
static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventRTCChange),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventWatchdog),
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
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOError),
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
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDiskChange),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventTrayChange),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMWakeup),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMSuspend),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBalloonChange),
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
};
verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
/*
* You must hold lock for at least the client
* We don't free stuff here, merely disconnect the client's
* network socket & resources.
* We keep the libvirt connection open until any async
* jobs have finished, then clean it up elsewhere
*/
void remoteClientFreeFunc(void *data)
{
struct daemonClientPrivate *priv = data;
/* Deregister event delivery callback */
if (priv->conn) {
int i;
for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++) {
if (priv->domainEventCallbackID[i] != -1) {
VIR_DEBUG("Deregistering to relay remote events %d", i);
virConnectDomainEventDeregisterAny(priv->conn,
priv->domainEventCallbackID[i]);
}
priv->domainEventCallbackID[i] = -1;
}
virConnectClose(priv->conn);
}
VIR_FREE(priv);
}
static void remoteClientCloseFunc(virNetServerClientPtr client)
{
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
daemonRemoveAllClientStreams(priv->streams);
}
void *remoteClientInitHook(virNetServerClientPtr client,
void *opaque ATTRIBUTE_UNUSED)
{
struct daemonClientPrivate *priv;
int i;
if (VIR_ALLOC(priv) < 0) {
virReportOOMError();
return NULL;
}
if (virMutexInit(&priv->lock) < 0) {
VIR_FREE(priv);
virReportOOMError();
return NULL;
}
for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++)
priv->domainEventCallbackID[i] = -1;
virNetServerClientSetCloseHook(client, remoteClientCloseFunc);
return priv;
}
/*----- Functions. -----*/
static int
remoteDispatchOpen(virNetServerPtr server,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
struct remote_open_args *args)
{
const char *name;
unsigned int flags;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
int rv = -1;
VIR_DEBUG("priv=%p conn=%p", priv, priv->conn);
virMutexLock(&priv->lock);
/* Already opened? */
if (priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection already open"));
goto cleanup;
}
if (virNetServerKeepAliveRequired(server) && !priv->keepalive_supported) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("keepalive support is required to connect"));
goto cleanup;
}
name = args->name ? *args->name : NULL;
/* If this connection arrived on a readonly socket, force
* the connection to be readonly.
*/
flags = args->flags;
if (virNetServerClientGetReadonly(client))
flags |= VIR_CONNECT_RO;
priv->conn =
flags & VIR_CONNECT_RO
? virConnectOpenReadOnly(name)
: virConnectOpen(name);
if (priv->conn == NULL)
goto cleanup;
rv = 0;
2008-12-04 22:12:53 +00:00
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virMutexUnlock(&priv->lock);
return rv;
}
static int
remoteDispatchClose(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED)
{
virNetServerClientDelayedClose(client);
2008-12-04 22:12:53 +00:00
return 0;
}
static int
remoteDispatchDomainGetSchedulerType(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_scheduler_type_args *args,
remote_domain_get_scheduler_type_ret *ret)
{
virDomainPtr dom = NULL;
char *type;
int nparams;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (!(type = virDomainGetSchedulerType(dom, &nparams)))
goto cleanup;
ret->type = type;
ret->nparams = nparams;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
return rv;
}
/* Helper to serialize typed parameters. This also filters out any string
* parameters that must not be returned to older clients. */
static int
remoteSerializeTypedParameters(virTypedParameterPtr params,
int nparams,
remote_typed_param **ret_params_val,
u_int *ret_params_len,
unsigned int flags)
{
int i;
int j;
int rv = -1;
remote_typed_param *val;
*ret_params_len = nparams;
if (VIR_ALLOC_N(val, nparams) < 0) {
virReportOOMError();
goto cleanup;
}
for (i = 0, j = 0; i < nparams; ++i) {
/* virDomainGetCPUStats can return a sparse array; also, we
* can't pass back strings to older clients. */
if (!params[i].type ||
(!(flags & VIR_TYPED_PARAM_STRING_OKAY) &&
params[i].type == VIR_TYPED_PARAM_STRING)) {
--*ret_params_len;
continue;
}
/* remoteDispatchClientRequest will free this: */
val[j].field = strdup(params[i].field);
if (val[j].field == NULL) {
virReportOOMError();
goto cleanup;
}
val[j].value.type = params[i].type;
switch (params[i].type) {
case VIR_TYPED_PARAM_INT:
val[j].value.remote_typed_param_value_u.i = params[i].value.i;
break;
case VIR_TYPED_PARAM_UINT:
val[j].value.remote_typed_param_value_u.ui = params[i].value.ui;
break;
case VIR_TYPED_PARAM_LLONG:
val[j].value.remote_typed_param_value_u.l = params[i].value.l;
break;
case VIR_TYPED_PARAM_ULLONG:
val[j].value.remote_typed_param_value_u.ul = params[i].value.ul;
break;
case VIR_TYPED_PARAM_DOUBLE:
val[j].value.remote_typed_param_value_u.d = params[i].value.d;
break;
case VIR_TYPED_PARAM_BOOLEAN:
val[j].value.remote_typed_param_value_u.b = params[i].value.b;
break;
case VIR_TYPED_PARAM_STRING:
val[j].value.remote_typed_param_value_u.s =
strdup(params[i].value.s);
if (val[j].value.remote_typed_param_value_u.s == NULL) {
virReportOOMError();
goto cleanup;
}
break;
default:
virReportError(VIR_ERR_RPC, _("unknown parameter type: %d"),
params[i].type);
goto cleanup;
}
j++;
}
*ret_params_val = val;
val = NULL;
rv = 0;
cleanup:
if (val) {
for (i = 0; i < nparams; i++) {
VIR_FREE(val[i].field);
if (val[i].value.type == VIR_TYPED_PARAM_STRING)
VIR_FREE(val[i].value.remote_typed_param_value_u.s);
}
VIR_FREE(val);
}
return rv;
}
/* Helper to deserialize typed parameters. */
static virTypedParameterPtr
remoteDeserializeTypedParameters(remote_typed_param *args_params_val,
u_int args_params_len,
int limit,
int *nparams)
{
int i = 0;
int rv = -1;
virTypedParameterPtr params = NULL;
/* Check the length of the returned list carefully. */
if (args_params_len > limit) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (VIR_ALLOC_N(params, args_params_len) < 0) {
virReportOOMError();
goto cleanup;
}
*nparams = args_params_len;
/* Deserialise the result. */
for (i = 0; i < args_params_len; ++i) {
if (virStrcpyStatic(params[i].field,
args_params_val[i].field) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Parameter %s too big for destination"),
args_params_val[i].field);
goto cleanup;
}
params[i].type = args_params_val[i].value.type;
switch (params[i].type) {
case VIR_TYPED_PARAM_INT:
params[i].value.i =
args_params_val[i].value.remote_typed_param_value_u.i;
break;
case VIR_TYPED_PARAM_UINT:
params[i].value.ui =
args_params_val[i].value.remote_typed_param_value_u.ui;
break;
case VIR_TYPED_PARAM_LLONG:
params[i].value.l =
args_params_val[i].value.remote_typed_param_value_u.l;
break;
case VIR_TYPED_PARAM_ULLONG:
params[i].value.ul =
args_params_val[i].value.remote_typed_param_value_u.ul;
break;
case VIR_TYPED_PARAM_DOUBLE:
params[i].value.d =
args_params_val[i].value.remote_typed_param_value_u.d;
break;
case VIR_TYPED_PARAM_BOOLEAN:
params[i].value.b =
args_params_val[i].value.remote_typed_param_value_u.b;
break;
case VIR_TYPED_PARAM_STRING:
params[i].value.s =
strdup(args_params_val[i].value.remote_typed_param_value_u.s);
if (params[i].value.s == NULL) {
virReportOOMError();
goto cleanup;
}
break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown parameter type: %d"),
params[i].type);
goto cleanup;
}
}
rv = 0;
cleanup:
if (rv < 0) {
virTypedParameterArrayClear(params, i);
VIR_FREE(params);
}
return params;
}
static int
remoteDispatchDomainGetSchedulerParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_scheduler_parameters_args *args,
remote_domain_get_scheduler_parameters_ret *ret)
{
virDomainPtr dom = NULL;
virTypedParameterPtr params = NULL;
int nparams = args->nparams;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (nparams > REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (nparams && VIR_ALLOC_N(params, nparams) < 0)
goto no_memory;
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainGetSchedulerParameters(dom, params, &nparams) < 0)
goto cleanup;
if (remoteSerializeTypedParameters(params, nparams,
&ret->params.params_val,
&ret->params.params_len,
0) < 0)
goto cleanup;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virTypedParameterArrayClear(params, nparams);
VIR_FREE(params);
if (dom)
virDomainFree(dom);
return rv;
no_memory:
virReportOOMError();
goto cleanup;
}
static int
remoteDispatchConnectListAllDomains(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_connect_list_all_domains_args *args,
remote_connect_list_all_domains_ret *ret)
{
virDomainPtr *doms = NULL;
int ndomains = 0;
int i;
int rv = -1;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if ((ndomains = virConnectListAllDomains(priv->conn,
args->need_results ? &doms : NULL,
args->flags)) < 0)
goto cleanup;
if (doms && ndomains) {
if (VIR_ALLOC_N(ret->domains.domains_val, ndomains) < 0) {
virReportOOMError();
goto cleanup;
}
ret->domains.domains_len = ndomains;
for (i = 0; i < ndomains; i++)
make_nonnull_domain(ret->domains.domains_val + i, doms[i]);
} else {
ret->domains.domains_len = 0;
ret->domains.domains_val = NULL;
}
ret->ret = ndomains;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (doms) {
for (i = 0; i < ndomains; i++)
virDomainFree(doms[i]);
VIR_FREE(doms);
}
return rv;
}
static int
remoteDispatchDomainGetSchedulerParametersFlags(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_scheduler_parameters_flags_args *args,
remote_domain_get_scheduler_parameters_flags_ret *ret)
{
virDomainPtr dom = NULL;
virTypedParameterPtr params = NULL;
int nparams = args->nparams;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (nparams > REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (nparams && VIR_ALLOC_N(params, nparams) < 0)
goto no_memory;
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainGetSchedulerParametersFlags(dom, params, &nparams,
args->flags) < 0)
goto cleanup;
if (remoteSerializeTypedParameters(params, nparams,
&ret->params.params_val,
&ret->params.params_len,
args->flags) < 0)
goto cleanup;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virTypedParameterArrayClear(params, nparams);
VIR_FREE(params);
if (dom)
virDomainFree(dom);
return rv;
no_memory:
virReportOOMError();
goto cleanup;
}
static int
remoteDispatchDomainMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_memory_stats_args *args,
remote_domain_memory_stats_ret *ret)
{
virDomainPtr dom = NULL;
struct _virDomainMemoryStat *stats;
int nr_stats, i;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (args->maxStats > REMOTE_DOMAIN_MEMORY_STATS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("maxStats > REMOTE_DOMAIN_MEMORY_STATS_MAX"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
/* Allocate stats array for making dispatch call */
if (VIR_ALLOC_N(stats, args->maxStats) < 0) {
virReportOOMError();
goto cleanup;
Add several missing vir*Free calls in libvirtd's remote code Justin Clift reported a problem with adding virStoragePoolIsPersistent to virsh's pool-info command, resulting in a strange problem. Here's an example: virsh # pool-create-as images_dir3 dir - - - - "/home/images2" Pool images_dir3 created virsh # pool-info images_dir3 Name: images_dir3 UUID: 90301885-94eb-4ca7-14c2-f30b25a29a36 State: running Capacity: 395.20 GB Allocation: 30.88 GB Available: 364.33 GB virsh # pool-destroy images_dir3 Pool images_dir3 destroyed At this point the images_dir3 pool should be gone (because it was transient) and we should be able to create a new pool with the same name: virsh # pool-create-as images_dir3 dir - - - - "/home/images2" Pool images_dir3 created virsh # pool-info images_dir3 Name: images_dir3 UUID: 90301885-94eb-4ca7-14c2-f30b25a29a36 error: Storage pool not found The new pool got the same UUID as the first one, but we didn't specify one. libvirt should have picked a random UUID, but it didn't. It turned out that virStoragePoolIsPersistent leaks a reference to the storage pool object (actually remoteDispatchStoragePoolIsPersistent does). As a result, pool-destroy doesn't remove the virStoragePool for the "images_dir3" pool from the virConnectPtr's storagePools hash on libvirtd's side. Then the second pool-create-as get's the stale virStoragePool object associated with the "images_dir3" name. But this object has the old UUID. This commit ensures that all get_nonnull_* and make_nonnull_* calls for libvirt objects are matched properly with vir*Free calls. This fixes the reference leaks and the reported problem. All remoteDispatch*IsActive and remoteDispatch*IsPersistent functions were affected. But also remoteDispatchDomainMigrateFinish2 was affected in the success path. I wonder why that didn't surface earlier. Probably because domainMigrateFinish2 is executed on the destination host and in the common case this connection is opened especially for the migration and gets closed after the migration is done. So there was no chance to run into a problem because of the leaked reference.
2010-06-12 15:13:33 +00:00
}
libvirt: do not mix internal flags into public API There were two API in driver.c that were silently masking flags bits prior to calling out to the drivers, and several others that were explicitly masking flags bits. This is not forward-compatible - if we ever have that many flags in the future, then talking to an old server that masks out the flags would be indistinguishable from talking to a new server that can honor the flag. In general, libvirt.c should forward _all_ flags on to drivers, and only the drivers should reject unknown flags. In the case of virDrvSecretGetValue, the solution is to separate the internal driver callback function to have two parameters instead of one, with only one parameter affected by the public API. In the case of virDomainGetXMLDesc, it turns out that no one was ever mixing VIR_DOMAIN_XML_INTERNAL_STATUS with the dumpxml path in the first place; that internal flag was only used in saving and restoring state files, which happened to be in functions internal to a single file, so there is no mixing of the internal flag with a public flags argument. Additionally, virDomainMemoryStats passed a flags argument over RPC, but not to the driver. * src/driver.h (VIR_DOMAIN_XML_FLAGS_MASK) (VIR_SECRET_GET_VALUE_FLAGS_MASK): Delete. (virDrvSecretGetValue): Separate out internal flags. (virDrvDomainMemoryStats): Provide missing flags argument. * src/driver.c (verify): Drop unused check. * src/conf/domain_conf.h (virDomainObjParseFile): Delete declaration. (virDomainXMLInternalFlags): Move... * src/conf/domain_conf.c: ...here. Delete redundant include. (virDomainObjParseFile): Make static. * src/libvirt.c (virDomainGetXMLDesc, virSecretGetValue): Update clients. (virDomainMemoryPeek, virInterfaceGetXMLDesc) (virDomainMemoryStats, virDomainBlockPeek, virNetworkGetXMLDesc) (virStoragePoolGetXMLDesc, virStorageVolGetXMLDesc) (virNodeNumOfDevices, virNodeListDevices, virNWFilterGetXMLDesc): Don't mask unknown flags. * src/interface/netcf_driver.c (interfaceGetXMLDesc): Reject unknown flags. * src/secret/secret_driver.c (secretGetValue): Update clients. * src/remote/remote_driver.c (remoteSecretGetValue) (remoteDomainMemoryStats): Likewise. * src/qemu/qemu_process.c (qemuProcessGetVolumeQcowPassphrase): Likewise. * src/qemu/qemu_driver.c (qemudDomainMemoryStats): Likewise. * daemon/remote.c (remoteDispatchDomainMemoryStats): Likewise.
2011-07-13 21:31:56 +00:00
nr_stats = virDomainMemoryStats(dom, stats, args->maxStats, args->flags);
if (nr_stats < 0)
goto cleanup;
/* Allocate return buffer */
if (VIR_ALLOC_N(ret->stats.stats_val, args->maxStats) < 0) {
virReportOOMError();
goto cleanup;
}
/* Copy the stats into the xdr return structure */
for (i = 0; i < nr_stats; i++) {
ret->stats.stats_val[i].tag = stats[i].tag;
ret->stats.stats_val[i].val = stats[i].val;
}
ret->stats.stats_len = nr_stats;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
VIR_FREE(stats);
return rv;
}
static int
remoteDispatchDomainBlockPeek(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_block_peek_args *args,
remote_domain_block_peek_ret *ret)
{
virDomainPtr dom = NULL;
char *path;
unsigned long long offset;
size_t size;
unsigned int flags;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
path = args->path;
offset = args->offset;
size = args->size;
flags = args->flags;
if (size > REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("size > maximum buffer size"));
goto cleanup;
}
ret->buffer.buffer_len = size;
if (VIR_ALLOC_N(ret->buffer.buffer_val, size) < 0) {
virReportOOMError();
goto cleanup;
}
if (virDomainBlockPeek(dom, path, offset, size,
ret->buffer.buffer_val, flags) < 0)
goto cleanup;
rv = 0;
cleanup:
if (rv < 0) {
virNetMessageSaveError(rerr);
VIR_FREE(ret->buffer.buffer_val);
}
if (dom)
virDomainFree(dom);
return rv;
}
2011-09-05 08:20:03 +00:00
static int
remoteDispatchDomainBlockStatsFlags(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
2011-09-05 08:20:03 +00:00
virNetMessageErrorPtr rerr,
remote_domain_block_stats_flags_args *args,
remote_domain_block_stats_flags_ret *ret)
{
virTypedParameterPtr params = NULL;
virDomainPtr dom = NULL;
const char *path = args->path;
int nparams = args->nparams;
unsigned int flags;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
2011-09-05 08:20:03 +00:00
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
flags = args->flags;
if (nparams > REMOTE_DOMAIN_BLOCK_STATS_PARAMETERS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
2011-09-05 08:20:03 +00:00
goto cleanup;
}
if (nparams && VIR_ALLOC_N(params, nparams) < 0) {
2011-09-05 08:20:03 +00:00
virReportOOMError();
goto cleanup;
}
if (virDomainBlockStatsFlags(dom, path, params, &nparams, flags) < 0)
goto cleanup;
/* In this case, we need to send back the number of parameters
* supported
*/
if (args->nparams == 0) {
ret->nparams = nparams;
goto success;
}
/* Serialise the block stats. */
if (remoteSerializeTypedParameters(params, nparams,
&ret->params.params_val,
&ret->params.params_len,
args->flags) < 0)
2011-09-05 08:20:03 +00:00
goto cleanup;
success:
rv = 0;
cleanup:
if (rv < 0)
2011-09-05 08:20:03 +00:00
virNetMessageSaveError(rerr);
virTypedParameterArrayClear(params, nparams);
VIR_FREE(params);
if (dom)
virDomainFree(dom);
2011-09-05 08:20:03 +00:00
return rv;
}
static int
remoteDispatchDomainMemoryPeek(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_memory_peek_args *args,
remote_domain_memory_peek_ret *ret)
{
virDomainPtr dom = NULL;
unsigned long long offset;
size_t size;
unsigned int flags;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
offset = args->offset;
size = args->size;
flags = args->flags;
if (size > REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("size > maximum buffer size"));
goto cleanup;
}
ret->buffer.buffer_len = size;
if (VIR_ALLOC_N(ret->buffer.buffer_val, size) < 0) {
virReportOOMError();
goto cleanup;
}
if (virDomainMemoryPeek(dom, offset, size,
ret->buffer.buffer_val, flags) < 0)
goto cleanup;
rv = 0;
cleanup:
if (rv < 0) {
virNetMessageSaveError(rerr);
VIR_FREE(ret->buffer.buffer_val);
}
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainGetSecurityLabel(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_security_label_args *args,
remote_domain_get_security_label_ret *ret)
{
virDomainPtr dom = NULL;
virSecurityLabelPtr seclabel = NULL;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (VIR_ALLOC(seclabel) < 0) {
virReportOOMError();
goto cleanup;
}
if (virDomainGetSecurityLabel(dom, seclabel) < 0)
goto cleanup;
ret->label.label_len = strlen(seclabel->label) + 1;
if (VIR_ALLOC_N(ret->label.label_val, ret->label.label_len) < 0) {
virReportOOMError();
goto cleanup;
}
strcpy(ret->label.label_val, seclabel->label);
ret->enforcing = seclabel->enforcing;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
VIR_FREE(seclabel);
return rv;
}
static int
remoteDispatchDomainGetSecurityLabelList(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_security_label_list_args *args,
remote_domain_get_security_label_list_ret *ret)
{
virDomainPtr dom = NULL;
virSecurityLabelPtr seclabels = NULL;
int i, len, rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if ((len = virDomainGetSecurityLabelList(dom, &seclabels)) < 0) {
ret->ret = len;
ret->labels.labels_len = 0;
ret->labels.labels_val = NULL;
goto done;
}
if (VIR_ALLOC_N(ret->labels.labels_val, len) < 0) {
virReportOOMError();
goto cleanup;
}
for (i = 0; i < len; i++) {
size_t label_len = strlen(seclabels[i].label) + 1;
remote_domain_get_security_label_ret *cur = &ret->labels.labels_val[i];
if (VIR_ALLOC_N(cur->label.label_val, label_len) < 0) {
virReportOOMError();
goto cleanup;
}
if (virStrcpy(cur->label.label_val, seclabels[i].label, label_len) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to copy security label"));
goto cleanup;
}
cur->label.label_len = label_len;
cur->enforcing = seclabels[i].enforcing;
}
ret->labels.labels_len = ret->ret = len;
done:
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
VIR_FREE(seclabels);
return rv;
}
static int
remoteDispatchNodeGetSecurityModel(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_node_get_security_model_ret *ret)
{
virSecurityModel secmodel;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
memset(&secmodel, 0, sizeof(secmodel));
if (virNodeGetSecurityModel(priv->conn, &secmodel) < 0)
goto cleanup;
ret->model.model_len = strlen(secmodel.model) + 1;
if (VIR_ALLOC_N(ret->model.model_val, ret->model.model_len) < 0) {
virReportOOMError();
goto cleanup;
}
strcpy(ret->model.model_val, secmodel.model);
ret->doi.doi_len = strlen(secmodel.doi) + 1;
if (VIR_ALLOC_N(ret->doi.doi_val, ret->doi.doi_len) < 0) {
virReportOOMError();
goto cleanup;
}
strcpy(ret->doi.doi_val, secmodel.doi);
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
return rv;
}
static int
remoteDispatchDomainGetVcpuPinInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_vcpu_pin_info_args *args,
remote_domain_get_vcpu_pin_info_ret *ret)
{
virDomainPtr dom = NULL;
unsigned char *cpumaps = NULL;
int num;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (args->ncpumaps > REMOTE_VCPUINFO_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("ncpumaps > REMOTE_VCPUINFO_MAX"));
goto cleanup;
}
if (INT_MULTIPLY_OVERFLOW(args->ncpumaps, args->maplen) ||
args->ncpumaps * args->maplen > REMOTE_CPUMAPS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("maxinfo * maplen > REMOTE_CPUMAPS_MAX"));
goto cleanup;
}
/* Allocate buffers to take the results. */
if (args->maplen > 0 &&
VIR_ALLOC_N(cpumaps, args->ncpumaps * args->maplen) < 0)
goto no_memory;
if ((num = virDomainGetVcpuPinInfo(dom,
args->ncpumaps,
cpumaps,
args->maplen,
args->flags)) < 0)
goto cleanup;
ret->num = num;
/* Don't need to allocate/copy the cpumaps if we make the reasonable
* assumption that unsigned char and char are the same size.
* Note that remoteDispatchClientRequest will free.
*/
ret->cpumaps.cpumaps_len = args->ncpumaps * args->maplen;
ret->cpumaps.cpumaps_val = (char *) cpumaps;
cpumaps = NULL;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
VIR_FREE(cpumaps);
if (dom)
virDomainFree(dom);
return rv;
no_memory:
virReportOOMError();
goto cleanup;
}
static int
remoteDispatchDomainPinEmulator(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_pin_emulator_args *args)
{
int rv = -1;
virDomainPtr dom = NULL;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainPinEmulator(dom,
(unsigned char *) args->cpumap.cpumap_val,
args->cpumap.cpumap_len,
args->flags) < 0)
goto cleanup;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainGetEmulatorPinInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_emulator_pin_info_args *args,
remote_domain_get_emulator_pin_info_ret *ret)
{
virDomainPtr dom = NULL;
unsigned char *cpumaps = NULL;
int r;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
/* Allocate buffers to take the results */
if (args->maplen > 0 &&
VIR_ALLOC_N(cpumaps, args->maplen) < 0)
goto no_memory;
if ((r = virDomainGetEmulatorPinInfo(dom,
cpumaps,
args->maplen,
args->flags)) < 0)
goto cleanup;
ret->ret = r;
ret->cpumaps.cpumaps_len = args->maplen;
ret->cpumaps.cpumaps_val = (char *) cpumaps;
cpumaps = NULL;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
VIR_FREE(cpumaps);
if (dom)
virDomainFree(dom);
return rv;
no_memory:
virReportOOMError();
goto cleanup;
}
static int
remoteDispatchDomainGetVcpus(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_vcpus_args *args,
remote_domain_get_vcpus_ret *ret)
{
virDomainPtr dom = NULL;
virVcpuInfoPtr info = NULL;
unsigned char *cpumaps = NULL;
int info_len, i;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (args->maxinfo > REMOTE_VCPUINFO_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("maxinfo > REMOTE_VCPUINFO_MAX"));
goto cleanup;
}
if (INT_MULTIPLY_OVERFLOW(args->maxinfo, args->maplen) ||
args->maxinfo * args->maplen > REMOTE_CPUMAPS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("maxinfo * maplen > REMOTE_CPUMAPS_MAX"));
goto cleanup;
}
/* Allocate buffers to take the results. */
if (VIR_ALLOC_N(info, args->maxinfo) < 0)
goto no_memory;
if (args->maplen > 0 &&
VIR_ALLOC_N(cpumaps, args->maxinfo * args->maplen) < 0)
goto no_memory;
if ((info_len = virDomainGetVcpus(dom,
info, args->maxinfo,
cpumaps, args->maplen)) < 0)
goto cleanup;
/* Allocate the return buffer for info. */
ret->info.info_len = info_len;
if (VIR_ALLOC_N(ret->info.info_val, info_len) < 0)
goto no_memory;
for (i = 0; i < info_len; ++i) {
ret->info.info_val[i].number = info[i].number;
ret->info.info_val[i].state = info[i].state;
ret->info.info_val[i].cpu_time = info[i].cpuTime;
ret->info.info_val[i].cpu = info[i].cpu;
}
/* Don't need to allocate/copy the cpumaps if we make the reasonable
* assumption that unsigned char and char are the same size.
* Note that remoteDispatchClientRequest will free.
*/
ret->cpumaps.cpumaps_len = args->maxinfo * args->maplen;
ret->cpumaps.cpumaps_val = (char *) cpumaps;
cpumaps = NULL;
rv = 0;
cleanup:
if (rv < 0) {
virNetMessageSaveError(rerr);
VIR_FREE(ret->info.info_val);
}
VIR_FREE(cpumaps);
VIR_FREE(info);
if (dom)
virDomainFree(dom);
return rv;
no_memory:
virReportOOMError();
goto cleanup;
}
static int
remoteDispatchDomainMigratePrepare(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_migrate_prepare_args *args,
remote_domain_migrate_prepare_ret *ret)
{
char *cookie = NULL;
int cookielen = 0;
char *uri_in;
char **uri_out;
char *dname;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
dname = args->dname == NULL ? NULL : *args->dname;
/* Wacky world of XDR ... */
if (VIR_ALLOC(uri_out) < 0) {
virReportOOMError();
goto cleanup;
}
if (virDomainMigratePrepare(priv->conn, &cookie, &cookielen,
uri_in, uri_out,
args->flags, dname, args->resource) < 0)
goto cleanup;
/* remoteDispatchClientRequest will free cookie, uri_out and
* the string if there is one.
*/
ret->cookie.cookie_len = cookielen;
ret->cookie.cookie_val = cookie;
if (*uri_out == NULL) {
ret->uri_out = NULL;
} else {
ret->uri_out = uri_out;
uri_out = NULL;
}
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
VIR_FREE(uri_out);
return rv;
}
static int
remoteDispatchDomainMigratePrepare2(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_migrate_prepare2_args *args,
remote_domain_migrate_prepare2_ret *ret)
{
char *cookie = NULL;
int cookielen = 0;
char *uri_in;
char **uri_out;
char *dname;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
dname = args->dname == NULL ? NULL : *args->dname;
/* Wacky world of XDR ... */
if (VIR_ALLOC(uri_out) < 0) {
virReportOOMError();
goto cleanup;
}
if (virDomainMigratePrepare2(priv->conn, &cookie, &cookielen,
uri_in, uri_out,
args->flags, dname, args->resource,
args->dom_xml) < 0)
goto cleanup;
/* remoteDispatchClientRequest will free cookie, uri_out and
* the string if there is one.
*/
ret->cookie.cookie_len = cookielen;
ret->cookie.cookie_val = cookie;
ret->uri_out = *uri_out == NULL ? NULL : uri_out;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
return rv;
}
static int
remoteDispatchDomainGetMemoryParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_memory_parameters_args *args,
remote_domain_get_memory_parameters_ret *ret)
{
virDomainPtr dom = NULL;
virTypedParameterPtr params = NULL;
int nparams = args->nparams;
unsigned int flags;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
flags = args->flags;
if (nparams > REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (nparams && VIR_ALLOC_N(params, nparams) < 0) {
virReportOOMError();
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainGetMemoryParameters(dom, params, &nparams, flags) < 0)
goto cleanup;
/* In this case, we need to send back the number of parameters
* supported
*/
if (args->nparams == 0) {
ret->nparams = nparams;
goto success;
}
if (remoteSerializeTypedParameters(params, nparams,
&ret->params.params_val,
&ret->params.params_len,
args->flags) < 0)
goto cleanup;
success:
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virTypedParameterArrayClear(params, nparams);
VIR_FREE(params);
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainGetNumaParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_numa_parameters_args *args,
remote_domain_get_numa_parameters_ret *ret)
{
virDomainPtr dom = NULL;
virTypedParameterPtr params = NULL;
int nparams = args->nparams;
unsigned int flags;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
flags = args->flags;
if (nparams > REMOTE_DOMAIN_NUMA_PARAMETERS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (nparams && VIR_ALLOC_N(params, nparams) < 0) {
virReportOOMError();
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainGetNumaParameters(dom, params, &nparams, flags) < 0)
goto cleanup;
/* In this case, we need to send back the number of parameters
* supported
*/
if (args->nparams == 0) {
ret->nparams = nparams;
goto success;
}
if (remoteSerializeTypedParameters(params, nparams,
&ret->params.params_val,
&ret->params.params_len,
flags) < 0)
goto cleanup;
success:
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virTypedParameterArrayClear(params, nparams);
VIR_FREE(params);
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainGetBlkioParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_blkio_parameters_args *args,
remote_domain_get_blkio_parameters_ret *ret)
{
virDomainPtr dom = NULL;
virTypedParameterPtr params = NULL;
int nparams = args->nparams;
unsigned int flags;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
flags = args->flags;
if (nparams > REMOTE_DOMAIN_BLKIO_PARAMETERS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (nparams && VIR_ALLOC_N(params, nparams) < 0) {
virReportOOMError();
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainGetBlkioParameters(dom, params, &nparams, flags) < 0)
goto cleanup;
/* In this case, we need to send back the number of parameters
* supported
*/
if (args->nparams == 0) {
ret->nparams = nparams;
goto success;
}
if (remoteSerializeTypedParameters(params, nparams,
&ret->params.params_val,
&ret->params.params_len,
args->flags) < 0)
goto cleanup;
success:
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virTypedParameterArrayClear(params, nparams);
VIR_FREE(params);
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchNodeGetCPUStats(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_node_get_cpu_stats_args *args,
remote_node_get_cpu_stats_ret *ret)
{
virNodeCPUStatsPtr params = NULL;
int i;
int cpuNum = args->cpuNum;
int nparams = args->nparams;
unsigned int flags;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
flags = args->flags;
if (nparams > REMOTE_NODE_CPU_STATS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (VIR_ALLOC_N(params, nparams) < 0) {
virReportOOMError();
goto cleanup;
}
if (virNodeGetCPUStats(priv->conn, cpuNum, params, &nparams, flags) < 0)
goto cleanup;
/* In this case, we need to send back the number of stats
* supported
*/
if (args->nparams == 0) {
ret->nparams = nparams;
goto success;
}
/* Serialise the memory parameters. */
ret->params.params_len = nparams;
if (VIR_ALLOC_N(ret->params.params_val, nparams) < 0)
goto no_memory;
for (i = 0; i < nparams; ++i) {
/* remoteDispatchClientRequest will free this: */
ret->params.params_val[i].field = strdup(params[i].field);
if (ret->params.params_val[i].field == NULL)
goto no_memory;
ret->params.params_val[i].value = params[i].value;
}
success:
rv = 0;
cleanup:
if (rv < 0) {
virNetMessageSaveError(rerr);
if (ret->params.params_val) {
for (i = 0; i < nparams; i++)
VIR_FREE(ret->params.params_val[i].field);
VIR_FREE(ret->params.params_val);
}
}
VIR_FREE(params);
return rv;
no_memory:
virReportOOMError();
goto cleanup;
}
static int
remoteDispatchNodeGetMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_node_get_memory_stats_args *args,
remote_node_get_memory_stats_ret *ret)
{
virNodeMemoryStatsPtr params = NULL;
int i;
int cellNum = args->cellNum;
int nparams = args->nparams;
unsigned int flags;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
flags = args->flags;
if (nparams > REMOTE_NODE_MEMORY_STATS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (VIR_ALLOC_N(params, nparams) < 0) {
virReportOOMError();
goto cleanup;
}
if (virNodeGetMemoryStats(priv->conn, cellNum, params, &nparams, flags) < 0)
goto cleanup;
/* In this case, we need to send back the number of parameters
* supported
*/
if (args->nparams == 0) {
ret->nparams = nparams;
goto success;
}
/* Serialise the memory parameters. */
ret->params.params_len = nparams;
if (VIR_ALLOC_N(ret->params.params_val, nparams) < 0)
goto no_memory;
for (i = 0; i < nparams; ++i) {
/* remoteDispatchClientRequest will free this: */
ret->params.params_val[i].field = strdup(params[i].field);
if (ret->params.params_val[i].field == NULL)
goto no_memory;
ret->params.params_val[i].value = params[i].value;
}
success:
rv = 0;
cleanup:
if (rv < 0) {
virNetMessageSaveError(rerr);
if (ret->params.params_val) {
for (i = 0; i < nparams; i++)
VIR_FREE(ret->params.params_val[i].field);
VIR_FREE(ret->params.params_val);
}
}
VIR_FREE(params);
return rv;
no_memory:
virReportOOMError();
goto cleanup;
}
static int
remoteDispatchDomainGetBlockJobInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_block_job_info_args *args,
remote_domain_get_block_job_info_ret *ret)
{
virDomainPtr dom = NULL;
virDomainBlockJobInfo tmp;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
rv = virDomainGetBlockJobInfo(dom, args->path, &tmp, args->flags);
if (rv <= 0)
goto cleanup;
ret->type = tmp.type;
ret->bandwidth = tmp.bandwidth;
ret->cur = tmp.cur;
ret->end = tmp.end;
ret->found = 1;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainGetBlockIoTune(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr hdr ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_block_io_tune_args *args,
remote_domain_get_block_io_tune_ret *ret)
{
virDomainPtr dom = NULL;
int rv = -1;
virTypedParameterPtr params = NULL;
int nparams = args->nparams;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (nparams > REMOTE_DOMAIN_BLOCK_IO_TUNE_PARAMETERS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (nparams && VIR_ALLOC_N(params, nparams) < 0) {
virReportOOMError();
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainGetBlockIoTune(dom, args->disk ? *args->disk : NULL,
params, &nparams, args->flags) < 0)
goto cleanup;
/* In this case, we need to send back the number of parameters
* supported
*/
if (args->nparams == 0) {
ret->nparams = nparams;
goto success;
}
/* Serialise the block I/O tuning parameters. */
if (remoteSerializeTypedParameters(params, nparams,
&ret->params.params_val,
&ret->params.params_len,
args->flags) < 0)
goto cleanup;
success:
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virTypedParameterArrayClear(params, nparams);
VIR_FREE(params);
if (dom)
virDomainFree(dom);
return rv;
}
/*-------------------------------------------------------------*/
static int
remoteDispatchAuthList(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_auth_list_ret *ret)
{
int rv = -1;
int auth = virNetServerClientGetAuth(client);
uid_t callerUid;
gid_t callerGid;
pid_t callerPid;
/* If the client is root then we want to bypass the
* policykit auth to avoid root being denied if
* some piece of polkit isn't present/running
*/
if (auth == VIR_NET_SERVER_SERVICE_AUTH_POLKIT) {
if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid,
&callerPid) < 0) {
/* Don't do anything on error - it'll be validated at next
* phase of auth anyway */
virResetLastError();
} else if (callerUid == 0) {
char *ident;
if (virAsprintf(&ident, "pid:%lld,uid:%d",
(long long) callerPid, callerUid) < 0) {
virReportOOMError();
goto cleanup;
}
VIR_INFO("Bypass polkit auth for privileged client %s", ident);
if (virNetServerClientSetIdentity(client, ident) < 0)
virResetLastError();
else
auth = VIR_NET_SERVER_SERVICE_AUTH_NONE;
VIR_FREE(ident);
}
}
ret->types.types_len = 1;
if (VIR_ALLOC_N(ret->types.types_val, ret->types.types_len) < 0) {
virReportOOMError();
goto cleanup;
}
switch (auth) {
case VIR_NET_SERVER_SERVICE_AUTH_NONE:
ret->types.types_val[0] = REMOTE_AUTH_NONE;
break;
case VIR_NET_SERVER_SERVICE_AUTH_POLKIT:
ret->types.types_val[0] = REMOTE_AUTH_POLKIT;
break;
case VIR_NET_SERVER_SERVICE_AUTH_SASL:
ret->types.types_val[0] = REMOTE_AUTH_SASL;
break;
default:
ret->types.types_val[0] = REMOTE_AUTH_NONE;
}
2008-12-04 22:16:40 +00:00
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
return rv;
}
#ifdef HAVE_SASL
/*
* Initializes the SASL session in prepare for authentication
* and gives the client a list of allowed mechanisms to choose
*/
static int
remoteDispatchAuthSaslInit(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_auth_sasl_init_ret *ret)
{
virNetSASLSessionPtr sasl = NULL;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
virMutexLock(&priv->lock);
2008-12-04 22:16:40 +00:00
VIR_DEBUG("Initialize SASL auth %d", virNetServerClientGetFD(client));
if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL ||
priv->sasl != NULL) {
VIR_ERROR(_("client tried invalid SASL init request"));
2008-12-04 22:16:40 +00:00
goto authfail;
}
sasl = virNetSASLSessionNewServer(saslCtxt,
"libvirt",
virNetServerClientLocalAddrString(client),
virNetServerClientRemoteAddrString(client));
if (!sasl)
2008-12-04 22:16:40 +00:00
goto authfail;
/* Inform SASL that we've got an external SSF layer from TLS */
if (virNetServerClientHasTLSSession(client)) {
int ssf;
if ((ssf = virNetServerClientGetTLSKeySize(client)) < 0)
2008-12-04 22:16:40 +00:00
goto authfail;
ssf *= 8; /* key size is bytes, sasl wants bits */
VIR_DEBUG("Setting external SSF %d", ssf);
if (virNetSASLSessionExtKeySize(sasl, ssf) < 0)
2008-12-04 22:16:40 +00:00
goto authfail;
}
if (virNetServerClientIsSecure(client))
/* If we've got TLS or UNIX domain sock, we don't care about SSF */
virNetSASLSessionSecProps(sasl, 0, 0, true);
else
/* Plain TCP, better get an SSF layer */
virNetSASLSessionSecProps(sasl,
56, /* Good enough to require kerberos */
100000, /* Arbitrary big number */
false); /* No anonymous */
if (!(ret->mechlist = virNetSASLSessionListMechanisms(sasl)))
2008-12-04 22:16:40 +00:00
goto authfail;
VIR_DEBUG("Available mechanisms for client: '%s'", ret->mechlist);
priv->sasl = sasl;
virMutexUnlock(&priv->lock);
return 0;
2008-12-04 22:16:40 +00:00
authfail:
virResetLastError();
virReportError(VIR_ERR_AUTH_FAILED, "%s",
_("authentication failed"));
virNetMessageSaveError(rerr);
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
"client=%p auth=%d",
client, REMOTE_AUTH_SASL);
virObjectUnref(sasl);
virMutexUnlock(&priv->lock);
2008-12-04 22:16:40 +00:00
return -1;
}
/*
* Returns 0 if ok, -1 on error, -2 if rejected
*/
static int
remoteSASLFinish(virNetServerClientPtr client)
{
const char *identity;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
int ssf;
/* TLS or UNIX domain sockets trivially OK */
if (!virNetServerClientIsSecure(client)) {
if ((ssf = virNetSASLSessionGetKeySize(priv->sasl)) < 0)
goto error;
VIR_DEBUG("negotiated an SSF of %d", ssf);
if (ssf < 56) { /* 56 is good for Kerberos */
VIR_ERROR(_("negotiated SSF %d was not strong enough"), ssf);
return -2;
}
}
if (!(identity = virNetSASLSessionGetIdentity(priv->sasl)))
return -2;
if (!virNetSASLContextCheckIdentity(saslCtxt, identity))
return -2;
if (virNetServerClientSetIdentity(client, identity) < 0)
goto error;
virNetServerClientSetSASLSession(client, priv->sasl);
VIR_DEBUG("Authentication successful %d", virNetServerClientGetFD(client));
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_ALLOW,
"client=%p auth=%d identity=%s",
client, REMOTE_AUTH_SASL, identity);
virObjectUnref(priv->sasl);
priv->sasl = NULL;
return 0;
error:
return -1;
}
/*
* This starts the SASL authentication negotiation.
*/
static int
remoteDispatchAuthSaslStart(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_auth_sasl_start_args *args,
remote_auth_sasl_start_ret *ret)
{
const char *serverout;
size_t serveroutlen;
int err;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
const char *identity;
virMutexLock(&priv->lock);
2008-12-04 22:16:40 +00:00
VIR_DEBUG("Start SASL auth %d", virNetServerClientGetFD(client));
if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL ||
priv->sasl == NULL) {
VIR_ERROR(_("client tried invalid SASL start request"));
2008-12-04 22:16:40 +00:00
goto authfail;
}
VIR_DEBUG("Using SASL mechanism %s. Data %d bytes, nil: %d",
args->mech, args->data.data_len, args->nil);
err = virNetSASLSessionServerStart(priv->sasl,
args->mech,
/* NB, distinction of NULL vs "" is *critical* in SASL */
args->nil ? NULL : args->data.data_val,
args->data.data_len,
&serverout,
&serveroutlen);
if (err != VIR_NET_SASL_COMPLETE &&
err != VIR_NET_SASL_CONTINUE)
2008-12-04 22:16:40 +00:00
goto authfail;
if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) {
VIR_ERROR(_("sasl start reply data too long %d"), (int)serveroutlen);
2008-12-04 22:16:40 +00:00
goto authfail;
}
/* NB, distinction of NULL vs "" is *critical* in SASL */
if (serverout) {
if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0)
goto authfail;
memcpy(ret->data.data_val, serverout, serveroutlen);
} else {
ret->data.data_val = NULL;
}
ret->nil = serverout ? 0 : 1;
ret->data.data_len = serveroutlen;
VIR_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil);
if (err == VIR_NET_SASL_CONTINUE) {
ret->complete = 0;
} else {
/* Check username whitelist ACL */
if ((err = remoteSASLFinish(client)) < 0) {
if (err == -2)
goto authdeny;
else
goto authfail;
}
ret->complete = 1;
}
virMutexUnlock(&priv->lock);
return 0;
2008-12-04 22:16:40 +00:00
authfail:
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
"client=%p auth=%d",
client, REMOTE_AUTH_SASL);
goto error;
authdeny:
identity = virNetSASLSessionGetIdentity(priv->sasl);
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_DENY,
"client=%p auth=%d identity=%s",
client, REMOTE_AUTH_SASL, identity);
goto error;
2008-12-04 22:16:40 +00:00
error:
virObjectUnref(priv->sasl);
priv->sasl = NULL;
virResetLastError();
virReportError(VIR_ERR_AUTH_FAILED, "%s",
_("authentication failed"));
if (rv < 0)
virNetMessageSaveError(rerr);
virMutexUnlock(&priv->lock);
2008-12-04 22:16:40 +00:00
return -1;
}
static int
remoteDispatchAuthSaslStep(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_auth_sasl_step_args *args,
remote_auth_sasl_step_ret *ret)
{
const char *serverout;
size_t serveroutlen;
int err;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
const char *identity;
2008-12-04 22:16:40 +00:00
virMutexLock(&priv->lock);
VIR_DEBUG("Step SASL auth %d", virNetServerClientGetFD(client));
if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL ||
priv->sasl == NULL) {
VIR_ERROR(_("client tried invalid SASL start request"));
2008-12-04 22:16:40 +00:00
goto authfail;
}
VIR_DEBUG("Step using SASL Data %d bytes, nil: %d",
args->data.data_len, args->nil);
err = virNetSASLSessionServerStep(priv->sasl,
/* NB, distinction of NULL vs "" is *critical* in SASL */
args->nil ? NULL : args->data.data_val,
args->data.data_len,
&serverout,
&serveroutlen);
if (err != VIR_NET_SASL_COMPLETE &&
err != VIR_NET_SASL_CONTINUE)
2008-12-04 22:16:40 +00:00
goto authfail;
if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) {
2009-01-06 18:32:03 +00:00
VIR_ERROR(_("sasl step reply data too long %d"),
(int)serveroutlen);
2008-12-04 22:16:40 +00:00
goto authfail;
}
/* NB, distinction of NULL vs "" is *critical* in SASL */
if (serverout) {
if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0)
goto authfail;
memcpy(ret->data.data_val, serverout, serveroutlen);
} else {
ret->data.data_val = NULL;
}
ret->nil = serverout ? 0 : 1;
ret->data.data_len = serveroutlen;
VIR_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil);
if (err == VIR_NET_SASL_CONTINUE) {
ret->complete = 0;
} else {
/* Check username whitelist ACL */
if ((err = remoteSASLFinish(client)) < 0) {
if (err == -2)
goto authdeny;
else
goto authfail;
}
ret->complete = 1;
}
virMutexUnlock(&priv->lock);
return 0;
2008-12-04 22:16:40 +00:00
authfail:
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
"client=%p auth=%d",
client, REMOTE_AUTH_SASL);
goto error;
authdeny:
identity = virNetSASLSessionGetIdentity(priv->sasl);
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_DENY,
"client=%p auth=%d identity=%s",
client, REMOTE_AUTH_SASL, identity);
goto error;
2008-12-04 22:16:40 +00:00
error:
virObjectUnref(priv->sasl);
priv->sasl = NULL;
virResetLastError();
virReportError(VIR_ERR_AUTH_FAILED, "%s",
_("authentication failed"));
if (rv < 0)
virNetMessageSaveError(rerr);
virMutexUnlock(&priv->lock);
return -1;
}
#else
static int
remoteDispatchAuthSaslInit(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED)
{
VIR_WARN("Client tried unsupported SASL auth");
virReportError(VIR_ERR_AUTH_FAILED, "%s",
_("authentication failed"));
virNetMessageSaveError(rerr);
return -1;
}
static int
remoteDispatchAuthSaslStart(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED,
remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED)
{
VIR_WARN("Client tried unsupported SASL auth");
virReportError(VIR_ERR_AUTH_FAILED, "%s",
_("authentication failed"));
virNetMessageSaveError(rerr);
return -1;
}
static int
remoteDispatchAuthSaslStep(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED,
remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED)
{
VIR_WARN("Client tried unsupported SASL auth");
virReportError(VIR_ERR_AUTH_FAILED, "%s",
_("authentication failed"));
virNetMessageSaveError(rerr);
return -1;
}
#endif
#if HAVE_POLKIT1
static int
remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_auth_polkit_ret *ret)
{
pid_t callerPid = -1;
gid_t callerGid = -1;
uid_t callerUid = -1;
const char *action;
int status = -1;
char *ident = NULL;
bool authdismissed = 0;
char *pkout = NULL;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
virCommandPtr cmd = NULL;
virMutexLock(&priv->lock);
action = virNetServerClientGetReadonly(client) ?
"org.libvirt.unix.monitor" :
"org.libvirt.unix.manage";
cmd = virCommandNewArgList(PKCHECK_PATH, "--action-id", action, NULL);
virCommandSetOutputBuffer(cmd, &pkout);
virCommandSetErrorBuffer(cmd, &pkout);
VIR_DEBUG("Start PolicyKit auth %d", virNetServerClientGetFD(client));
if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_POLKIT) {
VIR_ERROR(_("client tried invalid PolicyKit init request"));
goto authfail;
}
if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid,
&callerPid) < 0) {
goto authfail;
}
VIR_INFO("Checking PID %lld running as %d",
(long long) callerPid, callerUid);
virCommandAddArg(cmd, "--process");
virCommandAddArgFormat(cmd, "%lld", (long long) callerPid);
virCommandAddArg(cmd, "--allow-user-interaction");
if (virAsprintf(&ident, "pid:%lld,uid:%d",
(long long) callerPid, callerUid) < 0) {
virReportOOMError();
goto authfail;
}
if (virCommandRun(cmd, &status) < 0)
goto authfail;
authdismissed = (pkout && strstr(pkout, "dismissed=true"));
if (status != 0) {
char *tmp = virCommandTranslateStatus(status);
VIR_ERROR(_("Policy kit denied action %s from pid %lld, uid %d: %s"),
action, (long long) callerPid, callerUid, NULLSTR(tmp));
VIR_FREE(tmp);
goto authdeny;
}
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_ALLOW,
"client=%p auth=%d identity=%s",
client, REMOTE_AUTH_POLKIT, ident);
VIR_INFO("Policy allowed action %s from pid %lld, uid %d",
action, (long long) callerPid, callerUid);
ret->complete = 1;
virNetServerClientSetIdentity(client, ident);
virMutexUnlock(&priv->lock);
virCommandFree(cmd);
VIR_FREE(pkout);
VIR_FREE(ident);
return 0;
error:
virCommandFree(cmd);
VIR_FREE(ident);
virResetLastError();
if (authdismissed) {
virReportError(VIR_ERR_AUTH_CANCELLED, "%s",
_("authentication cancelled by user"));
} else {
virReportError(VIR_ERR_AUTH_FAILED, "%s",
pkout && *pkout ? pkout : _("authentication failed"));
}
VIR_FREE(pkout);
virNetMessageSaveError(rerr);
virMutexUnlock(&priv->lock);
return -1;
authfail:
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
"client=%p auth=%d",
client, REMOTE_AUTH_POLKIT);
goto error;
authdeny:
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_DENY,
"client=%p auth=%d identity=%s",
client, REMOTE_AUTH_POLKIT, ident);
goto error;
}
#elif HAVE_POLKIT0
2007-12-05 18:21:27 +00:00
static int
remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_auth_polkit_ret *ret)
2007-12-05 18:21:27 +00:00
{
pid_t callerPid;
gid_t callerGid;
2007-12-05 18:21:27 +00:00
uid_t callerUid;
2008-04-04 15:09:19 +00:00
PolKitCaller *pkcaller = NULL;
PolKitAction *pkaction = NULL;
PolKitContext *pkcontext = NULL;
PolKitError *pkerr = NULL;
PolKitResult pkresult;
DBusError err;
2008-12-04 22:16:40 +00:00
const char *action;
char *ident = NULL;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
DBusConnection *sysbus;
virMutexLock(&priv->lock);
2008-12-04 22:16:40 +00:00
action = virNetServerClientGetReadonly(client) ?
2008-04-04 15:09:19 +00:00
"org.libvirt.unix.monitor" :
"org.libvirt.unix.manage";
2007-12-05 18:21:27 +00:00
VIR_DEBUG("Start PolicyKit auth %d", virNetServerClientGetFD(client));
if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_POLKIT) {
VIR_ERROR(_("client tried invalid PolicyKit init request"));
2008-12-04 22:16:40 +00:00
goto authfail;
2007-12-05 18:21:27 +00:00
}
if (virNetServerClientGetUNIXIdentity(client, &callerUid, &callerGid,
&callerPid) < 0) {
VIR_ERROR(_("cannot get peer socket identity"));
2008-12-04 22:16:40 +00:00
goto authfail;
2007-12-05 18:21:27 +00:00
}
if (virAsprintf(&ident, "pid:%lld,uid:%d",
(long long) callerPid, callerUid) < 0) {
virReportOOMError();
goto authfail;
}
if (!(sysbus = virDBusGetSystemBus()))
goto authfail;
VIR_INFO("Checking PID %lld running as %d",
(long long) callerPid, callerUid);
2008-04-04 15:09:19 +00:00
dbus_error_init(&err);
if (!(pkcaller = polkit_caller_new_from_pid(sysbus,
2008-04-04 15:09:19 +00:00
callerPid, &err))) {
2009-01-06 18:32:03 +00:00
VIR_ERROR(_("Failed to lookup policy kit caller: %s"), err.message);
2008-04-04 15:09:19 +00:00
dbus_error_free(&err);
2008-12-04 22:16:40 +00:00
goto authfail;
2008-04-04 15:09:19 +00:00
}
2007-12-05 18:21:27 +00:00
2008-04-04 15:09:19 +00:00
if (!(pkaction = polkit_action_new())) {
char ebuf[1024];
VIR_ERROR(_("Failed to create polkit action %s"),
virStrerror(errno, ebuf, sizeof(ebuf)));
2008-04-04 15:09:19 +00:00
polkit_caller_unref(pkcaller);
2008-12-04 22:16:40 +00:00
goto authfail;
2008-04-04 15:09:19 +00:00
}
polkit_action_set_action_id(pkaction, action);
if (!(pkcontext = polkit_context_new()) ||
!polkit_context_init(pkcontext, &pkerr)) {
char ebuf[1024];
VIR_ERROR(_("Failed to create polkit context %s"),
2009-01-06 18:32:03 +00:00
(pkerr ? polkit_error_get_error_message(pkerr)
: virStrerror(errno, ebuf, sizeof(ebuf))));
2008-04-04 15:09:19 +00:00
if (pkerr)
polkit_error_free(pkerr);
polkit_caller_unref(pkcaller);
polkit_action_unref(pkaction);
dbus_error_free(&err);
2008-12-04 22:16:40 +00:00
goto authfail;
2008-04-04 15:09:19 +00:00
}
2007-12-05 18:21:27 +00:00
# if HAVE_POLKIT_CONTEXT_IS_CALLER_AUTHORIZED
2008-04-04 15:09:19 +00:00
pkresult = polkit_context_is_caller_authorized(pkcontext,
pkaction,
pkcaller,
0,
&pkerr);
if (pkerr && polkit_error_is_set(pkerr)) {
2009-01-06 18:32:03 +00:00
VIR_ERROR(_("Policy kit failed to check authorization %d %s"),
polkit_error_get_error_code(pkerr),
polkit_error_get_error_message(pkerr));
2008-12-04 22:16:40 +00:00
goto authfail;
2008-04-04 15:09:19 +00:00
}
# else
2008-04-04 15:09:19 +00:00
pkresult = polkit_context_can_caller_do_action(pkcontext,
pkaction,
pkcaller);
# endif
2008-04-04 15:09:19 +00:00
polkit_context_unref(pkcontext);
polkit_caller_unref(pkcaller);
polkit_action_unref(pkaction);
if (pkresult != POLKIT_RESULT_YES) {
VIR_ERROR(_("Policy kit denied action %s from pid %lld, uid %d, result: %s"),
action, (long long) callerPid, callerUid,
2009-01-06 18:32:03 +00:00
polkit_result_to_string_representation(pkresult));
goto authdeny;
2007-12-05 18:21:27 +00:00
}
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_ALLOW,
"client=%p auth=%d identity=%s",
client, REMOTE_AUTH_POLKIT, ident);
VIR_INFO("Policy allowed action %s from pid %lld, uid %d, result %s",
action, (long long) callerPid, callerUid,
polkit_result_to_string_representation(pkresult));
ret->complete = 1;
virNetServerClientSetIdentity(client, ident);
virMutexUnlock(&priv->lock);
VIR_FREE(ident);
return 0;
error:
VIR_FREE(ident);
virResetLastError();
virReportError(VIR_ERR_AUTH_FAILED, "%s",
_("authentication failed"));
virNetMessageSaveError(rerr);
virMutexUnlock(&priv->lock);
return -1;
authfail:
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_FAIL,
"client=%p auth=%d",
client, REMOTE_AUTH_POLKIT);
goto error;
authdeny:
Rewrite all the DTrace/SystemTAP probing The libvirtd daemon had a few crude system tap probes. Some of these were broken during the RPC rewrite. The new modular RPC code is structured in a way that allows much more effective tracing. Instead of trying to hook up the original probes, define a new set of probes for the RPC and event code. The master probes file is now src/probes.d. This contains probes for virNetServerClientPtr, virNetClientPtr, virSocketPtr virNetTLSContextPtr and virNetTLSSessionPtr modules. Also add probes for the poll event loop. The src/dtrace2systemtap.pl script can convert the probes.d file into a libvirt_probes.stp file to make use from systemtap much simpler. The src/rpc/gensystemtap.pl script can generate a set of systemtap functions for translating RPC enum values into printable strings. This works for all RPC header enums (program, type, status, procedure) and also the authentication enum The PROBE macro will automatically generate a VIR_DEBUG statement, so any place with a PROBE can remove any existing manual DEBUG statements. * daemon/libvirtd.stp, daemon/probes.d: Remove obsolete probing * daemon/libvirtd.h: Remove probe macros * daemon/Makefile.am: Remove all probe buildings/install * daemon/remote.c: Update authentication probes * src/dtrace2systemtap.pl, src/rpc/gensystemtap.pl: Scripts to generate STP files * src/internal.h: Add probe macros * src/probes.d: Master list of probes * src/rpc/virnetclient.c, src/rpc/virnetserverclient.c, src/rpc/virnetsocket.c, src/rpc/virnettlscontext.c, src/util/event_poll.c: Insert probe points, removing any DEBUG statements that duplicate the info
2011-09-30 13:40:23 +00:00
PROBE(RPC_SERVER_CLIENT_AUTH_DENY,
"client=%p auth=%d identity=%s",
client, REMOTE_AUTH_POLKIT, ident);
goto error;
}
#else /* !HAVE_POLKIT0 & !HAVE_POLKIT1*/
static int
remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED,
2011-06-28 19:09:05 +00:00
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_auth_polkit_ret *ret ATTRIBUTE_UNUSED)
{
VIR_ERROR(_("client tried unsupported PolicyKit init request"));
virReportError(VIR_ERR_AUTH_FAILED, "%s",
_("authentication failed"));
virNetMessageSaveError(rerr);
return -1;
}
#endif /* HAVE_POLKIT1 */
/***************************************************************
* NODE INFO APIS
**************************************************************/
static int
remoteDispatchNodeDeviceGetParent(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_node_device_get_parent_args *args,
remote_node_device_get_parent_ret *ret)
{
virNodeDevicePtr dev = NULL;
const char *parent = NULL;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dev = virNodeDeviceLookupByName(priv->conn, args->name)))
goto cleanup;
parent = virNodeDeviceGetParent(dev);
if (parent == NULL) {
ret->parent = NULL;
} else {
/* remoteDispatchClientRequest will free this. */
char **parent_p;
if (VIR_ALLOC(parent_p) < 0) {
virReportOOMError();
goto cleanup;
}
if (!(*parent_p = strdup(parent))) {
VIR_FREE(parent_p);
virReportOOMError();
goto cleanup;
}
ret->parent = parent_p;
}
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dev)
virNodeDeviceFree(dev);
return rv;
}
/***************************
* Register / deregister events
***************************/
static int
remoteDispatchDomainEventsRegister(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
remote_domain_events_register_ret *ret ATTRIBUTE_UNUSED)
{
int callbackID;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
virMutexLock(&priv->lock);
if (priv->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] != -1) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("domain event %d already registered"), VIR_DOMAIN_EVENT_ID_LIFECYCLE);
goto cleanup;
}
if ((callbackID = virConnectDomainEventRegisterAny(priv->conn,
NULL,
VIR_DOMAIN_EVENT_ID_LIFECYCLE,
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
client, NULL)) < 0)
goto cleanup;
priv->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = callbackID;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virMutexUnlock(&priv->lock);
return rv;
}
static int
remoteDispatchDomainEventsDeregister(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
remote_domain_events_deregister_ret *ret ATTRIBUTE_UNUSED)
{
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
virMutexLock(&priv->lock);
if (priv->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("domain event %d not registered"), VIR_DOMAIN_EVENT_ID_LIFECYCLE);
goto cleanup;
}
if (virConnectDomainEventDeregisterAny(priv->conn,
priv->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE]) < 0)
goto cleanup;
priv->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = -1;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virMutexUnlock(&priv->lock);
return rv;
}
static void
remoteDispatchDomainEventSend(virNetServerClientPtr client,
virNetServerProgramPtr program,
int procnr,
xdrproc_t proc,
void *data)
{
virNetMessagePtr msg;
2011-08-31 16:42:58 +00:00
if (!(msg = virNetMessageNew(false)))
goto cleanup;
msg->header.prog = virNetServerProgramGetID(program);
msg->header.vers = virNetServerProgramGetVersion(program);
msg->header.proc = procnr;
msg->header.type = VIR_NET_MESSAGE;
msg->header.serial = 1;
msg->header.status = VIR_NET_OK;
if (virNetMessageEncodeHeader(msg) < 0)
goto cleanup;
if (virNetMessageEncodePayload(msg, proc, data) < 0)
goto cleanup;
VIR_DEBUG("Queue event %d %zu", procnr, msg->bufferLength);
virNetServerClientSendMessage(client, msg);
xdr_free(proc, data);
return;
cleanup:
virNetMessageFree(msg);
xdr_free(proc, data);
}
static int
remoteDispatchSecretGetValue(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_secret_get_value_args *args,
remote_secret_get_value_ret *ret)
{
virSecretPtr secret = NULL;
size_t value_size;
unsigned char *value;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(secret = get_nonnull_secret(priv->conn, args->secret)))
goto cleanup;
if (!(value = virSecretGetValue(secret, &value_size, args->flags)))
goto cleanup;
ret->value.value_len = value_size;
ret->value.value_val = (char *)value;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (secret)
virSecretFree(secret);
return rv;
}
static int
remoteDispatchDomainGetState(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_state_args *args,
remote_domain_get_state_ret *ret)
{
virDomainPtr dom = NULL;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainGetState(dom, &ret->state, &ret->reason, args->flags) < 0)
goto cleanup;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
return rv;
}
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
static int
remoteDispatchDomainEventsRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
remote_domain_events_register_any_args *args)
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
{
int callbackID;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
virMutexLock(&priv->lock);
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
if (args->eventID >= VIR_DOMAIN_EVENT_ID_LAST ||
args->eventID < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"), args->eventID);
goto cleanup;
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
}
if (priv->domainEventCallbackID[args->eventID] != -1) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("domain event %d already registered"), args->eventID);
goto cleanup;
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
}
if ((callbackID = virConnectDomainEventRegisterAny(priv->conn,
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
NULL,
args->eventID,
domainEventCallbacks[args->eventID],
client, NULL)) < 0)
goto cleanup;
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
priv->domainEventCallbackID[args->eventID] = callbackID;
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virMutexUnlock(&priv->lock);
return rv;
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
}
static int
remoteDispatchDomainEventsDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
remote_domain_events_deregister_any_args *args)
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
{
int callbackID = -1;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
virMutexLock(&priv->lock);
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
if (args->eventID >= VIR_DOMAIN_EVENT_ID_LAST ||
args->eventID < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"), args->eventID);
goto cleanup;
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
}
callbackID = priv->domainEventCallbackID[args->eventID];
if (callbackID < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("domain event %d not registered"), args->eventID);
goto cleanup;
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
}
if (virConnectDomainEventDeregisterAny(priv->conn, callbackID) < 0)
goto cleanup;
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
priv->domainEventCallbackID[args->eventID] = -1;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virMutexUnlock(&priv->lock);
return rv;
Remote driver & daemon impl of new event API This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions
2010-03-18 14:56:56 +00:00
}
static int
qemuDispatchMonitorCommand(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
qemu_monitor_command_args *args,
qemu_monitor_command_ret *ret)
{
virDomainPtr dom = NULL;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainQemuMonitorCommand(dom, args->cmd, &ret->result,
args->flags) < 0)
goto cleanup;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainMigrateBegin3(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_migrate_begin3_args *args,
remote_domain_migrate_begin3_ret *ret)
{
char *xml = NULL;
virDomainPtr dom = NULL;
char *dname;
char *xmlin;
char *cookieout = NULL;
int cookieoutlen = 0;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
xmlin = args->xmlin == NULL ? NULL : *args->xmlin;
dname = args->dname == NULL ? NULL : *args->dname;
if (!(xml = virDomainMigrateBegin3(dom, xmlin,
&cookieout, &cookieoutlen,
args->flags, dname, args->resource)))
goto cleanup;
/* remoteDispatchClientRequest will free cookie and
* the xml string if there is one.
*/
ret->cookie_out.cookie_out_len = cookieoutlen;
ret->cookie_out.cookie_out_val = cookieout;
ret->xml = xml;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainMigratePrepare3(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_migrate_prepare3_args *args,
remote_domain_migrate_prepare3_ret *ret)
{
char *cookieout = NULL;
int cookieoutlen = 0;
char *uri_in;
char **uri_out;
char *dname;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
dname = args->dname == NULL ? NULL : *args->dname;
/* Wacky world of XDR ... */
if (VIR_ALLOC(uri_out) < 0) {
virReportOOMError();
goto cleanup;
}
if (virDomainMigratePrepare3(priv->conn,
args->cookie_in.cookie_in_val,
args->cookie_in.cookie_in_len,
&cookieout, &cookieoutlen,
uri_in, uri_out,
args->flags, dname, args->resource,
args->dom_xml) < 0)
goto cleanup;
/* remoteDispatchClientRequest will free cookie, uri_out and
* the string if there is one.
*/
ret->cookie_out.cookie_out_len = cookieoutlen;
ret->cookie_out.cookie_out_val = cookieout;
ret->uri_out = *uri_out == NULL ? NULL : uri_out;
rv = 0;
cleanup:
if (rv < 0) {
virNetMessageSaveError(rerr);
VIR_FREE(uri_out);
}
return rv;
}
static int
remoteDispatchDomainMigratePerform3(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_migrate_perform3_args *args,
remote_domain_migrate_perform3_ret *ret)
{
virDomainPtr dom = NULL;
char *xmlin;
char *dname;
char *uri;
char *dconnuri;
char *cookieout = NULL;
int cookieoutlen = 0;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
xmlin = args->xmlin == NULL ? NULL : *args->xmlin;
dname = args->dname == NULL ? NULL : *args->dname;
uri = args->uri == NULL ? NULL : *args->uri;
dconnuri = args->dconnuri == NULL ? NULL : *args->dconnuri;
if (virDomainMigratePerform3(dom, xmlin,
args->cookie_in.cookie_in_val,
args->cookie_in.cookie_in_len,
&cookieout, &cookieoutlen,
dconnuri, uri,
args->flags, dname, args->resource) < 0)
goto cleanup;
/* remoteDispatchClientRequest will free cookie
*/
ret->cookie_out.cookie_out_len = cookieoutlen;
ret->cookie_out.cookie_out_val = cookieout;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainMigrateFinish3(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_migrate_finish3_args *args,
remote_domain_migrate_finish3_ret *ret)
{
virDomainPtr dom = NULL;
char *cookieout = NULL;
int cookieoutlen = 0;
char *uri;
char *dconnuri;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
uri = args->uri == NULL ? NULL : *args->uri;
dconnuri = args->dconnuri == NULL ? NULL : *args->dconnuri;
if (!(dom = virDomainMigrateFinish3(priv->conn, args->dname,
args->cookie_in.cookie_in_val,
args->cookie_in.cookie_in_len,
&cookieout, &cookieoutlen,
dconnuri, uri,
args->flags,
args->cancelled)))
goto cleanup;
make_nonnull_domain(&ret->dom, dom);
/* remoteDispatchClientRequest will free cookie
*/
ret->cookie_out.cookie_out_len = cookieoutlen;
ret->cookie_out.cookie_out_val = cookieout;
rv = 0;
cleanup:
if (rv < 0) {
virNetMessageSaveError(rerr);
VIR_FREE(cookieout);
}
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainMigrateConfirm3(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_migrate_confirm3_args *args)
{
virDomainPtr dom = NULL;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainMigrateConfirm3(dom,
args->cookie_in.cookie_in_val,
args->cookie_in.cookie_in_len,
args->flags, args->cancelled) < 0)
goto cleanup;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
return rv;
}
static int remoteDispatchSupportsFeature(
virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_supports_feature_args *args,
remote_supports_feature_ret *ret)
{
int rv = -1;
int supported;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
/* This feature is checked before opening the connection, thus we must
* check it first.
*/
if (args->feature == VIR_DRV_FEATURE_PROGRAM_KEEPALIVE) {
if (virNetServerClientStartKeepAlive(client) < 0)
goto cleanup;
supported = 1;
goto done;
}
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
switch (args->feature) {
case VIR_DRV_FEATURE_FD_PASSING:
supported = 1;
break;
default:
if ((supported = virDrvSupportsFeature(priv->conn, args->feature)) < 0)
goto cleanup;
break;
}
done:
ret->supported = supported;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
return rv;
}
static int
remoteDispatchDomainOpenGraphics(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg,
virNetMessageErrorPtr rerr,
remote_domain_open_graphics_args *args)
{
virDomainPtr dom = NULL;
int rv = -1;
int fd = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if ((fd = virNetMessageDupFD(msg, 0)) < 0)
goto cleanup;
if (virDomainOpenGraphics(dom,
args->idx,
fd,
args->flags) < 0)
goto cleanup;
rv = 0;
cleanup:
VIR_FORCE_CLOSE(fd);
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainGetInterfaceParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_interface_parameters_args *args,
remote_domain_get_interface_parameters_ret *ret)
{
virDomainPtr dom = NULL;
virTypedParameterPtr params = NULL;
const char *device = args->device;
int nparams = args->nparams;
unsigned int flags;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
flags = args->flags;
if (nparams > REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (nparams && VIR_ALLOC_N(params, nparams) < 0) {
virReportOOMError();
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) < 0)
goto cleanup;
/* In this case, we need to send back the number of parameters
* supported
*/
if (args->nparams == 0) {
ret->nparams = nparams;
goto success;
}
if (remoteSerializeTypedParameters(params, nparams,
&ret->params.params_val,
&ret->params.params_len,
flags) < 0)
goto cleanup;
success:
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virTypedParameterArrayClear(params, nparams);
VIR_FREE(params);
if (dom)
virDomainFree(dom);
return rv;
}
static int
remoteDispatchDomainGetCPUStats(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr hdr ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_cpu_stats_args *args,
remote_domain_get_cpu_stats_ret *ret)
{
virDomainPtr dom = NULL;
struct daemonClientPrivate *priv;
virTypedParameterPtr params = NULL;
int rv = -1;
int percpu_len = 0;
priv = virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (args->nparams > REMOTE_NODE_CPU_STATS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (args->ncpus > REMOTE_DOMAIN_GET_CPU_STATS_NCPUS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("ncpus too large"));
goto cleanup;
}
if (args->nparams > 0 &&
VIR_ALLOC_N(params, args->ncpus * args->nparams) < 0) {
virReportOOMError();
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
percpu_len = virDomainGetCPUStats(dom, params, args->nparams,
args->start_cpu, args->ncpus,
args->flags);
if (percpu_len < 0)
goto cleanup;
/* If nparams == 0, the function returns a single value */
if (args->nparams == 0)
goto success;
if (remoteSerializeTypedParameters(params, args->nparams * args->ncpus,
&ret->params.params_val,
&ret->params.params_len,
args->flags) < 0)
goto cleanup;
success:
rv = 0;
ret->nparams = percpu_len;
rpc: allow truncated return for virDomainGetCPUStats The RPC code assumed that the array returned by the driver would be fully populated; that is, ncpus on entry resulted in ncpus * return value on exit. However, while we don't support holes in the middle of ncpus, we do want to permit the case of ncpus on entry being longer than the array returned by the driver (that is, it should be safe for the caller to pass ncpus=128 on entry, and the driver will stop populating the array when it hits max_id). Additionally, a successful return implies that the caller will then use virTypedParamArrayClear on the entire array; for this to not free uninitialized memory, the driver must ensure that all skipped entries are explicitly zeroed (the RPC driver did this, but not the qemu driver). There are now three cases: server 0.9.10 and client 0.9.10 or newer: No impact - there were no hypervisor drivers that supported cpu stats server 0.9.11 or newer and client 0.9.10: if the client calls with ncpus beyond the max, then the rpc call will fail on the client side and disconnect the client, but the server is no worse for the wear server 0.9.11 or newer and client 0.9.11: the server can return a truncated array and the client will do just fine I reproduced the problem by using a host with 2 CPUs, and doing: virsh cpu-stats $dom --start 1 --count 2 * daemon/remote.c (remoteDispatchDomainGetCPUStats): Allow driver to omit tail of array. * src/remote/remote_driver.c (remoteDomainGetCPUStats): Accommodate driver that omits tail of array. * src/libvirt.c (virDomainGetCPUStats): Document this. * src/qemu/qemu_driver.c (qemuDomainGetPercpuStats): Clear all unpopulated entries.
2012-03-07 04:36:53 +00:00
if (args->nparams && !(args->flags & VIR_TYPED_PARAM_STRING_OKAY)) {
int i;
for (i = 0; i < percpu_len; i++) {
if (params[i].type == VIR_TYPED_PARAM_STRING)
ret->nparams--;
}
}
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virTypedParameterArrayClear(params, args->ncpus * args->nparams);
VIR_FREE(params);
if (dom)
virDomainFree(dom);
return rv;
}
static int remoteDispatchDomainGetDiskErrors(
virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_get_disk_errors_args *args,
remote_domain_get_disk_errors_ret *ret)
{
int rv = -1;
virDomainPtr dom = NULL;
virDomainDiskErrorPtr errors = NULL;
int len = 0;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if (args->maxerrors > REMOTE_DOMAIN_DISK_ERRORS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("maxerrors too large"));
goto cleanup;
}
if (args->maxerrors &&
VIR_ALLOC_N(errors, args->maxerrors) < 0) {
virReportOOMError();
goto cleanup;
}
if ((len = virDomainGetDiskErrors(dom, errors,
args->maxerrors,
args->flags)) < 0)
goto cleanup;
ret->nerrors = len;
if (errors &&
remoteSerializeDomainDiskErrors(errors, len,
&ret->errors.errors_val,
&ret->errors.errors_len) < 0)
goto cleanup;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
if (errors) {
int i;
for (i = 0; i < len; i++)
VIR_FREE(errors[i].disk);
}
VIR_FREE(errors);
return rv;
}
static int
remoteDispatchDomainListAllSnapshots(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_list_all_snapshots_args *args,
remote_domain_list_all_snapshots_ret *ret)
{
virDomainSnapshotPtr *snaps = NULL;
int nsnaps = 0;
int i;
int rv = -1;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
virDomainPtr dom = NULL;
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
goto cleanup;
if ((nsnaps = virDomainListAllSnapshots(dom,
args->need_results ? &snaps : NULL,
args->flags)) < 0)
goto cleanup;
if (snaps && nsnaps) {
if (VIR_ALLOC_N(ret->snapshots.snapshots_val, nsnaps) < 0) {
virReportOOMError();
goto cleanup;
}
ret->snapshots.snapshots_len = nsnaps;
for (i = 0; i < nsnaps; i++)
make_nonnull_domain_snapshot(ret->snapshots.snapshots_val + i,
snaps[i]);
} else {
ret->snapshots.snapshots_len = 0;
ret->snapshots.snapshots_val = NULL;
}
ret->ret = nsnaps;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
if (snaps) {
for (i = 0; i < nsnaps; i++)
virDomainSnapshotFree(snaps[i]);
VIR_FREE(snaps);
}
return rv;
}
static int
remoteDispatchDomainSnapshotListAllChildren(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_domain_snapshot_list_all_children_args *args,
remote_domain_snapshot_list_all_children_ret *ret)
{
virDomainSnapshotPtr *snaps = NULL;
int nsnaps = 0;
int i;
int rv = -1;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
virDomainPtr dom = NULL;
virDomainSnapshotPtr snapshot = NULL;
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(dom = get_nonnull_domain(priv->conn, args->snapshot.dom)))
goto cleanup;
if (!(snapshot = get_nonnull_domain_snapshot(dom, args->snapshot)))
goto cleanup;
if ((nsnaps = virDomainSnapshotListAllChildren(snapshot,
args->need_results ? &snaps : NULL,
args->flags)) < 0)
goto cleanup;
if (snaps && nsnaps) {
if (VIR_ALLOC_N(ret->snapshots.snapshots_val, nsnaps) < 0) {
virReportOOMError();
goto cleanup;
}
ret->snapshots.snapshots_len = nsnaps;
for (i = 0; i < nsnaps; i++)
make_nonnull_domain_snapshot(ret->snapshots.snapshots_val + i,
snaps[i]);
} else {
ret->snapshots.snapshots_len = 0;
ret->snapshots.snapshots_val = NULL;
}
ret->ret = nsnaps;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (snapshot)
virDomainSnapshotFree(snapshot);
if (dom)
virDomainFree(dom);
if (snaps) {
for (i = 0; i < nsnaps; i++)
virDomainSnapshotFree(snaps[i]);
VIR_FREE(snaps);
}
return rv;
}
static int
remoteDispatchConnectListAllStoragePools(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_connect_list_all_storage_pools_args *args,
remote_connect_list_all_storage_pools_ret *ret)
{
virStoragePoolPtr *pools = NULL;
int npools = 0;
int i;
int rv = -1;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if ((npools = virConnectListAllStoragePools(priv->conn,
args->need_results ? &pools : NULL,
args->flags)) < 0)
goto cleanup;
if (pools && npools) {
if (VIR_ALLOC_N(ret->pools.pools_val, npools) < 0) {
virReportOOMError();
goto cleanup;
}
ret->pools.pools_len = npools;
for (i = 0; i < npools; i++)
make_nonnull_storage_pool(ret->pools.pools_val + i, pools[i]);
} else {
ret->pools.pools_len = 0;
ret->pools.pools_val = NULL;
}
ret->ret = npools;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (pools) {
for (i = 0; i < npools; i++)
virStoragePoolFree(pools[i]);
VIR_FREE(pools);
}
return rv;
}
static int
remoteDispatchStoragePoolListAllVolumes(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_storage_pool_list_all_volumes_args *args,
remote_storage_pool_list_all_volumes_ret *ret)
{
virStorageVolPtr *vols = NULL;
virStoragePoolPtr pool = NULL;
int nvols = 0;
int i;
int rv = -1;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if (!(pool = get_nonnull_storage_pool(priv->conn, args->pool)))
goto cleanup;
if ((nvols = virStoragePoolListAllVolumes(pool,
args->need_results ? &vols : NULL,
args->flags)) < 0)
goto cleanup;
if (vols && nvols) {
if (VIR_ALLOC_N(ret->vols.vols_val, nvols) < 0) {
virReportOOMError();
goto cleanup;
}
ret->vols.vols_len = nvols;
for (i = 0; i < nvols; i++)
make_nonnull_storage_vol(ret->vols.vols_val + i, vols[i]);
} else {
ret->vols.vols_len = 0;
ret->vols.vols_val = NULL;
}
ret->ret = nvols;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (vols) {
for (i = 0; i < nvols; i++)
virStorageVolFree(vols[i]);
VIR_FREE(vols);
}
return rv;
}
static int
remoteDispatchConnectListAllNetworks(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_connect_list_all_networks_args *args,
remote_connect_list_all_networks_ret *ret)
{
virNetworkPtr *nets = NULL;
int nnets = 0;
int i;
int rv = -1;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if ((nnets = virConnectListAllNetworks(priv->conn,
args->need_results ? &nets : NULL,
args->flags)) < 0)
goto cleanup;
if (nets && nnets) {
if (VIR_ALLOC_N(ret->nets.nets_val, nnets) < 0) {
virReportOOMError();
goto cleanup;
}
ret->nets.nets_len = nnets;
for (i = 0; i < nnets; i++)
make_nonnull_network(ret->nets.nets_val + i, nets[i]);
} else {
ret->nets.nets_len = 0;
ret->nets.nets_val = NULL;
}
ret->ret = nnets;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (nets) {
for (i = 0; i < nnets; i++)
virNetworkFree(nets[i]);
VIR_FREE(nets);
}
return rv;
}
static int
remoteDispatchConnectListAllInterfaces(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_connect_list_all_interfaces_args *args,
remote_connect_list_all_interfaces_ret *ret)
{
virInterfacePtr *ifaces = NULL;
int nifaces = 0;
int i;
int rv = -1;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if ((nifaces = virConnectListAllInterfaces(priv->conn,
args->need_results ? &ifaces : NULL,
args->flags)) < 0)
goto cleanup;
if (ifaces && nifaces) {
if (VIR_ALLOC_N(ret->ifaces.ifaces_val, nifaces) < 0) {
virReportOOMError();
goto cleanup;
}
ret->ifaces.ifaces_len = nifaces;
for (i = 0; i < nifaces; i++)
make_nonnull_interface(ret->ifaces.ifaces_val + i, ifaces[i]);
} else {
ret->ifaces.ifaces_len = 0;
ret->ifaces.ifaces_val = NULL;
}
ret->ret = nifaces;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (ifaces) {
for (i = 0; i < nifaces; i++)
virInterfaceFree(ifaces[i]);
VIR_FREE(ifaces);
}
return rv;
}
static int
remoteDispatchConnectListAllNodeDevices(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_connect_list_all_node_devices_args *args,
remote_connect_list_all_node_devices_ret *ret)
{
virNodeDevicePtr *devices = NULL;
int ndevices = 0;
int i;
int rv = -1;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if ((ndevices = virConnectListAllNodeDevices(priv->conn,
args->need_results ? &devices : NULL,
args->flags)) < 0)
goto cleanup;
if (devices && ndevices) {
if (VIR_ALLOC_N(ret->devices.devices_val, ndevices) < 0) {
virReportOOMError();
goto cleanup;
}
ret->devices.devices_len = ndevices;
for (i = 0; i < ndevices; i++)
make_nonnull_node_device(ret->devices.devices_val + i, devices[i]);
} else {
ret->devices.devices_len = 0;
ret->devices.devices_val = NULL;
}
ret->ret = ndevices;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (devices) {
for (i = 0; i < ndevices; i++)
virNodeDeviceFree(devices[i]);
VIR_FREE(devices);
}
return rv;
}
static int
remoteDispatchConnectListAllNWFilters(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_connect_list_all_nwfilters_args *args,
remote_connect_list_all_nwfilters_ret *ret)
{
virNWFilterPtr *filters = NULL;
int nfilters = 0;
int i;
int rv = -1;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if ((nfilters = virConnectListAllNWFilters(priv->conn,
args->need_results ? &filters : NULL,
args->flags)) < 0)
goto cleanup;
if (filters && nfilters) {
if (VIR_ALLOC_N(ret->filters.filters_val, nfilters) < 0) {
virReportOOMError();
goto cleanup;
}
ret->filters.filters_len = nfilters;
for (i = 0; i < nfilters; i++)
make_nonnull_nwfilter(ret->filters.filters_val + i, filters[i]);
} else {
ret->filters.filters_len = 0;
ret->filters.filters_val = NULL;
}
ret->ret = nfilters;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (filters) {
for (i = 0; i < nfilters; i++)
virNWFilterFree(filters[i]);
VIR_FREE(filters);
}
return rv;
}
static int
remoteDispatchConnectListAllSecrets(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_connect_list_all_secrets_args *args,
remote_connect_list_all_secrets_ret *ret)
{
virSecretPtr *secrets = NULL;
int nsecrets = 0;
int i;
int rv = -1;
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
if ((nsecrets = virConnectListAllSecrets(priv->conn,
args->need_results ? &secrets : NULL,
args->flags)) < 0)
goto cleanup;
if (secrets && nsecrets) {
if (VIR_ALLOC_N(ret->secrets.secrets_val, nsecrets) < 0) {
virReportOOMError();
goto cleanup;
}
ret->secrets.secrets_len = nsecrets;
for (i = 0; i < nsecrets; i++)
make_nonnull_secret(ret->secrets.secrets_val + i, secrets[i]);
} else {
ret->secrets.secrets_len = 0;
ret->secrets.secrets_val = NULL;
}
ret->ret = nsecrets;
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
if (secrets) {
for (i = 0; i < nsecrets; i++)
virSecretFree(secrets[i]);
VIR_FREE(secrets);
}
return rv;
}
static int
remoteDispatchNodeGetMemoryParameters(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr,
remote_node_get_memory_parameters_args *args,
remote_node_get_memory_parameters_ret *ret)
{
virTypedParameterPtr params = NULL;
int nparams = args->nparams;
unsigned int flags;
int rv = -1;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
flags = args->flags;
if (nparams > REMOTE_NODE_MEMORY_PARAMETERS_MAX) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
goto cleanup;
}
if (nparams && VIR_ALLOC_N(params, nparams) < 0) {
virReportOOMError();
goto cleanup;
}
if (virNodeGetMemoryParameters(priv->conn, params, &nparams, flags) < 0)
goto cleanup;
/* In this case, we need to send back the number of parameters
* supported
*/
if (args->nparams == 0) {
ret->nparams = nparams;
goto success;
}
if (remoteSerializeTypedParameters(params, nparams,
&ret->params.params_val,
&ret->params.params_len,
args->flags) < 0)
goto cleanup;
success:
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virTypedParameterArrayClear(params, nparams);
VIR_FREE(params);
return rv;
}
/*----- Helpers. -----*/
/* get_nonnull_domain and get_nonnull_network turn an on-wire
* (name, uuid) pair into virDomainPtr or virNetworkPtr object.
* virDomainPtr or virNetworkPtr cannot be NULL.
*
* NB. If these return NULL then the caller must return an error.
*/
static virDomainPtr
get_nonnull_domain(virConnectPtr conn, remote_nonnull_domain domain)
{
virDomainPtr dom;
dom = virGetDomain(conn, domain.name, BAD_CAST domain.uuid);
/* Should we believe the domain.id sent by the client? Maybe
* this should be a check rather than an assignment? XXX
*/
if (dom) dom->id = domain.id;
return dom;
}
static virNetworkPtr
get_nonnull_network(virConnectPtr conn, remote_nonnull_network network)
{
return virGetNetwork(conn, network.name, BAD_CAST network.uuid);
}
static virInterfacePtr
get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface)
{
return virGetInterface(conn, iface.name, iface.mac);
}
static virStoragePoolPtr
get_nonnull_storage_pool(virConnectPtr conn, remote_nonnull_storage_pool pool)
{
return virGetStoragePool(conn, pool.name, BAD_CAST pool.uuid);
}
static virStorageVolPtr
get_nonnull_storage_vol(virConnectPtr conn, remote_nonnull_storage_vol vol)
{
virStorageVolPtr ret;
ret = virGetStorageVol(conn, vol.pool, vol.name, vol.key);
return ret;
}
static virSecretPtr
get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret secret)
{
return virGetSecret(conn, BAD_CAST secret.uuid, secret.usageType, secret.usageID);
}
static virNWFilterPtr
get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nwfilter nwfilter)
{
return virGetNWFilter(conn, nwfilter.name, BAD_CAST nwfilter.uuid);
}
static virDomainSnapshotPtr
get_nonnull_domain_snapshot(virDomainPtr dom, remote_nonnull_domain_snapshot snapshot)
{
return virGetDomainSnapshot(dom, snapshot.name);
}
/* Make remote_nonnull_domain and remote_nonnull_network. */
static void
make_nonnull_domain(remote_nonnull_domain *dom_dst, virDomainPtr dom_src)
{
dom_dst->id = dom_src->id;
dom_dst->name = strdup(dom_src->name);
memcpy(dom_dst->uuid, dom_src->uuid, VIR_UUID_BUFLEN);
}
static void
make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr net_src)
{
net_dst->name = strdup(net_src->name);
memcpy(net_dst->uuid, net_src->uuid, VIR_UUID_BUFLEN);
}
static void
make_nonnull_interface(remote_nonnull_interface *interface_dst,
virInterfacePtr interface_src)
{
interface_dst->name = strdup(interface_src->name);
interface_dst->mac = strdup(interface_src->mac);
}
static void
make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src)
{
pool_dst->name = strdup(pool_src->name);
memcpy(pool_dst->uuid, pool_src->uuid, VIR_UUID_BUFLEN);
}
static void
make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src)
{
vol_dst->pool = strdup(vol_src->pool);
vol_dst->name = strdup(vol_src->name);
vol_dst->key = strdup(vol_src->key);
}
static void
make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src)
{
dev_dst->name = strdup(dev_src->name);
}
static void
make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src)
{
memcpy(secret_dst->uuid, secret_src->uuid, VIR_UUID_BUFLEN);
secret_dst->usageType = secret_src->usageType;
secret_dst->usageID = strdup(secret_src->usageID);
}
static void
make_nonnull_nwfilter(remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src)
{
nwfilter_dst->name = strdup(nwfilter_src->name);
memcpy(nwfilter_dst->uuid, nwfilter_src->uuid, VIR_UUID_BUFLEN);
}
static void
make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src)
{
snapshot_dst->name = strdup(snapshot_src->name);
make_nonnull_domain(&snapshot_dst->dom, snapshot_src->domain);
}
static int
remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors,
int nerrors,
remote_domain_disk_error **ret_errors_val,
u_int *ret_errors_len)
{
remote_domain_disk_error *val = NULL;
int i = 0;
if (VIR_ALLOC_N(val, nerrors) < 0)
goto no_memory;
for (i = 0; i < nerrors; i++) {
if (!(val[i].disk = strdup(errors[i].disk)))
goto no_memory;
val[i].error = errors[i].error;
}
*ret_errors_len = nerrors;
*ret_errors_val = val;
return 0;
no_memory:
if (val) {
int j;
for (j = 0; j < i; j++)
VIR_FREE(val[j].disk);
VIR_FREE(val);
}
virReportOOMError();
return -1;
}