libvirt/src/libvirt.c

1342 lines
35 KiB
C
Raw Normal View History

/*
* libvirt.c: Main interfaces for the libvirt library to handle virtualization
2005-11-02 12:50:21 +00:00
* domains from a process running in domain 0
*
* Copyright (C) 2005-2006, 2008-2014 Red Hat, Inc.
2005-11-02 12:50:21 +00:00
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
2005-11-02 12:50:21 +00:00
*
* Daniel Veillard <veillard@redhat.com>
*/
#include <config.h>
2005-11-02 12:50:21 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
2009-01-22 20:27:01 +00:00
#include <time.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include "getpass.h"
#ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
#endif
2013-01-08 21:09:09 +00:00
#ifdef WITH_CURL
# include <curl/curl.h>
#endif
#include "virerror.h"
2012-12-12 17:59:27 +00:00
#include "virlog.h"
#include "datatypes.h"
#include "driver.h"
2012-12-13 18:01:25 +00:00
#include "viruuid.h"
2012-12-12 18:06:53 +00:00
#include "viralloc.h"
maint: use gnulib configmake rather than open-coding things * bootstrap.conf (gnulib_modules): Add configmake. * daemon/Makefile.am (libvirtd_CFLAGS): Drop defines provided by gnulib. * src/Makefile.am (INCLUDES): Likewise. * tests/Makefile.am (INCLUDES): Likewise. * tools/Makefile.am (virsh_CFLAGS): Likewise. * daemon/libvirtd.c (qemudInitPaths, usage, main): Update clients. * src/cpu/cpu_map.c (CPUMAPFILE): Likewise. * src/driver.c (DEFAULT_DRIVER_DIR): Likewise. * src/internal.h (_): Likewise. * src/libvirt.c (virInitialize): Likewise. * src/lxc/lxc_conf.h (LXC_CONFIG_DIR, LXC_STATE_DIR, LXC_LOG_DIR): Likewise. * src/lxc/lxc_conf.c (lxcCapsInit, lxcLoadDriverConfig): Likewise. * src/network/bridge_driver.c (NETWORK_PID_DIR) (NETWORK_STATE_DIR, DNSMASQ_STATE_DIR, networkStartup): Likewise. * src/nwfilter/nwfilter_driver.c (nwfilterDriverStartup): Likewise. * src/qemu/qemu_conf.c (qemudLoadDriverConfig): Likewise. * src/qemu/qemu_driver.c (qemudStartup): Likewise. * src/remote/remote_driver.h (LIBVIRTD_PRIV_UNIX_SOCKET) (LIBVIRTD_PRIV_UNIX_SOCKET_RO, LIBVIRTD_CONFIGURATION_FILE) (LIBVIRT_PKI_DIR): Likewise. * src/secret/secret_driver.c (secretDriverStartup): Likewise. * src/security/security_apparmor.c (VIRT_AA_HELPER): Likewise. * src/security/virt-aa-helper.c (main): Likewise. * src/storage/storage_backend_disk.c (PARTHELPER): Likewise. * src/storage/storage_driver.c (storageDriverStartup): Likewise. * src/uml/uml_driver.c (TEMPDIR, umlStartup): Likewise. * src/util/hooks.c (LIBVIRT_HOOK_DIR): Likewise. * tools/virsh.c (main): Likewise. * docs/hooks.html.in: Likewise.
2010-11-16 14:54:17 +00:00
#include "configmake.h"
2012-12-12 16:35:35 +00:00
#include "virconf.h"
2013-01-08 21:02:05 +00:00
#if WITH_GNUTLS
# if WITH_GNUTLS_GCRYPT
# include <gcrypt.h>
# endif
# include "rpc/virnettlscontext.h"
#endif
#include "vircommand.h"
#include "virfile.h"
#include "virrandom.h"
#include "viruri.h"
#include "virthread.h"
#include "virstring.h"
#include "virutil.h"
#include "virtypedparam.h"
#ifdef WITH_TEST
# include "test/test_driver.h"
#endif
#ifdef WITH_REMOTE
# include "remote/remote_driver.h"
#endif
#ifdef WITH_OPENVZ
# include "openvz/openvz_driver.h"
#endif
#ifdef WITH_VMWARE
# include "vmware/vmware_driver.h"
#endif
#ifdef WITH_PHYP
# include "phyp/phyp_driver.h"
#endif
#ifdef WITH_ESX
# include "esx/esx_driver.h"
#endif
#ifdef WITH_HYPERV
# include "hyperv/hyperv_driver.h"
#endif
#ifdef WITH_XENAPI
# include "xenapi/xenapi_driver.h"
2008-11-21 12:16:08 +00:00
#endif
#ifdef WITH_BHYVE
# include "bhyve/bhyve_driver.h"
#endif
#define VIR_FROM_THIS VIR_FROM_NONE
VIR_LOG_INIT("libvirt");
2005-11-02 12:50:21 +00:00
/*
* TODO:
* - use lock to protect against concurrent accesses ?
* - use reference counting to guarantee coherent pointer state ?
2005-11-02 12:50:21 +00:00
*/
#define MAX_DRIVERS 21
static virConnectDriverPtr virConnectDriverTab[MAX_DRIVERS];
static int virConnectDriverTabCount;
static virStateDriverPtr virStateDriverTab[MAX_DRIVERS];
static int virStateDriverTabCount;
static virNetworkDriverPtr virSharedNetworkDriver;
static virInterfaceDriverPtr virSharedInterfaceDriver;
static virStorageDriverPtr virSharedStorageDriver;
static virNodeDeviceDriverPtr virSharedNodeDeviceDriver;
static virSecretDriverPtr virSharedSecretDriver;
static virNWFilterDriverPtr virSharedNWFilterDriver;
#if defined(POLKIT_AUTH)
static int
virConnectAuthGainPolkit(const char *privilege)
{
virCommandPtr cmd;
int ret = -1;
if (geteuid() == 0)
return 0;
cmd = virCommandNewArgList(POLKIT_AUTH, "--obtain", privilege, NULL);
util: make it easier to grab only regular command exit Auditing all callers of virCommandRun and virCommandWait that passed a non-NULL pointer for exit status turned up some interesting observations. Many callers were merely passing a pointer to avoid the overall command dying, but without caring what the exit status was - but these callers would be better off treating a child death by signal as an abnormal exit. Other callers were actually acting on the status, but not all of them remembered to filter by WIFEXITED and convert with WEXITSTATUS; depending on the platform, this can result in a status being reported as 256 times too big. And among those that correctly parse the output, it gets rather verbose. Finally, there were the callers that explicitly checked that the status was 0, and gave their own message, but with fewer details than what virCommand gives for free. So the best idea is to move the complexity out of callers and into virCommand - by default, we return the actual exit status already cleaned through WEXITSTATUS and treat signals as a failed command; but the few callers that care can ask for raw status and act on it themselves. * src/util/vircommand.h (virCommandRawStatus): New prototype. * src/libvirt_private.syms (util/command.h): Export it. * docs/internals/command.html.in: Document it. * src/util/vircommand.c (virCommandRawStatus): New function. (virCommandWait): Adjust semantics. * tests/commandtest.c (test1): Test it. * daemon/remote.c (remoteDispatchAuthPolkit): Adjust callers. * src/access/viraccessdriverpolkit.c (virAccessDriverPolkitCheck): Likewise. * src/fdstream.c (virFDStreamCloseInt): Likewise. * src/lxc/lxc_process.c (virLXCProcessStart): Likewise. * src/qemu/qemu_command.c (qemuCreateInBridgePortWithHelper): Likewise. * src/xen/xen_driver.c (xenUnifiedXendProbe): Simplify. * tests/reconnect.c (mymain): Likewise. * tests/statstest.c (mymain): Likewise. * src/bhyve/bhyve_process.c (virBhyveProcessStart) (virBhyveProcessStop): Don't overwrite virCommand error. * src/libvirt.c (virConnectAuthGainPolkit): Likewise. * src/openvz/openvz_driver.c (openvzDomainGetBarrierLimit) (openvzDomainSetBarrierLimit): Likewise. * src/util/virebtables.c (virEbTablesOnceInit): Likewise. * src/util/viriptables.c (virIpTablesOnceInit): Likewise. * src/util/virnetdevveth.c (virNetDevVethCreate): Fix debug message. * src/qemu/qemu_capabilities.c (virQEMUCapsInitQMP): Add comment. * src/storage/storage_backend_iscsi.c (virStorageBackendISCSINodeUpdate): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-20 00:32:19 +00:00
if (virCommandRun(cmd, NULL) < 0)
goto cleanup;
ret = 0;
cleanup:
virCommandFree(cmd);
return ret;
}
#endif
static int
virConnectAuthCallbackDefault(virConnectCredentialPtr cred,
unsigned int ncred,
void *cbdata ATTRIBUTE_UNUSED)
{
size_t i;
for (i = 0; i < ncred; i++) {
char buf[1024];
char *bufptr = buf;
size_t len;
switch (cred[i].type) {
case VIR_CRED_EXTERNAL: {
if (STRNEQ(cred[i].challenge, "PolicyKit"))
return -1;
2008-04-04 15:09:19 +00:00
#if defined(POLKIT_AUTH)
if (virConnectAuthGainPolkit(cred[i].prompt) < 0)
return -1;
2008-04-04 15:09:19 +00:00
#else
/*
* Ignore & carry on. Although we can't auth
* directly, the user may have authenticated
* themselves already outside context of libvirt
*/
#endif
break;
}
2008-04-04 15:09:19 +00:00
case VIR_CRED_USERNAME:
case VIR_CRED_AUTHNAME:
case VIR_CRED_ECHOPROMPT:
case VIR_CRED_REALM:
if (printf("%s: ", cred[i].prompt) < 0)
return -1;
if (fflush(stdout) != 0)
return -1;
if (!fgets(buf, sizeof(buf), stdin)) {
if (feof(stdin)) { /* Treat EOF as "" */
buf[0] = '\0';
break;
}
return -1;
}
len = strlen(buf);
if (len != 0 && buf[len-1] == '\n')
buf[len-1] = '\0';
break;
case VIR_CRED_PASSPHRASE:
case VIR_CRED_NOECHOPROMPT:
if (printf("%s: ", cred[i].prompt) < 0)
return -1;
if (fflush(stdout) != 0)
return -1;
bufptr = getpass("");
if (!bufptr)
return -1;
break;
default:
return -1;
}
2008-05-22 15:12:25 +00:00
if (cred[i].type != VIR_CRED_EXTERNAL) {
if (VIR_STRDUP(cred[i].result,
STREQ(bufptr, "") && cred[i].defresult ?
cred[i].defresult : bufptr) < 0)
2008-05-22 15:12:25 +00:00
return -1;
cred[i].resultlen = strlen(cred[i].result);
}
}
return 0;
}
/* Don't typically want VIR_CRED_USERNAME. It enables you to authenticate
* as one user, and act as another. It just results in annoying
* prompts for the username twice & is very rarely what you want
*/
static int virConnectCredTypeDefault[] = {
VIR_CRED_AUTHNAME,
VIR_CRED_ECHOPROMPT,
VIR_CRED_REALM,
VIR_CRED_PASSPHRASE,
VIR_CRED_NOECHOPROMPT,
VIR_CRED_EXTERNAL,
};
static virConnectAuth virConnectAuthDefault = {
virConnectCredTypeDefault,
sizeof(virConnectCredTypeDefault)/sizeof(int),
virConnectAuthCallbackDefault,
NULL,
};
/*
* virConnectAuthPtrDefault
*
* A default implementation of the authentication callbacks. This
* implementation is suitable for command line based tools. It will
* prompt for username, passwords, realm and one time keys as needed.
* It will print on STDOUT, and read from STDIN. If this is not
* suitable for the application's needs an alternative implementation
* should be provided.
*/
virConnectAuthPtr virConnectAuthPtrDefault = &virConnectAuthDefault;
#if HAVE_WINSOCK2_H
static int
virWinsockInit(void)
{
WORD winsock_version, err;
WSADATA winsock_data;
/* http://msdn2.microsoft.com/en-us/library/ms742213.aspx */
winsock_version = MAKEWORD (2, 2);
err = WSAStartup (winsock_version, &winsock_data);
return err == 0 ? 0 : -1;
}
#endif
#ifdef WITH_GNUTLS_GCRYPT
static int
virTLSMutexInit(void **priv)
{
virMutexPtr lock = NULL;
if (VIR_ALLOC_QUIET(lock) < 0)
return ENOMEM;
if (virMutexInit(lock) < 0) {
VIR_FREE(lock);
return errno;
}
*priv = lock;
return 0;
}
static int
virTLSMutexDestroy(void **priv)
{
virMutexPtr lock = *priv;
virMutexDestroy(lock);
VIR_FREE(lock);
return 0;
}
static int
virTLSMutexLock(void **priv)
{
virMutexPtr lock = *priv;
virMutexLock(lock);
return 0;
}
static int
virTLSMutexUnlock(void **priv)
{
virMutexPtr lock = *priv;
virMutexUnlock(lock);
return 0;
}
static struct gcry_thread_cbs virTLSThreadImpl = {
/* GCRY_THREAD_OPTION_VERSION was added in gcrypt 1.4.2 */
# ifdef GCRY_THREAD_OPTION_VERSION
(GCRY_THREAD_OPTION_PTHREAD | (GCRY_THREAD_OPTION_VERSION << 8)),
# else
GCRY_THREAD_OPTION_PTHREAD,
# endif
NULL,
virTLSMutexInit,
virTLSMutexDestroy,
virTLSMutexLock,
virTLSMutexUnlock,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
#endif /* WITH_GNUTLS_GCRYPT */
2009-01-15 19:56:05 +00:00
static bool virGlobalError;
static virOnceControl virGlobalOnce = VIR_ONCE_CONTROL_INITIALIZER;
static void
virGlobalInit(void)
{
/* It would be nice if we could trace the use of this call, to
* help diagnose in log files if a user calls something other than
* virConnectOpen first. But we can't rely on VIR_DEBUG working
* until after initialization is complete, and since this is
* one-shot, we never get here again. */
2009-01-20 12:01:45 +00:00
if (virThreadInitialize() < 0 ||
virErrorInitialize() < 0)
goto error;
2009-01-15 19:56:05 +00:00
#ifndef LIBVIRT_SETUID_RPC_CLIENT
if (virIsSUID()) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("libvirt.so is not safe to use from setuid programs"));
goto error;
}
#endif
#ifdef WITH_GNUTLS_GCRYPT
/*
* This sequence of API calls it copied exactly from
* gnutls 2.12.23 source lib/gcrypt/init.c, with
* exception that GCRYCTL_ENABLE_QUICK_RANDOM, is
* dropped
*/
if (gcry_control(GCRYCTL_ANY_INITIALIZATION_P) == 0) {
gcry_control(GCRYCTL_SET_THREAD_CBS, &virTLSThreadImpl);
gcry_check_version(NULL);
gcry_control(GCRYCTL_DISABLE_SECMEM, NULL, 0);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, NULL, 0);
}
#endif
virLogSetFromEnv();
2013-01-08 21:02:05 +00:00
#ifdef WITH_GNUTLS
virNetTLSInit();
#endif
2013-01-08 21:09:09 +00:00
#if WITH_CURL
curl_global_init(CURL_GLOBAL_DEFAULT);
#endif
VIR_DEBUG("register drivers");
#if HAVE_WINSOCK2_H
if (virWinsockInit() == -1)
goto error;
#endif
maint: use gnulib configmake rather than open-coding things * bootstrap.conf (gnulib_modules): Add configmake. * daemon/Makefile.am (libvirtd_CFLAGS): Drop defines provided by gnulib. * src/Makefile.am (INCLUDES): Likewise. * tests/Makefile.am (INCLUDES): Likewise. * tools/Makefile.am (virsh_CFLAGS): Likewise. * daemon/libvirtd.c (qemudInitPaths, usage, main): Update clients. * src/cpu/cpu_map.c (CPUMAPFILE): Likewise. * src/driver.c (DEFAULT_DRIVER_DIR): Likewise. * src/internal.h (_): Likewise. * src/libvirt.c (virInitialize): Likewise. * src/lxc/lxc_conf.h (LXC_CONFIG_DIR, LXC_STATE_DIR, LXC_LOG_DIR): Likewise. * src/lxc/lxc_conf.c (lxcCapsInit, lxcLoadDriverConfig): Likewise. * src/network/bridge_driver.c (NETWORK_PID_DIR) (NETWORK_STATE_DIR, DNSMASQ_STATE_DIR, networkStartup): Likewise. * src/nwfilter/nwfilter_driver.c (nwfilterDriverStartup): Likewise. * src/qemu/qemu_conf.c (qemudLoadDriverConfig): Likewise. * src/qemu/qemu_driver.c (qemudStartup): Likewise. * src/remote/remote_driver.h (LIBVIRTD_PRIV_UNIX_SOCKET) (LIBVIRTD_PRIV_UNIX_SOCKET_RO, LIBVIRTD_CONFIGURATION_FILE) (LIBVIRT_PKI_DIR): Likewise. * src/secret/secret_driver.c (secretDriverStartup): Likewise. * src/security/security_apparmor.c (VIRT_AA_HELPER): Likewise. * src/security/virt-aa-helper.c (main): Likewise. * src/storage/storage_backend_disk.c (PARTHELPER): Likewise. * src/storage/storage_driver.c (storageDriverStartup): Likewise. * src/uml/uml_driver.c (TEMPDIR, umlStartup): Likewise. * src/util/hooks.c (LIBVIRT_HOOK_DIR): Likewise. * tools/virsh.c (main): Likewise. * docs/hooks.html.in: Likewise.
2010-11-16 14:54:17 +00:00
if (!bindtextdomain(PACKAGE, LOCALEDIR))
goto error;
/*
* Note we must avoid everything except 'remote' driver
* for virt-login-shell usage
*/
#ifndef LIBVIRT_SETUID_RPC_CLIENT
/*
* Note that the order is important: the first ones have a higher
* priority when calling virConnectOpen.
*/
# ifdef WITH_TEST
if (testRegister() == -1)
goto error;
# endif
# ifdef WITH_OPENVZ
if (openvzRegister() == -1)
goto error;
# endif
# ifdef WITH_VMWARE
if (vmwareRegister() == -1)
goto error;
# endif
# ifdef WITH_PHYP
if (phypRegister() == -1)
goto error;
# endif
# ifdef WITH_ESX
if (esxRegister() == -1)
goto error;
# endif
# ifdef WITH_HYPERV
if (hypervRegister() == -1)
goto error;
# endif
# ifdef WITH_XENAPI
if (xenapiRegister() == -1)
goto error;
# endif
#endif
#ifdef WITH_REMOTE
if (remoteRegister() == -1)
goto error;
#endif
2007-02-14 01:40:09 +00:00
return;
error:
virGlobalError = true;
}
/**
* virInitialize:
*
* Initialize the library.
*
* This method is invoked automatically by any of the virConnectOpen() API
* calls, and by virGetVersion(). Since release 1.0.0, there is no need to
* call this method even in a multithreaded application, since
* initialization is performed in a thread safe manner; but applications
* using an older version of the library should manually call this before
* setting up competing threads that attempt virConnectOpen in parallel.
*
* The only other time it would be necessary to call virInitialize is if the
* application did not invoke virConnectOpen as its first API call, such
* as when calling virEventRegisterImpl() before setting up connections,
* or when using virSetErrorFunc() to alter error reporting of the first
* connection attempt.
*
* Returns 0 in case of success, -1 in case of error
*/
int
virInitialize(void)
{
if (virOnce(&virGlobalOnce, virGlobalInit) < 0)
return -1;
if (virGlobalError)
return -1;
return 0;
}
2009-01-15 19:56:05 +00:00
#ifdef WIN32
BOOL WINAPI
DllMain(HINSTANCE instance, DWORD reason, LPVOID ignore);
2009-01-15 19:56:05 +00:00
BOOL WINAPI
DllMain(HINSTANCE instance ATTRIBUTE_UNUSED,
DWORD reason,
LPVOID ignore ATTRIBUTE_UNUSED)
2009-01-15 19:56:05 +00:00
{
switch (reason) {
case DLL_PROCESS_ATTACH:
virInitialize();
break;
case DLL_THREAD_ATTACH:
/* Nothing todo in libvirt yet */
break;
case DLL_THREAD_DETACH:
/* Release per-thread local data */
virThreadOnExit();
break;
case DLL_PROCESS_DETACH:
/* Don't bother releasing per-thread data
since (hopefully) windows cleans up
everything on process exit */
break;
}
return TRUE;
}
#endif
/**
* virSetSharedNetworkDriver:
* @driver: pointer to a network driver block
*
* Register a network virtualization driver
*
* Returns 0 on success, or -1 in case of error.
*/
int
virSetSharedNetworkDriver(virNetworkDriverPtr driver)
{
virCheckNonNullArgReturn(driver, -1);
if (virSharedNetworkDriver) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("A network driver is already registered"));
return -1;
}
VIR_DEBUG("registering %s as network driver", driver->name);
2008-11-21 12:16:08 +00:00
virSharedNetworkDriver = driver;
return 0;
}
/**
* virSetSharedInterfaceDriver:
* @driver: pointer to an interface driver block
*
* Register an interface virtualization driver
*
* Returns the driver priority or -1 in case of error.
*/
int
virSetSharedInterfaceDriver(virInterfaceDriverPtr driver)
{
virCheckNonNullArgReturn(driver, -1);
if (virSharedInterfaceDriver) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("A interface driver is already registered"));
return -1;
}
VIR_DEBUG("registering %s as interface driver", driver->name);
virSharedInterfaceDriver = driver;
return 0;
}
/**
* virSetSharedStorageDriver:
* @driver: pointer to a storage driver block
*
* Register a storage virtualization driver
*
* Returns the driver priority or -1 in case of error.
*/
int
virSetSharedStorageDriver(virStorageDriverPtr driver)
{
virCheckNonNullArgReturn(driver, -1);
if (virSharedStorageDriver) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("A storage driver is already registered"));
return -1;
}
VIR_DEBUG("registering %s as storage driver", driver->name);
2008-11-21 12:16:08 +00:00
virSharedStorageDriver = driver;
return 0;
}
/**
* virSetSharedNodeDeviceDriver:
* @driver: pointer to a device monitor block
*
* Register a device monitor
*
* Returns the driver priority or -1 in case of error.
*/
int
virSetSharedNodeDeviceDriver(virNodeDeviceDriverPtr driver)
{
virCheckNonNullArgReturn(driver, -1);
if (virSharedNodeDeviceDriver) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("A node device driver is already registered"));
return -1;
}
2008-11-21 12:46:39 +00:00
VIR_DEBUG("registering %s as device driver", driver->name);
virSharedNodeDeviceDriver = driver;
return 0;
}
/**
* virSetSharedSecretDriver:
* @driver: pointer to a secret driver block
*
* Register a secret driver
*
* Returns the driver priority or -1 in case of error.
*/
int
virSetSharedSecretDriver(virSecretDriverPtr driver)
{
virCheckNonNullArgReturn(driver, -1);
if (virSharedSecretDriver) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("A secret driver is already registered"));
return -1;
}
VIR_DEBUG("registering %s as secret driver", driver->name);
virSharedSecretDriver = driver;
return 0;
}
/**
* virSetSharedNWFilterDriver:
* @driver: pointer to a network filter driver block
*
* Register a network filter virtualization driver
*
* Returns the driver priority or -1 in case of error.
*/
int
virSetSharedNWFilterDriver(virNWFilterDriverPtr driver)
{
virCheckNonNullArgReturn(driver, -1);
if (virSharedNWFilterDriver) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("A network filter driver is already registered"));
return -1;
}
VIR_DEBUG("registering %s as network filter driver", driver->name);
virSharedNWFilterDriver = driver;
return 0;
}
/**
* virRegisterConnectDriver:
* @driver: pointer to a driver block
* @setSharedDrivers: populate shared drivers
*
* Register a virtualization driver, optionally filling in
* any empty pointers for shared secondary drivers
*
* Returns the driver priority or -1 in case of error.
*/
int
virRegisterConnectDriver(virConnectDriverPtr driver,
bool setSharedDrivers)
{
VIR_DEBUG("driver=%p name=%s", driver,
driver ? NULLSTR(driver->hypervisorDriver->name) : "(null)");
virCheckNonNullArgReturn(driver, -1);
if (virConnectDriverTabCount >= MAX_DRIVERS) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Too many drivers, cannot register %s"),
driver->hypervisorDriver->name);
return -1;
}
VIR_DEBUG("registering %s as driver %d",
driver->hypervisorDriver->name, virConnectDriverTabCount);
if (setSharedDrivers) {
if (driver->interfaceDriver == NULL)
driver->interfaceDriver = virSharedInterfaceDriver;
if (driver->networkDriver == NULL)
driver->networkDriver = virSharedNetworkDriver;
if (driver->nodeDeviceDriver == NULL)
driver->nodeDeviceDriver = virSharedNodeDeviceDriver;
if (driver->nwfilterDriver == NULL)
driver->nwfilterDriver = virSharedNWFilterDriver;
if (driver->secretDriver == NULL)
driver->secretDriver = virSharedSecretDriver;
if (driver->storageDriver == NULL)
driver->storageDriver = virSharedStorageDriver;
}
virConnectDriverTab[virConnectDriverTabCount] = driver;
return virConnectDriverTabCount++;
}
/**
* virRegisterStateDriver:
* @driver: pointer to a driver block
*
* Register a virtualization driver
*
* Returns the driver priority or -1 in case of error.
*/
int
virRegisterStateDriver(virStateDriverPtr driver)
{
virCheckNonNullArgReturn(driver, -1);
if (virStateDriverTabCount >= MAX_DRIVERS) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Too many drivers, cannot register %s"),
driver->name);
return -1;
}
virStateDriverTab[virStateDriverTabCount] = driver;
return virStateDriverTabCount++;
}
/**
* virStateInitialize:
* @privileged: set to true if running with root privilege, false otherwise
* @callback: callback to invoke to inhibit shutdown of the daemon
* @opaque: data to pass to @callback
*
* Initialize all virtualization drivers. Accomplished in two phases,
* the first being state and structure initialization followed by any
* auto start supported by the driver. This is done to ensure dependencies
* that some drivers may have on another driver having been initialized
* will exist, such as the storage driver's need to use the secret driver.
*
* Returns 0 if all succeed, -1 upon any failure.
*/
int
virStateInitialize(bool privileged,
virStateInhibitCallback callback,
void *opaque)
{
size_t i;
if (virInitialize() < 0)
return -1;
for (i = 0; i < virStateDriverTabCount; i++) {
if (virStateDriverTab[i]->stateInitialize) {
VIR_DEBUG("Running global init for %s state driver",
virStateDriverTab[i]->name);
if (virStateDriverTab[i]->stateInitialize(privileged,
callback,
opaque) < 0) {
VIR_ERROR(_("Initialization of %s state driver failed: %s"),
virStateDriverTab[i]->name,
virGetLastErrorMessage());
return -1;
}
}
}
for (i = 0; i < virStateDriverTabCount; i++) {
if (virStateDriverTab[i]->stateAutoStart) {
VIR_DEBUG("Running global auto start for %s state driver",
virStateDriverTab[i]->name);
virStateDriverTab[i]->stateAutoStart();
}
}
return 0;
}
/**
* virStateCleanup:
*
* Run each virtualization driver's cleanup method.
*
* Returns 0 if all succeed, -1 upon any failure.
*/
int
virStateCleanup(void)
{
int r;
int ret = 0;
for (r = virStateDriverTabCount - 1; r >= 0; r--) {
if (virStateDriverTab[r]->stateCleanup &&
virStateDriverTab[r]->stateCleanup() < 0)
ret = -1;
}
return ret;
}
/**
* virStateReload:
*
* Run each virtualization driver's reload method.
*
* Returns 0 if all succeed, -1 upon any failure.
*/
int
virStateReload(void)
{
size_t i;
int ret = 0;
for (i = 0; i < virStateDriverTabCount; i++) {
if (virStateDriverTab[i]->stateReload &&
virStateDriverTab[i]->stateReload() < 0)
ret = -1;
}
return ret;
}
/**
* virStateStop:
*
* Run each virtualization driver's "stop" method.
*
* Returns 0 if successful, -1 on failure
*/
int
virStateStop(void)
{
size_t i;
int ret = 0;
for (i = 0; i < virStateDriverTabCount; i++) {
if (virStateDriverTab[i]->stateStop &&
virStateDriverTab[i]->stateStop())
ret = 1;
}
return ret;
}
/**
* virGetVersion:
* @libVer: return value for the library version (OUT)
Avoid virGetVersion failure on specific driver support configurations virGetVersion itself doesn't take a virConnectPtr, but in order to obtain the hypervisor version against which libvirt was compiled it is used in combination with virConnectGetType like this: hvType = virConnectGetType(conn) virGetVersion(&libVer, hvType, &typeVer) When virConnectGetType is called on a remote connection then the remote driver returns the type of the underlying driver on the server side, for example QEMU. Then virGetVersion compares hvType to a set of strings that depend on configure options and returns LIBVIR_VERSION_NUMBER in most cases. Now this fails in case libvirt on the client side is just compiled with the remote driver enabled only and the server side has the actual driver such as the QEMU driver. It just happens to work when the actual driver is enabled on client and server side. But that's not always true. I noticed this on FreeBSD: freebsd# virsh -c qemu+tcp://192.168.178.22/system version Compiled against library: libvir 0.9.2 error: failed to get the library version error: this function is not supported by the connection driver: virGetVersion This is not FreeBSD specific, happens on Windows as well due to the similar driver support configuration. The problem is that virConnectGetType returns QEMU, but virGetVersion on the client side only accepts Remote as hvType due to all other drivers being disabled on the client side. Daniel P. Berrange suggested to get rid of all the conditional code in virGetVersion, ignoring the hvType and always setting typeVer to LIBVIR_VERSION_NUMBER. virConnectGetVersion is supposed to be used to obtain the hypervisor version.
2011-06-01 08:29:13 +00:00
* @type: ignored; pass NULL
* @typeVer: pass NULL; for historical purposes duplicates @libVer if
* non-NULL
*
* Provides version information. @libVer is the version of the
* library and will always be set unless an error occurs, in which case
* an error code will be returned. @typeVer exists for historical
* compatibility; if it is not NULL it will duplicate @libVer (it was
* originally intended to return hypervisor information based on @type,
* but due to the design of remote clients this is not reliable). To
* get the version of the running hypervisor use the virConnectGetVersion()
Avoid virGetVersion failure on specific driver support configurations virGetVersion itself doesn't take a virConnectPtr, but in order to obtain the hypervisor version against which libvirt was compiled it is used in combination with virConnectGetType like this: hvType = virConnectGetType(conn) virGetVersion(&libVer, hvType, &typeVer) When virConnectGetType is called on a remote connection then the remote driver returns the type of the underlying driver on the server side, for example QEMU. Then virGetVersion compares hvType to a set of strings that depend on configure options and returns LIBVIR_VERSION_NUMBER in most cases. Now this fails in case libvirt on the client side is just compiled with the remote driver enabled only and the server side has the actual driver such as the QEMU driver. It just happens to work when the actual driver is enabled on client and server side. But that's not always true. I noticed this on FreeBSD: freebsd# virsh -c qemu+tcp://192.168.178.22/system version Compiled against library: libvir 0.9.2 error: failed to get the library version error: this function is not supported by the connection driver: virGetVersion This is not FreeBSD specific, happens on Windows as well due to the similar driver support configuration. The problem is that virConnectGetType returns QEMU, but virGetVersion on the client side only accepts Remote as hvType due to all other drivers being disabled on the client side. Daniel P. Berrange suggested to get rid of all the conditional code in virGetVersion, ignoring the hvType and always setting typeVer to LIBVIR_VERSION_NUMBER. virConnectGetVersion is supposed to be used to obtain the hypervisor version.
2011-06-01 08:29:13 +00:00
* function instead. To get the libvirt library version used by a
* connection use the virConnectGetLibVersion() instead.
*
* This function includes a call to virInitialize() when necessary.
*
* Returns -1 in case of failure, 0 otherwise, and values for @libVer and
* @typeVer have the format major * 1,000,000 + minor * 1,000 + release.
*/
int
Avoid virGetVersion failure on specific driver support configurations virGetVersion itself doesn't take a virConnectPtr, but in order to obtain the hypervisor version against which libvirt was compiled it is used in combination with virConnectGetType like this: hvType = virConnectGetType(conn) virGetVersion(&libVer, hvType, &typeVer) When virConnectGetType is called on a remote connection then the remote driver returns the type of the underlying driver on the server side, for example QEMU. Then virGetVersion compares hvType to a set of strings that depend on configure options and returns LIBVIR_VERSION_NUMBER in most cases. Now this fails in case libvirt on the client side is just compiled with the remote driver enabled only and the server side has the actual driver such as the QEMU driver. It just happens to work when the actual driver is enabled on client and server side. But that's not always true. I noticed this on FreeBSD: freebsd# virsh -c qemu+tcp://192.168.178.22/system version Compiled against library: libvir 0.9.2 error: failed to get the library version error: this function is not supported by the connection driver: virGetVersion This is not FreeBSD specific, happens on Windows as well due to the similar driver support configuration. The problem is that virConnectGetType returns QEMU, but virGetVersion on the client side only accepts Remote as hvType due to all other drivers being disabled on the client side. Daniel P. Berrange suggested to get rid of all the conditional code in virGetVersion, ignoring the hvType and always setting typeVer to LIBVIR_VERSION_NUMBER. virConnectGetVersion is supposed to be used to obtain the hypervisor version.
2011-06-01 08:29:13 +00:00
virGetVersion(unsigned long *libVer, const char *type ATTRIBUTE_UNUSED,
unsigned long *typeVer)
{
if (virInitialize() < 0)
goto error;
VIR_DEBUG("libVir=%p, type=%s, typeVer=%p", libVer, type, typeVer);
virResetLastError();
if (libVer == NULL)
goto error;
*libVer = LIBVIR_VERSION_NUMBER;
Avoid virGetVersion failure on specific driver support configurations virGetVersion itself doesn't take a virConnectPtr, but in order to obtain the hypervisor version against which libvirt was compiled it is used in combination with virConnectGetType like this: hvType = virConnectGetType(conn) virGetVersion(&libVer, hvType, &typeVer) When virConnectGetType is called on a remote connection then the remote driver returns the type of the underlying driver on the server side, for example QEMU. Then virGetVersion compares hvType to a set of strings that depend on configure options and returns LIBVIR_VERSION_NUMBER in most cases. Now this fails in case libvirt on the client side is just compiled with the remote driver enabled only and the server side has the actual driver such as the QEMU driver. It just happens to work when the actual driver is enabled on client and server side. But that's not always true. I noticed this on FreeBSD: freebsd# virsh -c qemu+tcp://192.168.178.22/system version Compiled against library: libvir 0.9.2 error: failed to get the library version error: this function is not supported by the connection driver: virGetVersion This is not FreeBSD specific, happens on Windows as well due to the similar driver support configuration. The problem is that virConnectGetType returns QEMU, but virGetVersion on the client side only accepts Remote as hvType due to all other drivers being disabled on the client side. Daniel P. Berrange suggested to get rid of all the conditional code in virGetVersion, ignoring the hvType and always setting typeVer to LIBVIR_VERSION_NUMBER. virConnectGetVersion is supposed to be used to obtain the hypervisor version.
2011-06-01 08:29:13 +00:00
if (typeVer != NULL)
*typeVer = LIBVIR_VERSION_NUMBER;
return 0;
error:
virDispatchError(NULL);
return -1;
}
static int
virConnectGetDefaultURI(virConfPtr conf,
char **name)
{
int ret = -1;
const char *defname = virGetEnvBlockSUID("LIBVIRT_DEFAULT_URI");
if (defname && *defname) {
VIR_DEBUG("Using LIBVIRT_DEFAULT_URI '%s'", defname);
if (VIR_STRDUP(*name, defname) < 0)
goto cleanup;
} else {
if (virConfGetValueString(conf, "uri_default", name) < 0)
goto cleanup;
if (*name)
VIR_DEBUG("Using config file uri '%s'", *name);
}
ret = 0;
cleanup:
return ret;
}
/*
* Check to see if an invalid URI like qemu://system (missing /) was passed,
* offer the suggested fix.
*/
static int
virConnectCheckURIMissingSlash(const char *uristr, virURIPtr uri)
{
if (!uri->scheme || !uri->path || !uri->server)
return 0;
/* To avoid false positives, only check drivers that mandate
a path component in the URI, like /system or /session */
if (STRNEQ(uri->scheme, "qemu") &&
STRNEQ(uri->scheme, "vbox") &&
STRNEQ(uri->scheme, "vz"))
return 0;
if (STREQ(uri->server, "session") ||
STREQ(uri->server, "system")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid URI %s (maybe you want %s:///%s)"),
uristr, uri->scheme, uri->server);
return -1;
}
return 0;
}
static virConnectPtr
virConnectOpenInternal(const char *name,
virConnectAuthPtr auth,
unsigned int flags)
{
size_t i;
int res;
virConnectPtr ret;
virConfPtr conf = NULL;
char *uristr = NULL;
ret = virGetConnect();
if (ret == NULL)
return NULL;
if (virConfLoadConfig(&conf, "libvirt.conf") < 0)
goto failed;
if (name && name[0] == '\0')
name = NULL;
if (!name && virIsSUID()) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("An explicit URI must be provided when setuid"));
goto failed;
}
/* Convert xen -> xen:/// for back compat */
if (name && STRCASEEQ(name, "xen"))
name = "xen:///";
/* Convert xen:// -> xen:/// because xmlParseURI cannot parse the
* former. This allows URIs such as xen://localhost to work.
*/
if (name && STREQ(name, "xen://"))
name = "xen:///";
/*
* If no URI is passed, then check for an environment string if not
* available probe the compiled in drivers to find a default hypervisor
* if detectable.
*/
if (name) {
if (VIR_STRDUP(uristr, name) < 0)
goto failed;
} else {
if (virConnectGetDefaultURI(conf, &uristr) < 0)
goto failed;
}
if (uristr) {
char *alias = NULL;
if (!(flags & VIR_CONNECT_NO_ALIASES) &&
virURIResolveAlias(conf, uristr, &alias) < 0)
goto failed;
if (alias) {
VIR_FREE(uristr);
uristr = alias;
}
if (!(ret->uri = virURIParse(uristr))) {
VIR_FREE(alias);
goto failed;
}
VIR_DEBUG("Split \"%s\" to URI components:\n"
" scheme %s\n"
" server %s\n"
" user %s\n"
" port %d\n"
" path %s",
uristr,
NULLSTR(ret->uri->scheme), NULLSTR(ret->uri->server),
NULLSTR(ret->uri->user), ret->uri->port,
NULLSTR(ret->uri->path));
if (virConnectCheckURIMissingSlash(uristr,
ret->uri) < 0) {
goto failed;
}
} else {
VIR_DEBUG("no name, allowing driver auto-select");
}
/* Cleansing flags */
ret->flags = flags & VIR_CONNECT_RO;
for (i = 0; i < virConnectDriverTabCount; i++) {
/* We're going to probe the remote driver next. So we have already
* probed all other client-side-only driver before, but none of them
* accepted the URI.
* If the scheme corresponds to a known but disabled client-side-only
* driver then report a useful error, instead of a cryptic one about
* not being able to connect to libvirtd or not being able to find
* certificates. */
if (STREQ(virConnectDriverTab[i]->hypervisorDriver->name, "remote") &&
ret->uri != NULL && ret->uri->scheme != NULL &&
(
#ifndef WITH_PHYP
STRCASEEQ(ret->uri->scheme, "phyp") ||
#endif
#ifndef WITH_ESX
STRCASEEQ(ret->uri->scheme, "vpx") ||
STRCASEEQ(ret->uri->scheme, "esx") ||
STRCASEEQ(ret->uri->scheme, "gsx") ||
2011-07-13 14:47:01 +00:00
#endif
#ifndef WITH_HYPERV
STRCASEEQ(ret->uri->scheme, "hyperv") ||
#endif
#ifndef WITH_XENAPI
STRCASEEQ(ret->uri->scheme, "xenapi") ||
#endif
#ifndef WITH_VZ
STRCASEEQ(ret->uri->scheme, "parallels") ||
#endif
false)) {
virReportErrorHelper(VIR_FROM_NONE, VIR_ERR_CONFIG_UNSUPPORTED,
__FILE__, __FUNCTION__, __LINE__,
_("libvirt was built without the '%s' driver"),
ret->uri->scheme);
goto failed;
}
VIR_DEBUG("trying driver %zu (%s) ...",
i, virConnectDriverTab[i]->hypervisorDriver->name);
ret->driver = virConnectDriverTab[i]->hypervisorDriver;
ret->interfaceDriver = virConnectDriverTab[i]->interfaceDriver;
ret->networkDriver = virConnectDriverTab[i]->networkDriver;
ret->nodeDeviceDriver = virConnectDriverTab[i]->nodeDeviceDriver;
ret->nwfilterDriver = virConnectDriverTab[i]->nwfilterDriver;
ret->secretDriver = virConnectDriverTab[i]->secretDriver;
ret->storageDriver = virConnectDriverTab[i]->storageDriver;
res = virConnectDriverTab[i]->hypervisorDriver->connectOpen(ret, auth, conf, flags);
VIR_DEBUG("driver %zu %s returned %s",
i, virConnectDriverTab[i]->hypervisorDriver->name,
res == VIR_DRV_OPEN_SUCCESS ? "SUCCESS" :
(res == VIR_DRV_OPEN_DECLINED ? "DECLINED" :
(res == VIR_DRV_OPEN_ERROR ? "ERROR" : "unknown status")));
if (res == VIR_DRV_OPEN_SUCCESS) {
break;
} else {
ret->driver = NULL;
ret->interfaceDriver = NULL;
ret->networkDriver = NULL;
ret->nodeDeviceDriver = NULL;
ret->nwfilterDriver = NULL;
ret->secretDriver = NULL;
ret->storageDriver = NULL;
if (res == VIR_DRV_OPEN_ERROR)
goto failed;
}
}
if (!ret->driver) {
/* If we reach here, then all drivers declined the connection. */
virReportError(VIR_ERR_NO_CONNECT, "%s", NULLSTR(name));
goto failed;
}
virConfFree(conf);
VIR_FREE(uristr);
return ret;
failed:
VIR_FREE(uristr);
virConfFree(conf);
virObjectUnref(ret);
return NULL;
}
/**
* virConnectOpen:
* @name: (optional) URI of the hypervisor
*
* This function should be called first to get a connection to the
* Hypervisor and xen store
*
* If @name is NULL, if the LIBVIRT_DEFAULT_URI environment variable is set,
* then it will be used. Otherwise if the client configuration file
* has the "uri_default" parameter set, then it will be used. Finally
* probing will be done to determine a suitable default driver to activate.
* This involves trying each hypervisor in turn until one successfully opens.
*
* If connecting to an unprivileged hypervisor driver which requires
* the libvirtd daemon to be active, it will automatically be launched
* if not already running. This can be prevented by setting the
* environment variable LIBVIRT_AUTOSTART=0
*
* URIs are documented at httsp://libvirt.org/uri.html
*
* virConnectClose should be used to release the resources after the connection
* is no longer needed.
*
* Returns a pointer to the hypervisor connection or NULL in case of error
*/
virConnectPtr
virConnectOpen(const char *name)
{
virConnectPtr ret = NULL;
if (virInitialize() < 0)
goto error;
VIR_DEBUG("name=%s", NULLSTR(name));
virResetLastError();
ret = virConnectOpenInternal(name, NULL, 0);
if (!ret)
goto error;
return ret;
error:
virDispatchError(NULL);
return NULL;
}
/**
* virConnectOpenReadOnly:
* @name: (optional) URI of the hypervisor
*
* This function should be called first to get a restricted connection to the
* library functionalities. The set of APIs usable are then restricted
* on the available methods to control the domains.
*
* See virConnectOpen for notes about environment variables which can
* have an effect on opening drivers and freeing the connection resources
*
* URIs are documented at https://libvirt.org/uri.html
*
* Returns a pointer to the hypervisor connection or NULL in case of error
*/
virConnectPtr
virConnectOpenReadOnly(const char *name)
{
virConnectPtr ret = NULL;
if (virInitialize() < 0)
goto error;
VIR_DEBUG("name=%s", NULLSTR(name));
virResetLastError();
ret = virConnectOpenInternal(name, NULL, VIR_CONNECT_RO);
if (!ret)
goto error;
return ret;
error:
virDispatchError(NULL);
return NULL;
}
/**
* virConnectOpenAuth:
* @name: (optional) URI of the hypervisor
* @auth: Authenticate callback parameters
* @flags: bitwise-OR of virConnectFlags
*
* This function should be called first to get a connection to the
* Hypervisor. If necessary, authentication will be performed fetching
* credentials via the callback
*
* See virConnectOpen for notes about environment variables which can
* have an effect on opening drivers and freeing the connection resources
*
* URIs are documented at https://libvirt.org/uri.html
*
* Returns a pointer to the hypervisor connection or NULL in case of error
*/
virConnectPtr
virConnectOpenAuth(const char *name,
virConnectAuthPtr auth,
unsigned int flags)
{
virConnectPtr ret = NULL;
if (virInitialize() < 0)
goto error;
VIR_DEBUG("name=%s, auth=%p, flags=0x%x", NULLSTR(name), auth, flags);
virResetLastError();
ret = virConnectOpenInternal(name, auth, flags);
if (!ret)
goto error;
return ret;
error:
virDispatchError(NULL);
return NULL;
2005-11-02 12:50:21 +00:00
}
2005-11-02 12:50:21 +00:00
/**
* virConnectClose:
2005-11-02 12:50:21 +00:00
* @conn: pointer to the hypervisor connection
*
* This function closes the connection to the Hypervisor. This should
* not be called if further interaction with the Hypervisor are needed
* especially if there is running domain which need further monitoring by
* the application.
*
* Connections are reference counted; the count is explicitly
* increased by the initial open (virConnectOpen, virConnectOpenAuth,
* and the like) as well as virConnectRef; it is also temporarily
* increased by other API that depend on the connection remaining
* alive. The open and every virConnectRef call should have a
* matching virConnectClose, and all other references will be released
* after the corresponding operation completes.
*
* Returns a positive number if at least 1 reference remains on
* success. The returned value should not be assumed to be the total
* reference count. A return of 0 implies no references remain and
* the connection is closed and memory has been freed. A return of -1
* implies a failure.
*
* It is possible for the last virConnectClose to return a positive
* value if some other object still has a temporary reference to the
* connection, but the application should not try to further use a
* connection after the virConnectClose that matches the initial open.
2005-11-02 12:50:21 +00:00
*/
int
virConnectClose(virConnectPtr conn)
{
VIR_DEBUG("conn=%p", conn);
2009-01-20 12:01:45 +00:00
virResetLastError();
virCheckConnectReturn(conn, -1);
if (!virObjectUnref(conn))
return 0;
return 1;
2005-11-02 12:50:21 +00:00
}
/* Helper function called to validate incoming client array on any
* interface that sets typed parameters in the hypervisor. */
int
virTypedParameterValidateSet(virConnectPtr conn,
virTypedParameterPtr params,
int nparams)
{
bool string_okay;
size_t i;
string_okay = VIR_DRV_SUPPORTS_FEATURE(conn->driver,
conn,
VIR_DRV_FEATURE_TYPED_PARAM_STRING);
for (i = 0; i < nparams; i++) {
if (strnlen(params[i].field, VIR_TYPED_PARAM_FIELD_LENGTH) ==
VIR_TYPED_PARAM_FIELD_LENGTH) {
virReportInvalidArg(params,
_("string parameter name '%.*s' too long"),
VIR_TYPED_PARAM_FIELD_LENGTH,
params[i].field);
return -1;
}
if (params[i].type == VIR_TYPED_PARAM_STRING) {
if (string_okay) {
if (!params[i].value.s) {
virReportInvalidArg(params,
_("NULL string parameter '%s'"),
params[i].field);
return -1;
}
} else {
virReportInvalidArg(params,
_("string parameter '%s' unsupported"),
params[i].field);
return -1;
}
}
}
return 0;
}