From 777ffbd0e2f921c0f71dad4d8839b1fdc3be5f3d Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 13 Oct 2011 11:49:45 +0100 Subject: [PATCH] Allow for URI aliases when connecting to libvirt This adds support for a libvirt client configuration file either /etc/libvirt/libvirt.conf for privileged clients, or $HOME/.libvirt/libvirt.conf for unprivileged clients. It allows one parameter uri_aliases = [ "hail=qemu+ssh://root@hail.cloud.example.com/system", "sleet=qemu+ssh://root@sleet.cloud.example.com/system", ] Any call to virConnectOpen with a non-NULL URI will first attempt to match against the uri_aliases list. An application can disable this by using VIR_CONNECT_NO_ALIASES * docs/uri.html.in: Document URI aliases * include/libvirt/libvirt.h.in: Add VIR_CONNECT_NO_ALIASES * libvirt.spec.in, mingw32-libvirt.spec.in: Add /etc/libvirt/libvirt.conf * src/Makefile.am: Install default config file * src/libvirt.c: Add support for URI aliases * src/remote/remote_driver.c: Don't try to handle URIs with no scheme and which clearly are not paths * src/util/conf.c: Don't raise error on virConfFree(NULL) * src/xen/xen_driver.c: Don't raise error on URIs with no scheme --- docs/uri.html.in | 103 ++++++++++++----------- include/libvirt/libvirt.h.in | 3 +- libvirt.spec.in | 1 + mingw32-libvirt.spec.in | 4 +- src/Makefile.am | 2 +- src/libvirt.c | 158 +++++++++++++++++++++++++++++++---- src/libvirt.conf | 12 +++ src/remote/remote_driver.c | 5 ++ src/util/conf.c | 6 +- src/xen/xen_driver.c | 12 +-- 10 files changed, 226 insertions(+), 80 deletions(-) create mode 100644 src/libvirt.conf diff --git a/docs/uri.html.in b/docs/uri.html.in index e6326b2307..79f878678c 100644 --- a/docs/uri.html.in +++ b/docs/uri.html.in @@ -2,6 +2,8 @@

Connection URIs

+ +

Since libvirt supports many different kinds of virtualization (often referred to as "drivers" or "hypervisors"), we need a @@ -13,41 +15,46 @@ machine over the network. To this end, libvirt uses URIs as used on the Web and as defined in RFC 2396. This page documents libvirt URIs.

- -

- Specifying URIs to libvirt -

+

Specifying URIs to libvirt

+

The URI is passed as the name parameter to virConnectOpen or virConnectOpenReadOnly. For example:

 virConnectPtr conn = virConnectOpenReadOnly ("test:///default");
 
-

+

+ Configuring URI aliases +

+ +

+To simplify life for administrators, it is possible to setup URI aliases in a +libvirt client configuration file. The configuration file is /etc/libvirt/libvirt.conf +for the root user, or $HOME/.libvirt/libvirt.conf for any unprivileged user. +In this file, the following syntax can be used to setup aliases +

+ +
+uri_aliases = [
+  "hail=qemu+ssh://root@hail.cloud.example.com/system",
+  "sleet=qemu+ssh://root@sleet.cloud.example.com/system",
+]
+
+ +

+ A URI alias should be a string made up from the characters + a-Z, 0-9, _, -. Following the = + can be any libvirt URI string, including arbitrary URI parameters. + URI aliases will apply to any application opening a libvirt + connection, unless it has explicitly passed the VIR_CONNECT_NO_ALIASES + parameter to virConnectOpenAuth. If the passed in + URI contains characters outside the allowed alias character + set, no alias lookup will be attempted. +

+ +

Specifying URIs to virsh, virt-manager and virt-install -

+

In virsh use the -c or --connect option:

@@ -76,9 +83,9 @@ In virt-install use the --connect=URI option:
 virt-install --connect=test:///default [other options]
 
-

+

xen:/// URI -

+

This section describes a feature which is new in libvirt > 0.2.3. For libvirt ≤ 0.2.3 use "xen". @@ -87,9 +94,9 @@ virt-install --connect=test:///default [other options] To access a Xen hypervisor running on the local machine use the URI xen:///.

-

+

qemu:///... QEMU and KVM URIs -

+

To use QEMU support in libvirt you must be running the libvirtd daemon (named libvirt_qemud @@ -119,9 +126,9 @@ KVM URIs are identical. You select between qemu, qemu accelerated and KVM guests in the guest XML as described here.

-

+

Remote URIs -

+

Remote URIs are formed by taking ordinary local URIs and adding a hostname and/or transport name. As a special case, using a URI @@ -182,9 +189,9 @@ We refer you to the libvirt remote URI reference and full documentation for libvirt remote support.

-

+

test:///... Test URIs -

+

The test driver is a dummy hypervisor for test purposes. The URIs supported are: @@ -196,12 +203,12 @@ host definitions built into the driver. a set of host definitions held in the named file. -

+

Other & legacy URI formats -

-

+

+

NULL and empty string URIs -

+

Libvirt allows you to pass a NULL pointer to virConnectOpen*. Empty string ("") acts in @@ -223,9 +230,9 @@ the user to type a URI in directly (if that is appropriate). If your application wishes to connect specifically to a Xen hypervisor, then for future proofing it should choose a full xen:/// URI.

-

+

File paths (xend-unix-server) -

+

If XenD is running and configured in /etc/xen/xend-config.sxp:

@@ -240,9 +247,9 @@ using a file URI such as:
 virsh -c ///var/run/xend/xend-socket
 
-

+

Legacy: http://... (xend-http-server) -

+

If XenD is running and configured in /etc/xen/xend-config.sxp: @@ -276,17 +283,17 @@ Notes: libvirt, only the old-style sexpr interface known in the Xen documentation as "unix server" or "http server". -

+

Legacy: "xen" -

+

Another legacy URI is to specify name as the string "xen". This will continue to refer to the Xen hypervisor. However you should prefer a full xen:/// URI in all future code.

-

+

Legacy: Xen proxy -

+

Libvirt continues to support connections to a separately running Xen proxy daemon. This provides a way to allow non-root users to make a diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 5f6a07a4dc..361881a018 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -844,7 +844,8 @@ typedef virNodeMemoryStats *virNodeMemoryStatsPtr; * Flags when opening a connection to a hypervisor */ typedef enum { - VIR_CONNECT_RO = 1, /* A readonly connection */ + VIR_CONNECT_RO = (1 << 0), /* A readonly connection */ + VIR_CONNECT_NO_ALIASES = (1 << 1), /* Don't try to resolve URI aliases */ } virConnectFlags; diff --git a/libvirt.spec.in b/libvirt.spec.in index 03bd7c72e8..262cfed044 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1084,6 +1084,7 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd %defattr(-, root, root) %doc AUTHORS ChangeLog.gz NEWS README COPYING.LIB TODO +%config(noreplace) %{_sysconfdir}/libvirt/libvirt.conf %{_mandir}/man1/virsh.1* %{_mandir}/man1/virt-xml-validate.1* %{_mandir}/man1/virt-pki-validate.1* diff --git a/mingw32-libvirt.spec.in b/mingw32-libvirt.spec.in index 521790ce9b..c2690f34dc 100644 --- a/mingw32-libvirt.spec.in +++ b/mingw32-libvirt.spec.in @@ -110,7 +110,7 @@ rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install -rm -rf $RPM_BUILD_ROOT%{_mingw32_sysconfdir}/libvirt +rm -rf $RPM_BUILD_ROOT%{_mingw32_sysconfdir}/libvirt/nwfilter rm -rf $RPM_BUILD_ROOT%{_mingw32_datadir}/doc/* rm -rf $RPM_BUILD_ROOT%{_mingw32_datadir}/gtk-doc/* @@ -126,6 +126,8 @@ rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) +%config(noreplace) %{_mingw32_sysconfdir}/libvirt/libvirt.conf + %{_mingw32_bindir}/libvirt-0.dll %{_mingw32_bindir}/virsh.exe %{_mingw32_bindir}/virt-xml-validate diff --git a/src/Makefile.am b/src/Makefile.am index 87d91ede79..07f27d2522 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,7 +39,7 @@ moddir = $(libdir)/libvirt/connection-driver mod_LTLIBRARIES = confdir = $(sysconfdir)/libvirt -conf_DATA = +conf_DATA = libvirt.conf augeasdir = $(datadir)/augeas/lenses augeas_DATA = diff --git a/src/libvirt.c b/src/libvirt.c index 69cffc5431..0b975daecc 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -40,6 +40,7 @@ #include "memory.h" #include "configmake.h" #include "intprops.h" +#include "conf.h" #include "rpc/virnettlscontext.h" #ifndef WITH_DRIVER_MODULES @@ -968,6 +969,126 @@ error: return -1; } +static char * +virConnectConfigFile(void) +{ + char *path; + if (geteuid() == 0) { + if (virAsprintf(&path, "%s/libvirt/libvirt.conf", + SYSCONFDIR) < 0) + goto no_memory; + } else { + char *userdir = virGetUserDirectory(geteuid()); + if (!userdir) + goto error; + + if (virAsprintf(&path, "%s/.libvirt/libvirt.conf", + userdir) < 0) + goto no_memory; + } + + return path; + +no_memory: + virReportOOMError(); +error: + return NULL; +} + +#define URI_ALIAS_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-" + +static int +virConnectOpenFindURIAliasMatch(virConfValuePtr value, const char *alias, char **uri) +{ + virConfValuePtr entry; + if (value->type != VIR_CONF_LIST) { + virLibConnError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected a list for 'uri_aliases' config parameter")); + return -1; + } + + entry = value->list; + while (entry) { + char *offset; + size_t safe; + + if (entry->type != VIR_CONF_STRING) { + virLibConnError(VIR_ERR_CONF_SYNTAX, "%s", + _("Expected a string for 'uri_aliases' config parameter list entry")); + return -1; + } + + if (!(offset = strchr(entry->str, '='))) { + virLibConnError(VIR_ERR_CONF_SYNTAX, + _("Malformed 'uri_aliases' config entry '%s', expected 'alias=uri://host/path'"), + entry->str); + return -1; + } + + safe = strspn(entry->str, URI_ALIAS_CHARS); + if (safe < (offset - entry->str)) { + virLibConnError(VIR_ERR_CONF_SYNTAX, + _("Malformed 'uri_aliases' config entry '%s', aliases may only container 'a-Z, 0-9, _, -'"), + entry->str); + return -1; + } + + if (STREQLEN(entry->str, alias, offset-entry->str)) { + VIR_DEBUG("Resolved alias '%s' to '%s'", + alias, offset+1); + if (!(*uri = strdup(offset+1))) { + virReportOOMError(); + return -1; + } + return 0; + } + + entry = entry->next; + } + + VIR_DEBUG("No alias found for '%s', passing through to drivers", + alias); + return 0; +} + +static int +virConnectOpenResolveURIAlias(const char *alias, char **uri) +{ + char *config = NULL; + int ret = -1; + virConfPtr conf = NULL; + virConfValuePtr value = NULL; + + *uri = NULL; + + /* Short circuit to avoid doing URI alias resolution + * when it clearly isn't an valid alias */ + if (strspn(alias, URI_ALIAS_CHARS) != strlen(alias)) + return 0; + + if (!(config = virConnectConfigFile())) + goto cleanup; + + if (!virFileExists(config)) { + ret = 0; + goto cleanup; + } + + VIR_DEBUG("Loading config file '%s'", config); + if (!(conf = virConfReadFile(config, 0))) + goto cleanup; + + if ((value = virConfGetValue(conf, "uri_aliases"))) + ret = virConnectOpenFindURIAliasMatch(value, alias, uri); + else + ret = 0; + +cleanup: + virConfFree(conf); + VIR_FREE(config); + return ret; +} + static virConnectPtr do_open (const char *name, virConnectAuthPtr auth, @@ -998,6 +1119,7 @@ do_open (const char *name, } if (name) { + char *alias = NULL; /* Convert xen -> xen:/// for back compat */ if (STRCASEEQ(name, "xen")) name = "xen:///"; @@ -1008,26 +1130,34 @@ do_open (const char *name, if (STREQ (name, "xen://")) name = "xen:///"; - ret->uri = xmlParseURI (name); + if (!(flags & VIR_CONNECT_NO_ALIASES) && + virConnectOpenResolveURIAlias(name, &alias) < 0) + goto failed; + + ret->uri = xmlParseURI (alias ? alias : name); if (!ret->uri) { virLibConnError(VIR_ERR_INVALID_ARG, - _("could not parse connection URI")); + _("could not parse connection URI %s"), + alias ? alias : name); + VIR_FREE(alias); goto failed; } VIR_DEBUG("name \"%s\" to URI components:\n" - " scheme %s\n" - " opaque %s\n" - " authority %s\n" - " server %s\n" - " user %s\n" - " port %d\n" - " path %s\n", - name, - NULLSTR(ret->uri->scheme), NULLSTR(ret->uri->opaque), - NULLSTR(ret->uri->authority), NULLSTR(ret->uri->server), - NULLSTR(ret->uri->user), ret->uri->port, - NULLSTR(ret->uri->path)); + " scheme %s\n" + " opaque %s\n" + " authority %s\n" + " server %s\n" + " user %s\n" + " port %d\n" + " path %s\n", + alias ? alias : name, + NULLSTR(ret->uri->scheme), NULLSTR(ret->uri->opaque), + NULLSTR(ret->uri->authority), NULLSTR(ret->uri->server), + NULLSTR(ret->uri->user), ret->uri->port, + NULLSTR(ret->uri->path)); + + VIR_FREE(alias); } else { VIR_DEBUG("no name, allowing driver auto-select"); } diff --git a/src/libvirt.conf b/src/libvirt.conf new file mode 100644 index 0000000000..c54903ce1c --- /dev/null +++ b/src/libvirt.conf @@ -0,0 +1,12 @@ +# +# This can be used to setup URI aliases for frequently +# used connection URIs. Aliases may contain only the +# characters a-Z, 0-9, _, -. +# +# Following the '=' may be any valid libvirt connection +# URI, including arbitrary parameters + +#uri_aliases = [ +# "hail=qemu+ssh://root@hail.cloud.example.com/system", +# "sleet=qemu+ssh://root@sleet.cloud.example.com/system", +#] diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 4dc6974550..1dea327fb5 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -313,6 +313,11 @@ doRemoteOpen (virConnectPtr conn, if (conn->uri) { if (!conn->uri->scheme) { /* This is the ///var/lib/xen/xend-socket local path style */ + if (!conn->uri->path) + return VIR_DRV_OPEN_DECLINED; + if (conn->uri->path[0] != '/') + return VIR_DRV_OPEN_DECLINED; + transport = trans_unix; } else { transport_str = get_transport_from_scheme (conn->uri->scheme); diff --git a/src/util/conf.c b/src/util/conf.c index 00045b54b4..c8dcc7f953 100644 --- a/src/util/conf.c +++ b/src/util/conf.c @@ -808,10 +808,8 @@ int virConfFree(virConfPtr conf) { virConfEntryPtr tmp; - if (conf == NULL) { - virConfError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); - return(-1); - } + if (conf == NULL) + return 0; tmp = conf->entries; while (tmp) { diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 68abb1759a..b3e7782890 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -296,17 +296,7 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, unsigned int flags) conn->uri->server) return VIR_DRV_OPEN_DECLINED; } else { - /* Special case URI for Xen driver only: - * - * Treat a plain path as a Xen UNIX socket path, and give - * error unless path is absolute - */ - if (!conn->uri->path || conn->uri->path[0] != '/') { - xenUnifiedError(VIR_ERR_INTERNAL_ERROR, - _("unexpected Xen URI path '%s', try ///var/lib/xen/xend-socket"), - NULLSTR(conn->uri->path)); - return VIR_DRV_OPEN_ERROR; - } + return VIR_DRV_OPEN_DECLINED; } }