From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> I finally got fed up of typing URIs when using virsh.... 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@xxxxxxxxxxxxxxxxxxxxxx/system", "sleet=qemu+ssh://root@xxxxxxxxxxxxxxxxxxxxxxx/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 | 101 ++++++++++++++------------- include/libvirt/libvirt.h.in | 3 +- libvirt.spec.in | 1 + mingw32-libvirt.spec.in | 2 + src/Makefile.am | 2 +- src/libvirt.c | 157 ++++++++++++++++++++++++++++++++++++++---- src/libvirt.conf | 13 ++++ src/remote/remote_driver.c | 5 ++ src/util/conf.c | 6 +- src/xen/xen_driver.c | 12 +--- 10 files changed, 223 insertions(+), 79 deletions(-) create mode 100644 src/libvirt.conf diff --git a/docs/uri.html.in b/docs/uri.html.in index e6326b2..5cc4dbf 100644 --- a/docs/uri.html.in +++ b/docs/uri.html.in @@ -2,6 +2,8 @@ <html> <body> <h1 >Connection URIs</h1> + + <ul id="toc"></ul> <p> Since libvirt supports many different kinds of virtualization (often referred to as "drivers" or "hypervisors"), we need a @@ -13,41 +15,44 @@ machine over the network. To this end, libvirt uses URIs as used on the Web and as defined in <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>. This page documents libvirt URIs. </p> - <ul> - <li> - <a href="#URI_libvirt">Specifying URIs to libvirt</a> - </li> - <li> - <a href="#URI_virsh">Specifying URIs to virsh, virt-manager and virt-install</a> - </li> - <li> - <a href="#URI_xen">xen:/// URI</a> - </li> - <li> - <a href="#URI_qemu">qemu:///... QEMU and KVM URIs</a> - </li> - <li> - <a href="#URI_remote">Remote URIs</a> - </li> - <li> - <a href="#URI_test">test:///... Test URIs</a> - </li> - <li> - <a href="#URI_legacy">Other & legacy URI formats</a> - </li> - </ul> - <h3> - <a name="URI_libvirt">Specifying URIs to libvirt</a> - </h3> + <h2><a name="URI_libvirt">Specifying URIs to libvirt</a></h2> + <p> The URI is passed as the <code>name</code> parameter to <a href="html/libvirt-libvirt.html#virConnectOpen"><code>virConnectOpen</code></a> or <a href="html/libvirt-libvirt.html#virConnectOpenReadOnly"><code>virConnectOpenReadOnly</code></a>. For example: </p> <pre> virConnectPtr conn = virConnectOpenReadOnly (<b>"test:///default"</b>); </pre> - <h3> + <h2> + <a name="URI_config">Configuring URI aliases</a> + </h2> + + <p> +To simplify live for administrators, it is possible to setup URI aliases in a +libvirt client configuration file. The configuration file is <code>/etc/libvirt/libvirt.conf</code> +for the root user, or <code>$HOME/.libvirt/libvirt.conf</code> for any unprivileged user. +In this file, the following syntax can be used to setup aliases + </p> + +<pre> +uri_aliases = [ + "hail=qemu+ssh://root@xxxxxxxxxxxxxxxxxxxxxx/system", + "sleet=qemu+ssh://root@xxxxxxxxxxxxxxxxxxxxxxx/system", +] +</pre> + +<p> + A URI alias should be a string made up from the characters + <code>a-Z, 0-9, _, -</code>. Following the <code>=</code> + 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 <code>VIR_CONNECT_NO_ALIASES</code> + parameter to <code>virConnectOpenAuth</code>. +</p> + + <h2> <a name="URI_virsh">Specifying URIs to virsh, virt-manager and virt-install</a> - </h3> + </h2> <p> In virsh use the <code>-c</code> or <code>--connect</code> option: </p> @@ -76,9 +81,9 @@ In virt-install use the <code>--connect=</code><i>URI</i> option: <pre> virt-install <b>--connect=test:///default</b> <i>[other options]</i> </pre> - <h3> + <h2> <a name="URI_xen">xen:/// URI</a> - </h3> + </h2> <p> <i>This section describes a feature which is new in libvirt > 0.2.3. For libvirt ≤ 0.2.3 use <a href="#URI_legacy_xen"><code>"xen"</code></a>.</i> @@ -87,9 +92,9 @@ virt-install <b>--connect=test:///default</b> <i>[other options]</i> To access a Xen hypervisor running on the local machine use the URI <code>xen:///</code>. </p> - <h3> + <h2> <a name="URI_qemu">qemu:///... QEMU and KVM URIs</a> - </h3> + </h2> <p> To use QEMU support in libvirt you must be running the <code>libvirtd</code> daemon (named <code>libvirt_qemud</code> @@ -119,9 +124,9 @@ KVM URIs are identical. You select between qemu, qemu accelerated and KVM guests in the <a href="format.html#KVM1">guest XML as described here</a>. </p> - <h3> + <h2> <a name="URI_remote">Remote URIs</a> - </h3> + </h2> <p> 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 +187,9 @@ We refer you to <a href="remote.html#Remote_URI_reference">the libvirt remote URI reference</a> and <a href="remote.html">full documentation for libvirt remote support</a>. </p> - <h3> + <h2> <a name="URI_test">test:///... Test URIs</a> - </h3> + </h2> <p> The test driver is a dummy hypervisor for test purposes. The URIs supported are: @@ -196,12 +201,12 @@ host definitions built into the driver. </li> a set of host definitions held in the named file. </li> </ul> - <h3> + <h2> <a name="URI_legacy">Other & legacy URI formats</a> - </h3> - <h4> + </h2> + <h3> <a name="URI_NULL">NULL and empty string URIs</a> - </h4> + </h3> <p> Libvirt allows you to pass a <code>NULL</code> pointer to <code>virConnectOpen*</code>. Empty string (<code>""</code>) acts in @@ -223,9 +228,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 <a href="#URI_xen"><code>xen:///</code> URI</a>. </p> - <h4> + <h3> <a name="URI_file">File paths (xend-unix-server)</a> - </h4> + </h3> <p> If XenD is running and configured in <code>/etc/xen/xend-config.sxp</code>: </p> @@ -240,9 +245,9 @@ using a file URI such as: <pre> virsh -c ///var/run/xend/xend-socket </pre> - <h4> + <h3> <a name="URI_http">Legacy: <code>http://...</code> (xend-http-server)</a> - </h4> + </h3> <p> If XenD is running and configured in <code>/etc/xen/xend-config.sxp</code>: @@ -276,17 +281,17 @@ Notes: libvirt, only the old-style sexpr interface known in the Xen documentation as "unix server" or "http server".</li> </ol> - <h4> + <h3> <a name="URI_legacy_xen">Legacy: <code>"xen"</code></a> - </h4> + </h3> <p> Another legacy URI is to specify name as the string <code>"xen"</code>. This will continue to refer to the Xen hypervisor. However you should prefer a full <a href="#URI_xen"><code>xen:///</code> URI</a> in all future code. </p> - <h4> + <h3> <a name="URI_legacy_proxy">Legacy: Xen proxy</a> - </h4> + </h3> <p> 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 c991dfc..6bb27c7 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -843,7 +843,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 7c63710..d6f4564 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1085,6 +1085,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 f651d11..f5e0d05 100644 --- a/mingw32-libvirt.spec.in +++ b/mingw32-libvirt.spec.in @@ -83,6 +83,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 302d395..fccc54d 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 f07c720..c285e31 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,125 @@ 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; +} + +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_INTERNAL_ERROR, "%s", + _("Expected a string for 'uri_aliases' config parameter list entry")); + return -1; + } + + if (!(offset = strchr(entry->str, '='))) { + virLibConnError(VIR_ERR_INTERNAL_ERROR, + _("Malformed 'uri_aliases' config entry '%s', expected 'alias=uri://host/path'"), + entry->str); + return -1; + } + + safe = strspn(entry->str, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"); + if (safe < (offset - entry->str)) { + virLibConnError(VIR_ERR_INTERNAL_ERROR, + _("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 alias */ + if (strchr(alias, '/') || + strchr(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 +1118,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 +1129,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 0000000..ffe8c21 --- /dev/null +++ b/src/libvirt.conf @@ -0,0 +1,13 @@ + +# +# 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@xxxxxxxxxxxxxxxxxxxxxx/system", +# "sleet=qemu+ssh://root@xxxxxxxxxxxxxxxxxxxxxxx/system", +#] diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 4dc6974..1dea327 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 00045b5..c8dcc7f 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 9c96fca..0a2267d 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; } } -- 1.7.6.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list