/* * libvirt.c: Main interfaces for the libvirt library to handle virtualization * domains from a process running in domain 0 * * Copyright (C) 2005-2006, 2008-2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include #include #include #include #include #include #include #include #include #ifdef WITH_CURL # include #endif #include "virerror.h" #include "virlog.h" #include "datatypes.h" #include "driver.h" #include "viruuid.h" #include "viralloc.h" #include "configmake.h" #include "virconf.h" #include "rpc/virnettlscontext.h" #include "vircommand.h" #include "virevent.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_ESX # include "esx/esx_driver.h" #endif #ifdef WITH_HYPERV # include "hyperv/hyperv_driver.h" #endif #ifdef WITH_BHYVE # include "bhyve/bhyve_driver.h" #endif #include "access/viraccessmanager.h" #define VIR_FROM_THIS VIR_FROM_NONE VIR_LOG_INIT("libvirt"); /* * TODO: * - use lock to protect against concurrent accesses ? * - use reference counting to guarantee coherent pointer state ? */ #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; static int virConnectAuthCallbackDefault(virConnectCredentialPtr cred, unsigned int ncred, void *cbdata G_GNUC_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; /* * Ignore & carry on. Although we can't auth * directly, the user may have authenticated * themselves already outside context of libvirt */ break; } 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 = virGetPassword(); if (STREQ(bufptr, "")) VIR_FREE(bufptr); break; default: return -1; } if (cred[i].type != VIR_CRED_EXTERNAL) { cred[i].result = bufptr ? bufptr : g_strdup(cred[i].defresult ? cred[i].defresult : ""); 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; 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. */ if (virErrorInitialize() < 0) goto error; virFileActivateDirOverrideForLib(); if (getuid() != geteuid() || getgid() != getegid()) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("libvirt.so is not safe to use from setuid/setgid programs")); goto error; } virLogSetFromEnv(); virNetTLSInit(); #if WITH_CURL curl_global_init(CURL_GLOBAL_DEFAULT); #endif VIR_DEBUG("register drivers"); g_networking_init(); #ifdef HAVE_LIBINTL_H if (!bindtextdomain(PACKAGE, LOCALEDIR)) goto error; #endif /* HAVE_LIBINTL_H */ /* * 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_ESX if (esxRegister() == -1) goto error; #endif #ifdef WITH_HYPERV if (hypervRegister() == -1) goto error; #endif #ifdef WITH_REMOTE if (remoteRegister() == -1) goto error; #endif 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; } #ifdef WIN32 BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID ignore); BOOL WINAPI DllMain(HINSTANCE instance G_GNUC_UNUSED, DWORD reason, LPVOID ignore G_GNUC_UNUSED) { switch (reason) { case DLL_PROCESS_ATTACH: virInitialize(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: /* Nothing todo in libvirt yet */ 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); 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); 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; } 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++; } /** * virHasDriverForURIScheme: * @scheme: the URI scheme * * Determine if there is a driver registered that explicitly * handles URIs with the scheme @scheme. * * Returns: true if a driver is registered */ bool virHasDriverForURIScheme(const char *scheme) { size_t i; size_t j; for (i = 0; i < virConnectDriverTabCount; i++) { if (!virConnectDriverTab[i]->uriSchemes) continue; for (j = 0; virConnectDriverTab[i]->uriSchemes[j]; j++) { if (STREQ(virConnectDriverTab[i]->uriSchemes[j], scheme)) return true; } } return false; } /** * 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 * @mandatory: set to true if all drivers must report success, not skipped * @root: directory to use for embedded mode * @callback: callback to invoke to inhibit shutdown of the daemon * @opaque: data to pass to @callback * * Initialize all virtualization drivers. * * Passing a non-NULL @root instructs the driver to run in embedded mode. * Instead of using the compile time $prefix as the basis for directory * paths, @root should be used instead. In addition any '/libvirt' * component of the paths should be stripped. * * eg consider a build with prefix=/usr/local. A driver might use the * locations * * /usr/local/etc/libvirt/$DRIVER/ * /usr/local/var/lib/libvirt/$DRIVER/ * /usr/local/run/libvirt/$DRIVER/ * * When run with @root, the locations should instead be * * @root/etc/$DRIVER/ * @root/var/lib/$DRIVER/ * @root/run/$DRIVER/ * * Returns 0 if all succeed, -1 upon any failure. */ int virStateInitialize(bool privileged, bool mandatory, const char *root, virStateInhibitCallback callback, void *opaque) { size_t i; if (virInitialize() < 0) return -1; for (i = 0; i < virStateDriverTabCount; i++) { if (virStateDriverTab[i]->stateInitialize && !virStateDriverTab[i]->initialized) { virDrvStateInitResult ret; VIR_DEBUG("Running global init for %s state driver", virStateDriverTab[i]->name); virStateDriverTab[i]->initialized = true; ret = virStateDriverTab[i]->stateInitialize(privileged, root, callback, opaque); VIR_DEBUG("State init result %d (mandatory=%d)", ret, mandatory); if (ret == VIR_DRV_STATE_INIT_ERROR) { VIR_ERROR(_("Initialization of %s state driver failed: %s"), virStateDriverTab[i]->name, virGetLastErrorMessage()); return -1; } else if (ret == VIR_DRV_STATE_INIT_SKIPPED && mandatory) { VIR_ERROR(_("Initialization of mandatory %s state driver skipped"), virStateDriverTab[i]->name); return -1; } } } 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) * @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() * 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 virGetVersion(unsigned long *libVer, const char *type G_GNUC_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; if (typeVer != NULL) *typeVer = LIBVIR_VERSION_NUMBER; return 0; error: virDispatchError(NULL); return -1; } static int virConnectGetDefaultURI(virConfPtr conf, char **name) { const char *defname = getenv("LIBVIRT_DEFAULT_URI"); if (defname && *defname) { VIR_DEBUG("Using LIBVIRT_DEFAULT_URI '%s'", defname); *name = g_strdup(defname); } else { if (virConfGetValueString(conf, "uri_default", name) < 0) return -1; if (*name) VIR_DEBUG("Using config file uri '%s'", *name); } return 0; } /* * 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->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; g_autoptr(virConf) conf = NULL; char *uristr = NULL; bool embed = false; ret = virGetConnect(); if (ret == NULL) return NULL; if (virConfLoadConfig(&conf, "libvirt.conf") < 0) goto failed; if (name && name[0] == '\0') name = NULL; /* Convert xen -> xen:///system for back compat */ if (name && STRCASEEQ(name, "xen")) name = "xen:///system"; /* Convert xen:// -> xen:///system because xmlParseURI cannot parse the * former. This allows URIs such as xen://localhost to work. */ if (name && STREQ(name, "xen://")) name = "xen:///system"; /* * 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) { uristr = g_strdup(name); } else { if (virConnectGetDefaultURI(conf, &uristr) < 0) goto failed; if (uristr == NULL) { VIR_DEBUG("Trying to probe for default URI"); for (i = 0; i < virConnectDriverTabCount && uristr == NULL; i++) { if (virConnectDriverTab[i]->hypervisorDriver->connectURIProbe) { if (virConnectDriverTab[i]->hypervisorDriver->connectURIProbe(&uristr) < 0) goto failed; VIR_DEBUG("%s driver URI probe returned '%s'", virConnectDriverTab[i]->hypervisorDriver->name, NULLSTR(uristr)); } } } } 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; } /* Avoid need for drivers to worry about NULLs, as * no one needs to distinguish "" vs NULL */ if (ret->uri->path == NULL) ret->uri->path = g_strdup(""); 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, ret->uri->path); if (ret->uri->scheme == NULL) { virReportError(VIR_ERR_NO_CONNECT, _("URI '%s' does not include a driver name"), name); goto failed; } if (virConnectCheckURIMissingSlash(uristr, ret->uri) < 0) { goto failed; } if (STREQ(ret->uri->path, "/embed")) { const char *root = NULL; g_autofree char *regMethod = NULL; VIR_DEBUG("URI path requests %s driver embedded mode", ret->uri->scheme); if (strspn(ret->uri->scheme, "abcdefghijklmnopqrstuvwxyz") != strlen(ret->uri->scheme)) { virReportError(VIR_ERR_NO_CONNECT, _("URI scheme '%s' for embedded driver is not valid"), ret->uri->scheme); goto failed; } root = virURIGetParam(ret->uri, "root"); if (!root) goto failed; if (virEventRequireImpl() < 0) goto failed; regMethod = g_strdup_printf("%sRegister", ret->uri->scheme); if (virDriverLoadModule(ret->uri->scheme, regMethod, false) < 0) goto failed; if (virAccessManagerGetDefault() == NULL) { virAccessManagerPtr acl; virResetLastError(); if (!(acl = virAccessManagerNew("none"))) goto failed; virAccessManagerSetDefault(acl); } if (virStateInitialize(geteuid() == 0, true, root, NULL, NULL) < 0) goto failed; embed = true; } } 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 && ( #ifndef WITH_ESX STRCASEEQ(ret->uri->scheme, "vpx") || STRCASEEQ(ret->uri->scheme, "esx") || STRCASEEQ(ret->uri->scheme, "gsx") || #endif #ifndef WITH_HYPERV STRCASEEQ(ret->uri->scheme, "hyperv") || #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); if (virConnectDriverTab[i]->localOnly && ret->uri && ret->uri->server) { VIR_DEBUG("Server present, skipping local only driver"); continue; } /* Filter drivers based on declared URI schemes */ if (virConnectDriverTab[i]->uriSchemes) { bool matchScheme = false; size_t s; if (!ret->uri) { VIR_DEBUG("No URI, skipping driver with URI whitelist"); continue; } if (embed && !virConnectDriverTab[i]->embeddable) { VIR_DEBUG("Ignoring non-embeddable driver %s", virConnectDriverTab[i]->hypervisorDriver->name); continue; } VIR_DEBUG("Checking for supported URI schemes"); for (s = 0; virConnectDriverTab[i]->uriSchemes[s] != NULL; s++) { if (STREQ(ret->uri->scheme, virConnectDriverTab[i]->uriSchemes[s])) { VIR_DEBUG("Matched URI scheme '%s'", ret->uri->scheme); matchScheme = true; break; } } if (!matchScheme) { VIR_DEBUG("No matching URI scheme"); continue; } } else { if (embed) { VIR_DEBUG("Skipping wildcard for embedded URI"); continue; } else { VIR_DEBUG("Matching any URI scheme for '%s'", ret->uri ? ret->uri->scheme : ""); } } if (embed && !virConnectDriverTab[i]->embeddable) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Driver %s cannot be used in embedded mode"), virConnectDriverTab[i]->hypervisorDriver->name); goto failed; } /* before starting the new connection, check if the driver only works * with a server, and so return an error if the server is missing */ if (virConnectDriverTab[i]->remoteOnly && ret->uri && !ret->uri->server) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("URI is missing the server part")); goto failed; } 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; } VIR_FREE(uristr); return ret; failed: VIR_FREE(uristr); 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 https://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; } /** * virConnectClose: * @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. */ int virConnectClose(virConnectPtr conn) { VIR_DEBUG("conn=%p", conn); virResetLastError(); virCheckConnectReturn(conn, -1); if (!virObjectUnref(conn)) return 0; return 1; } /* 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; }