mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-23 21:34:54 +03:00
3212 lines
101 KiB
C
3212 lines
101 KiB
C
/*
|
|
* remote.c: handlers for RPC method calls
|
|
*
|
|
* Copyright (C) 2007-2011 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Author: Richard W.M. Jones <rjones@redhat.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/poll.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <stdlib.h>
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fnmatch.h>
|
|
#include <arpa/inet.h>
|
|
#include "virterror_internal.h"
|
|
|
|
#if HAVE_POLKIT0
|
|
# include <polkit/polkit.h>
|
|
# include <polkit-dbus/polkit-dbus.h>
|
|
#endif
|
|
|
|
#include "remote.h"
|
|
#include "dispatch.h"
|
|
|
|
#include "libvirt_internal.h"
|
|
#include "datatypes.h"
|
|
#include "memory.h"
|
|
#include "util.h"
|
|
#include "stream.h"
|
|
#include "uuid.h"
|
|
#include "network.h"
|
|
#include "libvirt/libvirt-qemu.h"
|
|
#include "command.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_REMOTE
|
|
|
|
#define virNetError(code, ...) \
|
|
virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \
|
|
__FUNCTION__, __LINE__, __VA_ARGS__)
|
|
|
|
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);
|
|
|
|
|
|
#include "remote_dispatch_prototypes.h"
|
|
#include "qemu_dispatch_prototypes.h"
|
|
|
|
static const dispatch_data const dispatch_table[] = {
|
|
#include "remote_dispatch_table.h"
|
|
};
|
|
|
|
static const dispatch_data const qemu_dispatch_table[] = {
|
|
#include "qemu_dispatch_table.h"
|
|
};
|
|
|
|
const dispatch_data const *remoteGetDispatchData(int proc)
|
|
{
|
|
if (proc >= ARRAY_CARDINALITY(dispatch_table) ||
|
|
dispatch_table[proc].fn == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return &(dispatch_table[proc]);
|
|
}
|
|
|
|
const dispatch_data const *qemuGetDispatchData(int proc)
|
|
{
|
|
if (proc >= ARRAY_CARDINALITY(qemu_dispatch_table) ||
|
|
qemu_dispatch_table[proc].fn == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return &(qemu_dispatch_table[proc]);
|
|
}
|
|
|
|
/* Prototypes */
|
|
static void
|
|
remoteDispatchDomainEventSend(struct qemud_client *client,
|
|
int procnr,
|
|
xdrproc_t proc,
|
|
void *data);
|
|
|
|
static int remoteRelayDomainEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
virDomainPtr dom,
|
|
int event,
|
|
int detail,
|
|
void *opaque)
|
|
{
|
|
struct qemud_client *client = opaque;
|
|
remote_domain_event_lifecycle_msg data;
|
|
|
|
if (!client)
|
|
return -1;
|
|
|
|
VIR_DEBUG("Relaying domain lifecycle event %d %d", event, detail);
|
|
|
|
virMutexLock(&client->lock);
|
|
|
|
/* build return data */
|
|
memset(&data, 0, sizeof data);
|
|
make_nonnull_domain(&data.dom, dom);
|
|
data.event = event;
|
|
data.detail = detail;
|
|
|
|
remoteDispatchDomainEventSend(client,
|
|
REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE,
|
|
(xdrproc_t)xdr_remote_domain_event_lifecycle_msg, &data);
|
|
|
|
virMutexUnlock(&client->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int remoteRelayDomainEventReboot(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
virDomainPtr dom,
|
|
void *opaque)
|
|
{
|
|
struct qemud_client *client = opaque;
|
|
remote_domain_event_reboot_msg data;
|
|
|
|
if (!client)
|
|
return -1;
|
|
|
|
VIR_DEBUG("Relaying domain reboot event %s %d", dom->name, dom->id);
|
|
|
|
virMutexLock(&client->lock);
|
|
|
|
/* build return data */
|
|
memset(&data, 0, sizeof data);
|
|
make_nonnull_domain(&data.dom, dom);
|
|
|
|
remoteDispatchDomainEventSend(client,
|
|
REMOTE_PROC_DOMAIN_EVENT_REBOOT,
|
|
(xdrproc_t)xdr_remote_domain_event_reboot_msg, &data);
|
|
|
|
virMutexUnlock(&client->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int remoteRelayDomainEventRTCChange(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
virDomainPtr dom,
|
|
long long offset,
|
|
void *opaque)
|
|
{
|
|
struct qemud_client *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);
|
|
|
|
virMutexLock(&client->lock);
|
|
|
|
/* build return data */
|
|
memset(&data, 0, sizeof data);
|
|
make_nonnull_domain(&data.dom, dom);
|
|
data.offset = offset;
|
|
|
|
remoteDispatchDomainEventSend(client,
|
|
REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE,
|
|
(xdrproc_t)xdr_remote_domain_event_rtc_change_msg, &data);
|
|
|
|
virMutexUnlock(&client->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int remoteRelayDomainEventWatchdog(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
virDomainPtr dom,
|
|
int action,
|
|
void *opaque)
|
|
{
|
|
struct qemud_client *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);
|
|
|
|
virMutexLock(&client->lock);
|
|
|
|
/* build return data */
|
|
memset(&data, 0, sizeof data);
|
|
make_nonnull_domain(&data.dom, dom);
|
|
data.action = action;
|
|
|
|
remoteDispatchDomainEventSend(client,
|
|
REMOTE_PROC_DOMAIN_EVENT_WATCHDOG,
|
|
(xdrproc_t)xdr_remote_domain_event_watchdog_msg, &data);
|
|
|
|
virMutexUnlock(&client->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int remoteRelayDomainEventIOError(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
virDomainPtr dom,
|
|
const char *srcPath,
|
|
const char *devAlias,
|
|
int action,
|
|
void *opaque)
|
|
{
|
|
struct qemud_client *client = opaque;
|
|
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);
|
|
|
|
virMutexLock(&client->lock);
|
|
|
|
/* build return data */
|
|
memset(&data, 0, sizeof data);
|
|
make_nonnull_domain(&data.dom, dom);
|
|
data.srcPath = (char*)srcPath;
|
|
data.devAlias = (char*)devAlias;
|
|
data.action = action;
|
|
|
|
remoteDispatchDomainEventSend(client,
|
|
REMOTE_PROC_DOMAIN_EVENT_IO_ERROR,
|
|
(xdrproc_t)xdr_remote_domain_event_io_error_msg, &data);
|
|
|
|
virMutexUnlock(&client->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int remoteRelayDomainEventIOErrorReason(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
virDomainPtr dom,
|
|
const char *srcPath,
|
|
const char *devAlias,
|
|
int action,
|
|
const char *reason,
|
|
void *opaque)
|
|
{
|
|
struct qemud_client *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);
|
|
|
|
virMutexLock(&client->lock);
|
|
|
|
/* build return data */
|
|
memset(&data, 0, sizeof data);
|
|
make_nonnull_domain(&data.dom, dom);
|
|
data.srcPath = (char*)srcPath;
|
|
data.devAlias = (char*)devAlias;
|
|
data.action = action;
|
|
data.reason = (char*)reason;
|
|
|
|
remoteDispatchDomainEventSend(client,
|
|
REMOTE_PROC_DOMAIN_EVENT_IO_ERROR_REASON,
|
|
(xdrproc_t)xdr_remote_domain_event_io_error_reason_msg, &data);
|
|
|
|
virMutexUnlock(&client->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
virDomainPtr dom,
|
|
int phase,
|
|
virDomainEventGraphicsAddressPtr local,
|
|
virDomainEventGraphicsAddressPtr remote,
|
|
const char *authScheme,
|
|
virDomainEventGraphicsSubjectPtr subject,
|
|
void *opaque)
|
|
{
|
|
struct qemud_client *client = opaque;
|
|
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);
|
|
|
|
VIR_DEBUG("Subject %d", subject->nidentity);
|
|
for (i = 0 ; i < subject->nidentity ; i++) {
|
|
VIR_DEBUG(" %s=%s", subject->identities[i].type, subject->identities[i].name);
|
|
}
|
|
|
|
virMutexLock(&client->lock);
|
|
|
|
/* build return data */
|
|
memset(&data, 0, sizeof data);
|
|
make_nonnull_domain(&data.dom, dom);
|
|
data.phase = phase;
|
|
data.authScheme = (char*)authScheme;
|
|
|
|
data.local.family = local->family;
|
|
data.local.node = (char *)local->node;
|
|
data.local.service = (char *)local->service;
|
|
|
|
data.remote.family = remote->family;
|
|
data.remote.node = (char*)remote->node;
|
|
data.remote.service = (char*)remote->service;
|
|
|
|
data.subject.subject_len = subject->nidentity;
|
|
if (VIR_ALLOC_N(data.subject.subject_val, data.subject.subject_len) < 0) {
|
|
VIR_WARN("cannot allocate memory for graphics event subject");
|
|
return -1;
|
|
}
|
|
for (i = 0 ; i < data.subject.subject_len ; i++) {
|
|
data.subject.subject_val[i].type = (char*)subject->identities[i].type;
|
|
data.subject.subject_val[i].name = (char*)subject->identities[i].name;
|
|
}
|
|
|
|
remoteDispatchDomainEventSend(client,
|
|
REMOTE_PROC_DOMAIN_EVENT_GRAPHICS,
|
|
(xdrproc_t)xdr_remote_domain_event_graphics_msg, &data);
|
|
|
|
VIR_FREE(data.subject.subject_val);
|
|
|
|
virMutexUnlock(&client->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
|
|
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
|
|
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot),
|
|
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventRTCChange),
|
|
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventWatchdog),
|
|
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOError),
|
|
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics),
|
|
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason),
|
|
};
|
|
|
|
verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
|
|
|
|
/*----- Functions. -----*/
|
|
|
|
static int
|
|
remoteDispatchOpen(struct qemud_server *server,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
struct remote_open_args *args, void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
const char *name;
|
|
int flags;
|
|
int rv = -1;
|
|
|
|
virMutexLock(&server->lock);
|
|
virMutexLock(&client->lock);
|
|
virMutexUnlock(&server->lock);
|
|
|
|
if (conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection already open"));
|
|
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 (client->readonly) flags |= VIR_CONNECT_RO;
|
|
|
|
client->conn =
|
|
flags & VIR_CONNECT_RO
|
|
? virConnectOpenReadOnly(name)
|
|
: virConnectOpen(name);
|
|
|
|
if (client->conn == NULL)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
virMutexUnlock(&client->lock);
|
|
return rv;
|
|
}
|
|
|
|
|
|
static int
|
|
remoteDispatchClose(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr ATTRIBUTE_UNUSED,
|
|
void *args ATTRIBUTE_UNUSED, void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
virMutexLock(&server->lock);
|
|
virMutexLock(&client->lock);
|
|
virMutexUnlock(&server->lock);
|
|
|
|
client->closing = 1;
|
|
|
|
virMutexUnlock(&client->lock);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchGetType(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
void *args ATTRIBUTE_UNUSED, remote_get_type_ret *ret)
|
|
{
|
|
const char *type;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(type = virConnectGetType(conn)))
|
|
goto cleanup;
|
|
|
|
/* We have to strdup because remoteDispatchClientRequest will
|
|
* free this string after it's been serialised.
|
|
*/
|
|
if (!(ret->type = strdup(type))) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainGetSchedulerType(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *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;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (!(type = virDomainGetSchedulerType(dom, &nparams)))
|
|
goto cleanup;
|
|
|
|
ret->type = type;
|
|
ret->nparams = nparams;
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainGetSchedulerParameters(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
remote_domain_get_scheduler_parameters_args *args,
|
|
remote_domain_get_scheduler_parameters_ret *ret)
|
|
{
|
|
virDomainPtr dom = NULL;
|
|
virSchedParameterPtr params = NULL;
|
|
int i;
|
|
int nparams = args->nparams;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (nparams > REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
|
|
goto cleanup;
|
|
}
|
|
if (VIR_ALLOC_N(params, nparams) < 0)
|
|
goto no_memory;
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetSchedulerParameters(dom, params, &nparams) < 0)
|
|
goto cleanup;
|
|
|
|
/* Serialise the scheduler 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.type = params[i].type;
|
|
switch (params[i].type) {
|
|
case VIR_DOMAIN_SCHED_FIELD_INT:
|
|
ret->params.params_val[i].value.remote_sched_param_value_u.i = params[i].value.i; break;
|
|
case VIR_DOMAIN_SCHED_FIELD_UINT:
|
|
ret->params.params_val[i].value.remote_sched_param_value_u.ui = params[i].value.ui; break;
|
|
case VIR_DOMAIN_SCHED_FIELD_LLONG:
|
|
ret->params.params_val[i].value.remote_sched_param_value_u.l = params[i].value.l; break;
|
|
case VIR_DOMAIN_SCHED_FIELD_ULLONG:
|
|
ret->params.params_val[i].value.remote_sched_param_value_u.ul = params[i].value.ul; break;
|
|
case VIR_DOMAIN_SCHED_FIELD_DOUBLE:
|
|
ret->params.params_val[i].value.remote_sched_param_value_u.d = params[i].value.d; break;
|
|
case VIR_DOMAIN_SCHED_FIELD_BOOLEAN:
|
|
ret->params.params_val[i].value.remote_sched_param_value_u.b = params[i].value.b; break;
|
|
default:
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("unknown type"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0) {
|
|
remoteDispatchError(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);
|
|
}
|
|
}
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
VIR_FREE(params);
|
|
return rv;
|
|
|
|
no_memory:
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainSetSchedulerParameters(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
remote_domain_set_scheduler_parameters_args *args,
|
|
void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
virDomainPtr dom = NULL;
|
|
virSchedParameterPtr params = NULL;
|
|
int i, nparams;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
nparams = args->params.params_len;
|
|
|
|
if (nparams > REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
|
|
goto cleanup;
|
|
}
|
|
if (VIR_ALLOC_N(params, nparams) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Deserialise parameters. */
|
|
for (i = 0; i < nparams; ++i) {
|
|
if (virStrcpyStatic(params[i].field, args->params.params_val[i].field) == NULL) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, _("Field %s too big for destination"),
|
|
args->params.params_val[i].field);
|
|
goto cleanup;
|
|
}
|
|
params[i].type = args->params.params_val[i].value.type;
|
|
switch (params[i].type) {
|
|
case VIR_DOMAIN_SCHED_FIELD_INT:
|
|
params[i].value.i = args->params.params_val[i].value.remote_sched_param_value_u.i; break;
|
|
case VIR_DOMAIN_SCHED_FIELD_UINT:
|
|
params[i].value.ui = args->params.params_val[i].value.remote_sched_param_value_u.ui; break;
|
|
case VIR_DOMAIN_SCHED_FIELD_LLONG:
|
|
params[i].value.l = args->params.params_val[i].value.remote_sched_param_value_u.l; break;
|
|
case VIR_DOMAIN_SCHED_FIELD_ULLONG:
|
|
params[i].value.ul = args->params.params_val[i].value.remote_sched_param_value_u.ul; break;
|
|
case VIR_DOMAIN_SCHED_FIELD_DOUBLE:
|
|
params[i].value.d = args->params.params_val[i].value.remote_sched_param_value_u.d; break;
|
|
case VIR_DOMAIN_SCHED_FIELD_BOOLEAN:
|
|
params[i].value.b = args->params.params_val[i].value.remote_sched_param_value_u.b; break;
|
|
}
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainSetSchedulerParameters(dom, params, nparams) < 0)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
VIR_FREE(params);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainMemoryStats(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *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;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (args->maxStats > REMOTE_DOMAIN_MEMORY_STATS_MAX) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("maxStats > REMOTE_DOMAIN_MEMORY_STATS_MAX"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
/* Allocate stats array for making dispatch call */
|
|
if (VIR_ALLOC_N(stats, args->maxStats) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
nr_stats = virDomainMemoryStats(dom, stats, args->maxStats, 0);
|
|
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)
|
|
remoteDispatchError(rerr);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
VIR_FREE(stats);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainBlockPeek(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *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;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(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) {
|
|
virNetError(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) {
|
|
remoteDispatchError(rerr);
|
|
VIR_FREE(ret->buffer.buffer_val);
|
|
}
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainMemoryPeek(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *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;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
offset = args->offset;
|
|
size = args->size;
|
|
flags = args->flags;
|
|
|
|
if (size > REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX) {
|
|
virNetError(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;
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0) {
|
|
remoteDispatchError(rerr);
|
|
VIR_FREE(ret->buffer.buffer_val);
|
|
}
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainCreateWithFlags(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
remote_domain_create_with_flags_args *args,
|
|
remote_domain_create_with_flags_ret *ret)
|
|
{
|
|
int rv = -1;
|
|
virDomainPtr dom = NULL;
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainCreateWithFlags(dom, args->flags) < 0)
|
|
goto cleanup;
|
|
|
|
make_nonnull_domain(&ret->dom, dom);
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainGetSecurityLabel(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
remote_domain_get_security_label_args *args,
|
|
remote_domain_get_security_label_ret *ret)
|
|
{
|
|
virDomainPtr dom = NULL;
|
|
virSecurityLabelPtr seclabel = NULL;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(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)
|
|
remoteDispatchError(rerr);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
VIR_FREE(seclabel);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchNodeGetSecurityModel(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
void *args ATTRIBUTE_UNUSED,
|
|
remote_node_get_security_model_ret *ret)
|
|
{
|
|
virSecurityModel secmodel;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
memset(&secmodel, 0, sizeof secmodel);
|
|
if (virNodeGetSecurityModel(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)
|
|
remoteDispatchError(rerr);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainGetVcpus(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *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;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (args->maxinfo > REMOTE_VCPUINFO_MAX) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("maxinfo > REMOTE_VCPUINFO_MAX"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (args->maxinfo * args->maplen > REMOTE_CPUMAPS_MAX) {
|
|
virNetError(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) {
|
|
remoteDispatchError(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(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *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;
|
|
|
|
if (!conn) {
|
|
virNetError(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(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)
|
|
remoteDispatchError(rerr);
|
|
VIR_FREE(uri_out);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainMigratePrepare2(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *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;
|
|
|
|
if (!conn) {
|
|
virNetError(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(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)
|
|
remoteDispatchError(rerr);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainMigratePrepareTunnel(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr,
|
|
remote_error *rerr,
|
|
remote_domain_migrate_prepare_tunnel_args *args,
|
|
void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
char *dname;
|
|
struct qemud_client_stream *stream = NULL;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
dname = args->dname == NULL ? NULL : *args->dname;
|
|
|
|
if (!(stream = remoteCreateClientStream(conn, hdr))) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virDomainMigratePrepareTunnel(conn, stream->st,
|
|
args->flags, dname, args->resource,
|
|
args->dom_xml) < 0)
|
|
goto cleanup;
|
|
|
|
if (remoteAddClientStream(client, stream, 0) < 0)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0) {
|
|
remoteDispatchError(rerr);
|
|
if (stream) {
|
|
virStreamAbort(stream->st);
|
|
remoteFreeClientStream(client, stream);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainPinVcpu(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
remote_domain_pin_vcpu_args *args,
|
|
void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
virDomainPtr dom = NULL;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (args->cpumap.cpumap_len > REMOTE_CPUMAP_MAX) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("cpumap_len > REMOTE_CPUMAP_MAX"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virDomainPinVcpu(dom, args->vcpu,
|
|
(unsigned char *) args->cpumap.cpumap_val,
|
|
args->cpumap.cpumap_len) < 0)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainSetMemoryParameters(struct qemud_server *server
|
|
ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client
|
|
ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *
|
|
hdr ATTRIBUTE_UNUSED,
|
|
remote_error * rerr,
|
|
remote_domain_set_memory_parameters_args
|
|
* args, void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
virDomainPtr dom = NULL;
|
|
virMemoryParameterPtr params = NULL;
|
|
int i, nparams;
|
|
unsigned int flags;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
nparams = args->params.params_len;
|
|
flags = args->flags;
|
|
|
|
if (nparams > REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
|
|
goto cleanup;
|
|
}
|
|
if (VIR_ALLOC_N(params, nparams) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Deserialise parameters. */
|
|
for (i = 0; i < nparams; ++i) {
|
|
if (virStrcpyStatic
|
|
(params[i].field, args->params.params_val[i].field) == NULL) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Field %s too big for destination"),
|
|
args->params.params_val[i].field);
|
|
goto cleanup;
|
|
}
|
|
params[i].type = args->params.params_val[i].value.type;
|
|
switch (params[i].type) {
|
|
case VIR_DOMAIN_MEMORY_PARAM_INT:
|
|
params[i].value.i =
|
|
args->params.params_val[i].value.
|
|
remote_memory_param_value_u.i;
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_PARAM_UINT:
|
|
params[i].value.ui =
|
|
args->params.params_val[i].value.
|
|
remote_memory_param_value_u.ui;
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_PARAM_LLONG:
|
|
params[i].value.l =
|
|
args->params.params_val[i].value.
|
|
remote_memory_param_value_u.l;
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_PARAM_ULLONG:
|
|
params[i].value.ul =
|
|
args->params.params_val[i].value.
|
|
remote_memory_param_value_u.ul;
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_PARAM_DOUBLE:
|
|
params[i].value.d =
|
|
args->params.params_val[i].value.
|
|
remote_memory_param_value_u.d;
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_PARAM_BOOLEAN:
|
|
params[i].value.b =
|
|
args->params.params_val[i].value.
|
|
remote_memory_param_value_u.b;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainSetMemoryParameters(dom, params, nparams, flags) < 0)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
VIR_FREE(params);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainGetMemoryParameters(struct qemud_server *server
|
|
ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client
|
|
ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *
|
|
hdr ATTRIBUTE_UNUSED,
|
|
remote_error * rerr,
|
|
remote_domain_get_memory_parameters_args
|
|
* args,
|
|
remote_domain_get_memory_parameters_ret
|
|
* ret)
|
|
{
|
|
virDomainPtr dom = NULL;
|
|
virMemoryParameterPtr params = NULL;
|
|
int i;
|
|
int nparams = args->nparams;
|
|
unsigned int flags;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
flags = args->flags;
|
|
|
|
if (nparams > REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
|
|
goto cleanup;
|
|
}
|
|
if (VIR_ALLOC_N(params, nparams) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(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;
|
|
}
|
|
|
|
/* 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.type = params[i].type;
|
|
switch (params[i].type) {
|
|
case VIR_DOMAIN_MEMORY_PARAM_INT:
|
|
ret->params.params_val[i].
|
|
value.remote_memory_param_value_u.i =
|
|
params[i].value.i;
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_PARAM_UINT:
|
|
ret->params.params_val[i].
|
|
value.remote_memory_param_value_u.ui =
|
|
params[i].value.ui;
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_PARAM_LLONG:
|
|
ret->params.params_val[i].
|
|
value.remote_memory_param_value_u.l =
|
|
params[i].value.l;
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_PARAM_ULLONG:
|
|
ret->params.params_val[i].
|
|
value.remote_memory_param_value_u.ul =
|
|
params[i].value.ul;
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_PARAM_DOUBLE:
|
|
ret->params.params_val[i].
|
|
value.remote_memory_param_value_u.d =
|
|
params[i].value.d;
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_PARAM_BOOLEAN:
|
|
ret->params.params_val[i].
|
|
value.remote_memory_param_value_u.b =
|
|
params[i].value.b;
|
|
break;
|
|
default:
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("unknown type"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
success:
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0) {
|
|
remoteDispatchError(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);
|
|
}
|
|
}
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
VIR_FREE(params);
|
|
return rv;
|
|
|
|
no_memory:
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainSetBlkioParameters(struct qemud_server *server
|
|
ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client
|
|
ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *
|
|
hdr ATTRIBUTE_UNUSED,
|
|
remote_error * rerr,
|
|
remote_domain_set_blkio_parameters_args
|
|
* args, void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
virDomainPtr dom = NULL;
|
|
virBlkioParameterPtr params = NULL;
|
|
int i, nparams;
|
|
unsigned int flags;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
nparams = args->params.params_len;
|
|
flags = args->flags;
|
|
|
|
if (nparams > REMOTE_DOMAIN_BLKIO_PARAMETERS_MAX) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
|
|
goto cleanup;
|
|
}
|
|
if (VIR_ALLOC_N(params, nparams) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Deserialise parameters. */
|
|
for (i = 0; i < nparams; ++i) {
|
|
if (virStrcpyStatic
|
|
(params[i].field, args->params.params_val[i].field) == NULL) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Field %s too big for destination"),
|
|
args->params.params_val[i].field);
|
|
goto cleanup;
|
|
}
|
|
params[i].type = args->params.params_val[i].value.type;
|
|
switch (params[i].type) {
|
|
case VIR_DOMAIN_BLKIO_PARAM_INT:
|
|
params[i].value.i =
|
|
args->params.params_val[i].value.
|
|
remote_blkio_param_value_u.i;
|
|
break;
|
|
case VIR_DOMAIN_BLKIO_PARAM_UINT:
|
|
params[i].value.ui =
|
|
args->params.params_val[i].value.
|
|
remote_blkio_param_value_u.ui;
|
|
break;
|
|
case VIR_DOMAIN_BLKIO_PARAM_LLONG:
|
|
params[i].value.l =
|
|
args->params.params_val[i].value.
|
|
remote_blkio_param_value_u.l;
|
|
break;
|
|
case VIR_DOMAIN_BLKIO_PARAM_ULLONG:
|
|
params[i].value.ul =
|
|
args->params.params_val[i].value.
|
|
remote_blkio_param_value_u.ul;
|
|
break;
|
|
case VIR_DOMAIN_BLKIO_PARAM_DOUBLE:
|
|
params[i].value.d =
|
|
args->params.params_val[i].value.
|
|
remote_blkio_param_value_u.d;
|
|
break;
|
|
case VIR_DOMAIN_BLKIO_PARAM_BOOLEAN:
|
|
params[i].value.b =
|
|
args->params.params_val[i].value.
|
|
remote_blkio_param_value_u.b;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
VIR_FREE(params);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainGetBlkioParameters(struct qemud_server *server
|
|
ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client
|
|
ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *
|
|
hdr ATTRIBUTE_UNUSED,
|
|
remote_error * rerr,
|
|
remote_domain_get_blkio_parameters_args
|
|
* args,
|
|
remote_domain_get_blkio_parameters_ret
|
|
* ret)
|
|
{
|
|
virDomainPtr dom = NULL;
|
|
virBlkioParameterPtr params = NULL;
|
|
int i;
|
|
int nparams = args->nparams;
|
|
unsigned int flags;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
flags = args->flags;
|
|
|
|
if (nparams > REMOTE_DOMAIN_BLKIO_PARAMETERS_MAX) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
|
|
goto cleanup;
|
|
}
|
|
if (VIR_ALLOC_N(params, nparams) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(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;
|
|
}
|
|
|
|
/* Serialise the blkio 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.type = params[i].type;
|
|
switch (params[i].type) {
|
|
case VIR_DOMAIN_BLKIO_PARAM_INT:
|
|
ret->params.params_val[i].
|
|
value.remote_blkio_param_value_u.i =
|
|
params[i].value.i;
|
|
break;
|
|
case VIR_DOMAIN_BLKIO_PARAM_UINT:
|
|
ret->params.params_val[i].
|
|
value.remote_blkio_param_value_u.ui =
|
|
params[i].value.ui;
|
|
break;
|
|
case VIR_DOMAIN_BLKIO_PARAM_LLONG:
|
|
ret->params.params_val[i].
|
|
value.remote_blkio_param_value_u.l =
|
|
params[i].value.l;
|
|
break;
|
|
case VIR_DOMAIN_BLKIO_PARAM_ULLONG:
|
|
ret->params.params_val[i].
|
|
value.remote_blkio_param_value_u.ul =
|
|
params[i].value.ul;
|
|
break;
|
|
case VIR_DOMAIN_BLKIO_PARAM_DOUBLE:
|
|
ret->params.params_val[i].
|
|
value.remote_blkio_param_value_u.d =
|
|
params[i].value.d;
|
|
break;
|
|
case VIR_DOMAIN_BLKIO_PARAM_BOOLEAN:
|
|
ret->params.params_val[i].
|
|
value.remote_blkio_param_value_u.b =
|
|
params[i].value.b;
|
|
break;
|
|
default:
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("unknown type"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
success:
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0) {
|
|
remoteDispatchError(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);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
|
|
no_memory:
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainScreenshot (struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr,
|
|
remote_error *rerr,
|
|
remote_domain_screenshot_args *args,
|
|
remote_domain_screenshot_ret *ret)
|
|
{
|
|
int rv = -1;
|
|
struct qemud_client_stream *stream = NULL;
|
|
virDomainPtr dom;
|
|
char *mime, **mime_p;
|
|
|
|
ret->mime = NULL;
|
|
|
|
dom = get_nonnull_domain (conn, args->dom);
|
|
if (dom == NULL)
|
|
goto err;
|
|
|
|
stream = remoteCreateClientStream(conn, hdr);
|
|
if (!stream)
|
|
goto err;
|
|
|
|
mime = virDomainScreenshot(dom, stream->st, args->screen, args->flags);
|
|
if (!mime)
|
|
goto err;
|
|
|
|
if (remoteAddClientStream(client, stream, 1) < 0) {
|
|
virStreamAbort(stream->st);
|
|
goto err;
|
|
}
|
|
|
|
if (VIR_ALLOC(mime_p) < 0) {
|
|
remoteDispatchOOMError(rerr);
|
|
goto cleanup;
|
|
}
|
|
|
|
*mime_p = strdup(mime);
|
|
if (*mime_p == NULL) {
|
|
remoteDispatchOOMError(rerr);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret->mime = mime_p;
|
|
rv = 0;
|
|
|
|
err:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
cleanup:
|
|
virDomainFree(dom);
|
|
if (stream && rv != 0)
|
|
remoteFreeClientStream(client, stream);
|
|
return rv;
|
|
}
|
|
|
|
/*-------------------------------------------------------------*/
|
|
|
|
static int
|
|
remoteDispatchAuthList(struct qemud_server *server,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
void *args ATTRIBUTE_UNUSED,
|
|
remote_auth_list_ret *ret)
|
|
{
|
|
int rv = -1;
|
|
|
|
ret->types.types_len = 1;
|
|
if (VIR_ALLOC_N(ret->types.types_val, ret->types.types_len) < 0) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
virMutexLock(&server->lock);
|
|
virMutexLock(&client->lock);
|
|
virMutexUnlock(&server->lock);
|
|
ret->types.types_val[0] = client->auth;
|
|
virMutexUnlock(&client->lock);
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
return rv;
|
|
}
|
|
|
|
|
|
#if HAVE_SASL
|
|
/*
|
|
* Initializes the SASL session in prepare for authentication
|
|
* and gives the client a list of allowed mechanisms to choose
|
|
*
|
|
* XXX callbacks for stuff like password verification ?
|
|
*/
|
|
static int
|
|
remoteDispatchAuthSaslInit(struct qemud_server *server,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
void *args ATTRIBUTE_UNUSED,
|
|
remote_auth_sasl_init_ret *ret)
|
|
{
|
|
const char *mechlist = NULL;
|
|
sasl_security_properties_t secprops;
|
|
int err;
|
|
virSocketAddr sa;
|
|
char *localAddr, *remoteAddr;
|
|
|
|
virMutexLock(&server->lock);
|
|
virMutexLock(&client->lock);
|
|
virMutexUnlock(&server->lock);
|
|
|
|
VIR_DEBUG("Initialize SASL auth %d", client->fd);
|
|
if (client->auth != REMOTE_AUTH_SASL ||
|
|
client->saslconn != NULL) {
|
|
VIR_ERROR(_("client tried invalid SASL init request"));
|
|
goto authfail;
|
|
}
|
|
|
|
/* Get local address in form IPADDR:PORT */
|
|
sa.len = sizeof(sa.data.stor);
|
|
if (getsockname(client->fd, &sa.data.sa, &sa.len) < 0) {
|
|
char ebuf[1024];
|
|
virNetError(VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to get sock address: %s"),
|
|
virStrerror(errno, ebuf, sizeof ebuf));
|
|
goto error;
|
|
}
|
|
if ((localAddr = virSocketFormatAddrFull(&sa, true, ";")) == NULL)
|
|
goto error;
|
|
|
|
/* Get remote address in form IPADDR:PORT */
|
|
sa.len = sizeof(sa.data.stor);
|
|
if (getpeername(client->fd, &sa.data.sa, &sa.len) < 0) {
|
|
char ebuf[1024];
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, _("failed to get peer address: %s"),
|
|
virStrerror(errno, ebuf, sizeof ebuf));
|
|
VIR_FREE(localAddr);
|
|
goto error;
|
|
}
|
|
if ((remoteAddr = virSocketFormatAddrFull(&sa, true, ";")) == NULL) {
|
|
VIR_FREE(localAddr);
|
|
goto error;
|
|
}
|
|
|
|
err = sasl_server_new("libvirt",
|
|
NULL, /* FQDN - just delegates to gethostname */
|
|
NULL, /* User realm */
|
|
localAddr,
|
|
remoteAddr,
|
|
NULL, /* XXX Callbacks */
|
|
SASL_SUCCESS_DATA,
|
|
&client->saslconn);
|
|
VIR_FREE(localAddr);
|
|
VIR_FREE(remoteAddr);
|
|
if (err != SASL_OK) {
|
|
VIR_ERROR(_("sasl context setup failed %d (%s)"),
|
|
err, sasl_errstring(err, NULL, NULL));
|
|
client->saslconn = NULL;
|
|
goto authfail;
|
|
}
|
|
|
|
/* Inform SASL that we've got an external SSF layer from TLS */
|
|
if (client->type == QEMUD_SOCK_TYPE_TLS) {
|
|
gnutls_cipher_algorithm_t cipher;
|
|
sasl_ssf_t ssf;
|
|
|
|
cipher = gnutls_cipher_get(client->tlssession);
|
|
if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
|
|
VIR_ERROR(_("cannot get TLS cipher size"));
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
goto authfail;
|
|
}
|
|
ssf *= 8; /* tls key size is bytes, sasl wants bits */
|
|
|
|
err = sasl_setprop(client->saslconn, SASL_SSF_EXTERNAL, &ssf);
|
|
if (err != SASL_OK) {
|
|
VIR_ERROR(_("cannot set SASL external SSF %d (%s)"),
|
|
err, sasl_errstring(err, NULL, NULL));
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
goto authfail;
|
|
}
|
|
}
|
|
|
|
memset(&secprops, 0, sizeof secprops);
|
|
if (client->type == QEMUD_SOCK_TYPE_TLS ||
|
|
client->type == QEMUD_SOCK_TYPE_UNIX) {
|
|
/* If we've got TLS or UNIX domain sock, we don't care about SSF */
|
|
secprops.min_ssf = 0;
|
|
secprops.max_ssf = 0;
|
|
secprops.maxbufsize = 8192;
|
|
secprops.security_flags = 0;
|
|
} else {
|
|
/* Plain TCP, better get an SSF layer */
|
|
secprops.min_ssf = 56; /* Good enough to require kerberos */
|
|
secprops.max_ssf = 100000; /* Arbitrary big number */
|
|
secprops.maxbufsize = 8192;
|
|
/* Forbid any anonymous or trivially crackable auth */
|
|
secprops.security_flags =
|
|
SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
|
|
}
|
|
|
|
err = sasl_setprop(client->saslconn, SASL_SEC_PROPS, &secprops);
|
|
if (err != SASL_OK) {
|
|
VIR_ERROR(_("cannot set SASL security props %d (%s)"),
|
|
err, sasl_errstring(err, NULL, NULL));
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
goto authfail;
|
|
}
|
|
|
|
err = sasl_listmech(client->saslconn,
|
|
NULL, /* Don't need to set user */
|
|
"", /* Prefix */
|
|
",", /* Separator */
|
|
"", /* Suffix */
|
|
&mechlist,
|
|
NULL,
|
|
NULL);
|
|
if (err != SASL_OK) {
|
|
VIR_ERROR(_("cannot list SASL mechanisms %d (%s)"),
|
|
err, sasl_errdetail(client->saslconn));
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
goto authfail;
|
|
}
|
|
VIR_DEBUG("Available mechanisms for client: '%s'", mechlist);
|
|
ret->mechlist = strdup(mechlist);
|
|
if (!ret->mechlist) {
|
|
VIR_ERROR(_("cannot allocate mechlist"));
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
goto authfail;
|
|
}
|
|
|
|
virMutexUnlock(&client->lock);
|
|
return 0;
|
|
|
|
authfail:
|
|
remoteDispatchAuthError(rerr);
|
|
error:
|
|
PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL);
|
|
virMutexUnlock(&client->lock);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* We asked for an SSF layer, so sanity check that we actually
|
|
* got what we asked for
|
|
* Returns 0 if ok, -1 on error, -2 if rejected
|
|
*/
|
|
static int
|
|
remoteSASLCheckSSF(struct qemud_client *client,
|
|
remote_error *rerr) {
|
|
const void *val;
|
|
int err, ssf;
|
|
|
|
if (client->type == QEMUD_SOCK_TYPE_TLS ||
|
|
client->type == QEMUD_SOCK_TYPE_UNIX)
|
|
return 0; /* TLS or UNIX domain sockets trivially OK */
|
|
|
|
err = sasl_getprop(client->saslconn, SASL_SSF, &val);
|
|
if (err != SASL_OK) {
|
|
VIR_ERROR(_("cannot query SASL ssf on connection %d (%s)"),
|
|
err, sasl_errstring(err, NULL, NULL));
|
|
remoteDispatchAuthError(rerr);
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
return -1;
|
|
}
|
|
ssf = *(const int *)val;
|
|
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);
|
|
remoteDispatchAuthError(rerr);
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
return -2;
|
|
}
|
|
|
|
/* Only setup for read initially, because we're about to send an RPC
|
|
* reply which must be in plain text. When the next incoming RPC
|
|
* arrives, we'll switch on writes too
|
|
*
|
|
* cf qemudClientReadSASL in qemud.c
|
|
*/
|
|
client->saslSSF = QEMUD_SASL_SSF_READ;
|
|
|
|
/* We have a SSF !*/
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Returns 0 if ok, -1 on error, -2 if rejected
|
|
*/
|
|
static int
|
|
remoteSASLCheckAccess(struct qemud_server *server,
|
|
struct qemud_client *client,
|
|
remote_error *rerr) {
|
|
const void *val;
|
|
int err;
|
|
char **wildcards;
|
|
|
|
err = sasl_getprop(client->saslconn, SASL_USERNAME, &val);
|
|
if (err != SASL_OK) {
|
|
VIR_ERROR(_("cannot query SASL username on connection %d (%s)"),
|
|
err, sasl_errstring(err, NULL, NULL));
|
|
remoteDispatchAuthError(rerr);
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
return -1;
|
|
}
|
|
if (val == NULL) {
|
|
VIR_ERROR(_("no client username was found"));
|
|
remoteDispatchAuthError(rerr);
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
return -1;
|
|
}
|
|
VIR_DEBUG("SASL client username %s", (const char *)val);
|
|
|
|
client->saslUsername = strdup((const char*)val);
|
|
if (client->saslUsername == NULL) {
|
|
VIR_ERROR(_("out of memory copying username"));
|
|
remoteDispatchAuthError(rerr);
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
return -1;
|
|
}
|
|
|
|
/* If the list is not set, allow any DN. */
|
|
wildcards = server->saslUsernameWhitelist;
|
|
if (!wildcards)
|
|
return 0; /* No ACL, allow all */
|
|
|
|
while (*wildcards) {
|
|
if (fnmatch(*wildcards, client->saslUsername, 0) == 0)
|
|
return 0; /* Allowed */
|
|
wildcards++;
|
|
}
|
|
|
|
/* Denied */
|
|
VIR_ERROR(_("SASL client %s not allowed in whitelist"), client->saslUsername);
|
|
remoteDispatchAuthError(rerr);
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
return -2;
|
|
}
|
|
|
|
|
|
/*
|
|
* This starts the SASL authentication negotiation.
|
|
*/
|
|
static int
|
|
remoteDispatchAuthSaslStart(struct qemud_server *server,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
remote_auth_sasl_start_args *args,
|
|
remote_auth_sasl_start_ret *ret)
|
|
{
|
|
const char *serverout;
|
|
unsigned int serveroutlen;
|
|
int err;
|
|
|
|
virMutexLock(&server->lock);
|
|
virMutexLock(&client->lock);
|
|
virMutexUnlock(&server->lock);
|
|
|
|
VIR_DEBUG("Start SASL auth %d", client->fd);
|
|
if (client->auth != REMOTE_AUTH_SASL ||
|
|
client->saslconn == NULL) {
|
|
VIR_ERROR(_("client tried invalid SASL start request"));
|
|
goto authfail;
|
|
}
|
|
|
|
VIR_DEBUG("Using SASL mechanism %s. Data %d bytes, nil: %d",
|
|
args->mech, args->data.data_len, args->nil);
|
|
err = sasl_server_start(client->saslconn,
|
|
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 != SASL_OK &&
|
|
err != SASL_CONTINUE) {
|
|
VIR_ERROR(_("sasl start failed %d (%s)"),
|
|
err, sasl_errdetail(client->saslconn));
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
goto authfail;
|
|
}
|
|
if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) {
|
|
VIR_ERROR(_("sasl start reply data too long %d"), serveroutlen);
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
goto authfail;
|
|
}
|
|
|
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
|
if (serverout) {
|
|
if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0) {
|
|
virReportOOMError();
|
|
remoteDispatchError(rerr);
|
|
goto error;
|
|
}
|
|
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 == SASL_CONTINUE) {
|
|
ret->complete = 0;
|
|
} else {
|
|
/* Check username whitelist ACL */
|
|
if ((err = remoteSASLCheckAccess(server, client, rerr)) < 0 ||
|
|
(err = remoteSASLCheckSSF(client, rerr)) < 0) {
|
|
if (err == -2)
|
|
goto authdeny;
|
|
else
|
|
goto authfail;
|
|
}
|
|
|
|
VIR_DEBUG("Authentication successful %d", client->fd);
|
|
PROBE(CLIENT_AUTH_ALLOW, "fd=%d, auth=%d, username=%s",
|
|
client->fd, REMOTE_AUTH_SASL, client->saslUsername);
|
|
ret->complete = 1;
|
|
client->auth = REMOTE_AUTH_NONE;
|
|
}
|
|
|
|
virMutexUnlock(&client->lock);
|
|
return 0;
|
|
|
|
authfail:
|
|
PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL);
|
|
remoteDispatchAuthError(rerr);
|
|
goto error;
|
|
|
|
authdeny:
|
|
PROBE(CLIENT_AUTH_DENY, "fd=%d, auth=%d, username=%s",
|
|
client->fd, REMOTE_AUTH_SASL, client->saslUsername);
|
|
goto error;
|
|
|
|
error:
|
|
virMutexUnlock(&client->lock);
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
remoteDispatchAuthSaslStep(struct qemud_server *server,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
remote_auth_sasl_step_args *args,
|
|
remote_auth_sasl_step_ret *ret)
|
|
{
|
|
const char *serverout;
|
|
unsigned int serveroutlen;
|
|
int err;
|
|
|
|
virMutexLock(&server->lock);
|
|
virMutexLock(&client->lock);
|
|
virMutexUnlock(&server->lock);
|
|
|
|
VIR_DEBUG("Step SASL auth %d", client->fd);
|
|
if (client->auth != REMOTE_AUTH_SASL ||
|
|
client->saslconn == NULL) {
|
|
VIR_ERROR(_("client tried invalid SASL start request"));
|
|
goto authfail;
|
|
}
|
|
|
|
VIR_DEBUG("Using SASL Data %d bytes, nil: %d",
|
|
args->data.data_len, args->nil);
|
|
err = sasl_server_step(client->saslconn,
|
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
|
args->nil ? NULL : args->data.data_val,
|
|
args->data.data_len,
|
|
&serverout,
|
|
&serveroutlen);
|
|
if (err != SASL_OK &&
|
|
err != SASL_CONTINUE) {
|
|
VIR_ERROR(_("sasl step failed %d (%s)"),
|
|
err, sasl_errdetail(client->saslconn));
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
goto authfail;
|
|
}
|
|
|
|
if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) {
|
|
VIR_ERROR(_("sasl step reply data too long %d"),
|
|
serveroutlen);
|
|
sasl_dispose(&client->saslconn);
|
|
client->saslconn = NULL;
|
|
goto authfail;
|
|
}
|
|
|
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
|
if (serverout) {
|
|
if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0) {
|
|
virReportOOMError();
|
|
remoteDispatchError(rerr);
|
|
goto error;
|
|
}
|
|
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 == SASL_CONTINUE) {
|
|
ret->complete = 0;
|
|
} else {
|
|
/* Check username whitelist ACL */
|
|
if ((err = remoteSASLCheckAccess(server, client, rerr)) < 0 ||
|
|
(err = remoteSASLCheckSSF(client, rerr)) < 0) {
|
|
if (err == -2)
|
|
goto authdeny;
|
|
else
|
|
goto authfail;
|
|
}
|
|
|
|
VIR_DEBUG("Authentication successful %d", client->fd);
|
|
PROBE(CLIENT_AUTH_ALLOW, "fd=%d, auth=%d, username=%s",
|
|
client->fd, REMOTE_AUTH_SASL, client->saslUsername);
|
|
ret->complete = 1;
|
|
client->auth = REMOTE_AUTH_NONE;
|
|
}
|
|
|
|
virMutexUnlock(&client->lock);
|
|
return 0;
|
|
|
|
authfail:
|
|
PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL);
|
|
remoteDispatchAuthError(rerr);
|
|
goto error;
|
|
|
|
authdeny:
|
|
PROBE(CLIENT_AUTH_DENY, "fd=%d, auth=%d, username=%s",
|
|
client->fd, REMOTE_AUTH_SASL, client->saslUsername);
|
|
goto error;
|
|
|
|
error:
|
|
virMutexUnlock(&client->lock);
|
|
return -1;
|
|
}
|
|
|
|
|
|
#else /* HAVE_SASL */
|
|
static int
|
|
remoteDispatchAuthSaslInit(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
void *args ATTRIBUTE_UNUSED,
|
|
remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
VIR_ERROR(_("client tried unsupported SASL init request"));
|
|
PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL);
|
|
remoteDispatchAuthError(rerr);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchAuthSaslStart(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED,
|
|
remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
VIR_ERROR(_("client tried unsupported SASL start request"));
|
|
PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL);
|
|
remoteDispatchAuthError(rerr);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchAuthSaslStep(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED,
|
|
remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
VIR_ERROR(_("client tried unsupported SASL step request"));
|
|
PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL);
|
|
remoteDispatchAuthError(rerr);
|
|
return -1;
|
|
}
|
|
#endif /* HAVE_SASL */
|
|
|
|
|
|
#if HAVE_POLKIT1
|
|
static int
|
|
remoteDispatchAuthPolkit(struct qemud_server *server,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
void *args ATTRIBUTE_UNUSED,
|
|
remote_auth_polkit_ret *ret)
|
|
{
|
|
pid_t callerPid = -1;
|
|
uid_t callerUid = -1;
|
|
const char *action;
|
|
int status = -1;
|
|
char pidbuf[50];
|
|
char ident[100];
|
|
int rv;
|
|
|
|
memset(ident, 0, sizeof ident);
|
|
|
|
virMutexLock(&server->lock);
|
|
virMutexLock(&client->lock);
|
|
virMutexUnlock(&server->lock);
|
|
|
|
action = client->readonly ?
|
|
"org.libvirt.unix.monitor" :
|
|
"org.libvirt.unix.manage";
|
|
|
|
const char * const pkcheck [] = {
|
|
PKCHECK_PATH,
|
|
"--action-id", action,
|
|
"--process", pidbuf,
|
|
"--allow-user-interaction",
|
|
NULL
|
|
};
|
|
|
|
VIR_DEBUG("Start PolicyKit auth %d", client->fd);
|
|
if (client->auth != REMOTE_AUTH_POLKIT) {
|
|
VIR_ERROR(_("client tried invalid PolicyKit init request"));
|
|
goto authfail;
|
|
}
|
|
|
|
if (qemudGetSocketIdentity(client->fd, &callerUid, &callerPid) < 0) {
|
|
VIR_ERROR(_("cannot get peer socket identity"));
|
|
goto authfail;
|
|
}
|
|
|
|
VIR_INFO("Checking PID %d running as %d", callerPid, callerUid);
|
|
|
|
rv = snprintf(pidbuf, sizeof pidbuf, "%d", callerPid);
|
|
if (rv < 0 || rv >= sizeof pidbuf) {
|
|
VIR_ERROR(_("Caller PID was too large %d"), callerPid);
|
|
goto authfail;
|
|
}
|
|
|
|
rv = snprintf(ident, sizeof ident, "pid:%d,uid:%d", callerPid, callerUid);
|
|
if (rv < 0 || rv >= sizeof ident) {
|
|
VIR_ERROR(_("Caller identity was too large %d:%d"), callerPid, callerUid);
|
|
goto authfail;
|
|
}
|
|
|
|
if (virRun(pkcheck, &status) < 0) {
|
|
VIR_ERROR(_("Cannot invoke %s"), PKCHECK_PATH);
|
|
goto authfail;
|
|
}
|
|
if (status != 0) {
|
|
char *tmp = virCommandTranslateStatus(status);
|
|
VIR_ERROR(_("Policy kit denied action %s from pid %d, uid %d: %s"),
|
|
action, callerPid, callerUid, NULLSTR(tmp));
|
|
VIR_FREE(tmp);
|
|
goto authdeny;
|
|
}
|
|
PROBE(CLIENT_AUTH_ALLOW, "fd=%d, auth=%d, username=%s",
|
|
client->fd, REMOTE_AUTH_POLKIT, (char *)ident);
|
|
VIR_INFO("Policy allowed action %s from pid %d, uid %d",
|
|
action, callerPid, callerUid);
|
|
ret->complete = 1;
|
|
client->auth = REMOTE_AUTH_NONE;
|
|
|
|
virMutexUnlock(&client->lock);
|
|
return 0;
|
|
|
|
authfail:
|
|
PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_POLKIT);
|
|
goto error;
|
|
|
|
authdeny:
|
|
PROBE(CLIENT_AUTH_DENY, "fd=%d, auth=%d, username=%s",
|
|
client->fd, REMOTE_AUTH_POLKIT, (char *)ident);
|
|
goto error;
|
|
|
|
error:
|
|
remoteDispatchAuthError(rerr);
|
|
virMutexUnlock(&client->lock);
|
|
return -1;
|
|
}
|
|
#elif HAVE_POLKIT0
|
|
static int
|
|
remoteDispatchAuthPolkit(struct qemud_server *server,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
void *args ATTRIBUTE_UNUSED,
|
|
remote_auth_polkit_ret *ret)
|
|
{
|
|
pid_t callerPid;
|
|
uid_t callerUid;
|
|
PolKitCaller *pkcaller = NULL;
|
|
PolKitAction *pkaction = NULL;
|
|
PolKitContext *pkcontext = NULL;
|
|
PolKitError *pkerr = NULL;
|
|
PolKitResult pkresult;
|
|
DBusError err;
|
|
const char *action;
|
|
char ident[100];
|
|
int rv;
|
|
|
|
memset(ident, 0, sizeof ident);
|
|
|
|
virMutexLock(&server->lock);
|
|
virMutexLock(&client->lock);
|
|
virMutexUnlock(&server->lock);
|
|
|
|
action = client->readonly ?
|
|
"org.libvirt.unix.monitor" :
|
|
"org.libvirt.unix.manage";
|
|
|
|
VIR_DEBUG("Start PolicyKit auth %d", client->fd);
|
|
if (client->auth != REMOTE_AUTH_POLKIT) {
|
|
VIR_ERROR(_("client tried invalid PolicyKit init request"));
|
|
goto authfail;
|
|
}
|
|
|
|
if (qemudGetSocketIdentity(client->fd, &callerUid, &callerPid) < 0) {
|
|
VIR_ERROR(_("cannot get peer socket identity"));
|
|
goto authfail;
|
|
}
|
|
|
|
rv = snprintf(ident, sizeof ident, "pid:%d,uid:%d", callerPid, callerUid);
|
|
if (rv < 0 || rv >= sizeof ident) {
|
|
VIR_ERROR(_("Caller identity was too large %d:%d"), callerPid, callerUid);
|
|
goto authfail;
|
|
}
|
|
|
|
VIR_INFO("Checking PID %d running as %d", callerPid, callerUid);
|
|
dbus_error_init(&err);
|
|
if (!(pkcaller = polkit_caller_new_from_pid(server->sysbus,
|
|
callerPid, &err))) {
|
|
VIR_ERROR(_("Failed to lookup policy kit caller: %s"), err.message);
|
|
dbus_error_free(&err);
|
|
goto authfail;
|
|
}
|
|
|
|
if (!(pkaction = polkit_action_new())) {
|
|
char ebuf[1024];
|
|
VIR_ERROR(_("Failed to create polkit action %s"),
|
|
virStrerror(errno, ebuf, sizeof ebuf));
|
|
polkit_caller_unref(pkcaller);
|
|
goto authfail;
|
|
}
|
|
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"),
|
|
(pkerr ? polkit_error_get_error_message(pkerr)
|
|
: virStrerror(errno, ebuf, sizeof ebuf)));
|
|
if (pkerr)
|
|
polkit_error_free(pkerr);
|
|
polkit_caller_unref(pkcaller);
|
|
polkit_action_unref(pkaction);
|
|
dbus_error_free(&err);
|
|
goto authfail;
|
|
}
|
|
|
|
# if HAVE_POLKIT_CONTEXT_IS_CALLER_AUTHORIZED
|
|
pkresult = polkit_context_is_caller_authorized(pkcontext,
|
|
pkaction,
|
|
pkcaller,
|
|
0,
|
|
&pkerr);
|
|
if (pkerr && polkit_error_is_set(pkerr)) {
|
|
VIR_ERROR(_("Policy kit failed to check authorization %d %s"),
|
|
polkit_error_get_error_code(pkerr),
|
|
polkit_error_get_error_message(pkerr));
|
|
goto authfail;
|
|
}
|
|
# else
|
|
pkresult = polkit_context_can_caller_do_action(pkcontext,
|
|
pkaction,
|
|
pkcaller);
|
|
# endif
|
|
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 %d, uid %d, result: %s"),
|
|
action, callerPid, callerUid,
|
|
polkit_result_to_string_representation(pkresult));
|
|
goto authdeny;
|
|
}
|
|
PROBE(CLIENT_AUTH_ALLOW, "fd=%d, auth=%d, username=%s",
|
|
client->fd, REMOTE_AUTH_POLKIT, ident);
|
|
VIR_INFO("Policy allowed action %s from pid %d, uid %d, result %s",
|
|
action, callerPid, callerUid,
|
|
polkit_result_to_string_representation(pkresult));
|
|
ret->complete = 1;
|
|
client->auth = REMOTE_AUTH_NONE;
|
|
|
|
virMutexUnlock(&client->lock);
|
|
return 0;
|
|
|
|
authfail:
|
|
PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_POLKIT);
|
|
goto error;
|
|
|
|
authdeny:
|
|
PROBE(CLIENT_AUTH_DENY, "fd=%d, auth=%d, username=%s",
|
|
client->fd, REMOTE_AUTH_POLKIT, ident);
|
|
goto error;
|
|
|
|
error:
|
|
remoteDispatchAuthError(rerr);
|
|
virMutexUnlock(&client->lock);
|
|
return -1;
|
|
}
|
|
|
|
#else /* !HAVE_POLKIT0 & !HAVE_POLKIT1*/
|
|
|
|
static int
|
|
remoteDispatchAuthPolkit(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
void *args ATTRIBUTE_UNUSED,
|
|
remote_auth_polkit_ret *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
VIR_ERROR(_("client tried unsupported PolicyKit init request"));
|
|
remoteDispatchAuthError(rerr);
|
|
return -1;
|
|
}
|
|
#endif /* HAVE_POLKIT1 */
|
|
|
|
|
|
/***************************************************************
|
|
* NODE INFO APIS
|
|
**************************************************************/
|
|
|
|
static int
|
|
remoteDispatchNodeDeviceGetParent(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *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;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dev = virNodeDeviceLookupByName(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)
|
|
remoteDispatchError(rerr);
|
|
if (dev)
|
|
virNodeDeviceFree(dev);
|
|
return rv;
|
|
}
|
|
|
|
static int remoteDispatchStorageVolUpload(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr,
|
|
remote_error *rerr,
|
|
remote_storage_vol_upload_args *args,
|
|
void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
struct qemud_client_stream *stream = NULL;
|
|
virStorageVolPtr vol = NULL;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(vol = get_nonnull_storage_vol(conn, args->vol)))
|
|
goto cleanup;
|
|
|
|
if (!(stream = remoteCreateClientStream(conn, hdr)))
|
|
goto cleanup;
|
|
|
|
if (virStorageVolUpload(vol, stream->st,
|
|
args->offset, args->length,
|
|
args->flags) < 0)
|
|
goto cleanup;
|
|
|
|
if (remoteAddClientStream(client, stream, 0) < 0)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
if (vol)
|
|
virStorageVolFree(vol);
|
|
if (stream && rv != 0) {
|
|
virStreamAbort(stream->st);
|
|
remoteFreeClientStream(client, stream);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static int remoteDispatchStorageVolDownload(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr,
|
|
remote_error *rerr,
|
|
remote_storage_vol_download_args *args,
|
|
void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
struct qemud_client_stream *stream = NULL;
|
|
virStorageVolPtr vol = NULL;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(vol = get_nonnull_storage_vol(conn, args->vol)))
|
|
goto cleanup;
|
|
|
|
if (!(stream = remoteCreateClientStream(conn, hdr)))
|
|
goto cleanup;
|
|
|
|
if (virStorageVolDownload(vol, stream->st,
|
|
args->offset, args->length,
|
|
args->flags) < 0)
|
|
goto cleanup;
|
|
|
|
if (remoteAddClientStream(client, stream, 1) < 0)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
if (vol)
|
|
virStorageVolFree(vol);
|
|
if (stream && rv != 0) {
|
|
virStreamAbort(stream->st);
|
|
remoteFreeClientStream(client, stream);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
/***************************
|
|
* Register / deregister events
|
|
***************************/
|
|
static int
|
|
remoteDispatchDomainEventsRegister(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr ATTRIBUTE_UNUSED,
|
|
void *args ATTRIBUTE_UNUSED,
|
|
remote_domain_events_register_ret *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
int callbackID;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] != -1) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, _("domain event %d already registered"), VIR_DOMAIN_EVENT_ID_LIFECYCLE);
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((callbackID = virConnectDomainEventRegisterAny(conn,
|
|
NULL,
|
|
VIR_DOMAIN_EVENT_ID_LIFECYCLE,
|
|
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
|
|
client, NULL)) < 0)
|
|
goto cleanup;
|
|
|
|
client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = callbackID;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainEventsDeregister(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr ATTRIBUTE_UNUSED,
|
|
void *args ATTRIBUTE_UNUSED,
|
|
remote_domain_events_deregister_ret *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] < 0) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, _("domain event %d not registered"), VIR_DOMAIN_EVENT_ID_LIFECYCLE);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virConnectDomainEventDeregisterAny(conn,
|
|
client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE]) < 0)
|
|
goto cleanup;
|
|
|
|
client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = -1;
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
return rv;
|
|
}
|
|
|
|
static void
|
|
remoteDispatchDomainEventSend(struct qemud_client *client,
|
|
int procnr,
|
|
xdrproc_t proc,
|
|
void *data)
|
|
{
|
|
struct qemud_client_message *msg = NULL;
|
|
XDR xdr;
|
|
unsigned int len;
|
|
|
|
if (VIR_ALLOC(msg) < 0)
|
|
return;
|
|
|
|
msg->hdr.prog = REMOTE_PROGRAM;
|
|
msg->hdr.vers = REMOTE_PROTOCOL_VERSION;
|
|
msg->hdr.proc = procnr;
|
|
msg->hdr.type = REMOTE_MESSAGE;
|
|
msg->hdr.serial = 1;
|
|
msg->hdr.status = REMOTE_OK;
|
|
|
|
if (remoteEncodeClientMessageHeader(msg) < 0)
|
|
goto cleanup;
|
|
|
|
/* Serialise the return header and event. */
|
|
xdrmem_create(&xdr,
|
|
msg->buffer,
|
|
msg->bufferLength,
|
|
XDR_ENCODE);
|
|
|
|
/* Skip over the header we just wrote */
|
|
if (xdr_setpos(&xdr, msg->bufferOffset) == 0)
|
|
goto xdr_cleanup;
|
|
|
|
if (!(proc)(&xdr, data)) {
|
|
VIR_WARN("Failed to serialize domain event %d", procnr);
|
|
goto xdr_cleanup;
|
|
}
|
|
|
|
/* Update length word to include payload*/
|
|
len = msg->bufferOffset = xdr_getpos(&xdr);
|
|
if (xdr_setpos(&xdr, 0) == 0)
|
|
goto xdr_cleanup;
|
|
|
|
if (!xdr_u_int(&xdr, &len))
|
|
goto xdr_cleanup;
|
|
|
|
/* Send it. */
|
|
msg->async = 1;
|
|
msg->bufferLength = len;
|
|
msg->bufferOffset = 0;
|
|
|
|
VIR_DEBUG("Queue event %d %d", procnr, msg->bufferLength);
|
|
qemudClientMessageQueuePush(&client->tx, msg);
|
|
qemudUpdateClientEvent(client);
|
|
|
|
xdr_destroy(&xdr);
|
|
return;
|
|
|
|
xdr_cleanup:
|
|
xdr_destroy(&xdr);
|
|
cleanup:
|
|
VIR_FREE(msg);
|
|
}
|
|
|
|
static int
|
|
remoteDispatchSecretGetValue(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *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;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(secret = get_nonnull_secret(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)
|
|
remoteDispatchError(rerr);
|
|
if (secret)
|
|
virSecretFree(secret);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainGetState(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
remote_domain_get_state_args *args,
|
|
remote_domain_get_state_ret *ret)
|
|
{
|
|
virDomainPtr dom = NULL;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainGetState(dom, &ret->state, &ret->reason, args->flags) < 0)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
remoteDispatchDomainEventsRegisterAny(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr ATTRIBUTE_UNUSED,
|
|
remote_domain_events_register_any_args *args,
|
|
void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
int callbackID;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (args->eventID >= VIR_DOMAIN_EVENT_ID_LAST ||
|
|
args->eventID < 0) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"), args->eventID);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (client->domainEventCallbackID[args->eventID] != -1) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, _("domain event %d already registered"), args->eventID);
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((callbackID = virConnectDomainEventRegisterAny(conn,
|
|
NULL,
|
|
args->eventID,
|
|
domainEventCallbacks[args->eventID],
|
|
client, NULL)) < 0)
|
|
goto cleanup;
|
|
|
|
client->domainEventCallbackID[args->eventID] = callbackID;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
return rv;
|
|
}
|
|
|
|
|
|
static int
|
|
remoteDispatchDomainEventsDeregisterAny(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr ATTRIBUTE_UNUSED,
|
|
remote_domain_events_deregister_any_args *args,
|
|
void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
int callbackID = -1;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (args->eventID >= VIR_DOMAIN_EVENT_ID_LAST ||
|
|
args->eventID < 0) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"), args->eventID);
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((callbackID = client->domainEventCallbackID[args->eventID]) < 0) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, _("domain event %d not registered"), args->eventID);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virConnectDomainEventDeregisterAny(conn, callbackID) < 0)
|
|
goto cleanup;
|
|
|
|
client->domainEventCallbackID[args->eventID] = -1;
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
qemuDispatchMonitorCommand(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client ATTRIBUTE_UNUSED,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
|
remote_error *rerr,
|
|
qemu_monitor_command_args *args,
|
|
qemu_monitor_command_ret *ret)
|
|
{
|
|
virDomainPtr dom = NULL;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (virDomainQemuMonitorCommand(dom, args->cmd, &ret->result,
|
|
args->flags) < 0)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
}
|
|
|
|
|
|
static int
|
|
remoteDispatchDomainOpenConsole(struct qemud_server *server ATTRIBUTE_UNUSED,
|
|
struct qemud_client *client,
|
|
virConnectPtr conn,
|
|
remote_message_header *hdr,
|
|
remote_error *rerr,
|
|
remote_domain_open_console_args *args,
|
|
void *ret ATTRIBUTE_UNUSED)
|
|
{
|
|
struct qemud_client_stream *stream = NULL;
|
|
virDomainPtr dom = NULL;
|
|
int rv = -1;
|
|
|
|
if (!conn) {
|
|
virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(dom = get_nonnull_domain(conn, args->dom)))
|
|
goto cleanup;
|
|
|
|
if (!(stream = remoteCreateClientStream(conn, hdr))) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virDomainOpenConsole(dom,
|
|
args->devname ? *args->devname : NULL,
|
|
stream->st,
|
|
args->flags) < 0)
|
|
goto cleanup;
|
|
|
|
if (remoteAddClientStream(client, stream, 1) < 0)
|
|
goto cleanup;
|
|
|
|
rv = 0;
|
|
|
|
cleanup:
|
|
if (rv < 0)
|
|
remoteDispatchError(rerr);
|
|
if (stream && rv < 0) {
|
|
virStreamAbort(stream->st);
|
|
remoteFreeClientStream(client, stream);
|
|
}
|
|
if (dom)
|
|
virDomainFree(dom);
|
|
return rv;
|
|
}
|
|
|
|
#include "remote_dispatch_bodies.h"
|
|
#include "qemu_dispatch_bodies.h"
|
|
|
|
|
|
/*----- 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);
|
|
}
|