[libvirt] [PATCH v3 00/11] Introduce virt-admin client

v1: 1/9 - ACKed if adjusted 6/9 - ACKed 8/9 - ACKed if squashed into 4/9 --> resolves 7/9 v2: - double empty line in private.syms is preserved (1/9) - adjusted authors of virt-admin modules (2/9) - removed unnecessary includes from virt-admin module (2/9) - replaced "int" disconnected type for type "bool" (2/9) - dropped unused command groups (2/9) - removed unnecessary flags and adminControl struct element (2/9) - introduction of virAdmConnectIsAlive split separately (3/9) - remote version of admin API moved into a separate module (4/9) - updated admin_protocol-structs (8/9) - removed debugging leftovers from gendispatch.pl (8/9) - properly indented if-else conditions in gendispatch.pl (8/9) - removed unrelated paragraph, domain references, unsupported options from virt-admin.pod and command 'connect' is now (hopefully correctly) documented (9/9) - introduction of vshAdmCatchDisconnect and introduction of close callbacks had to be squashed together due to dependencies both ways <-- review 4/9 pointed out dummy NULL close callback which should be replaced by something relevant since v2: 1/9 - ACKed if virGetLibvirtConfigFile is renamed 3/9 - ACKed, 'disconnected' removed, replaced by virAdmConnectIsAlive 4/9 - ACKed, admin_remote.c include moved up 5/9 - ACKed v3: - adjusted formatting issues (missing/extra whitespaces, missaligned tabs) - fixed dual assignment in virAdmConnectOpen - cleaned some of the 'bogus' in vshAdmShowVersion - fixed the logic in getSocketPath - fixed coverity issues - virt-admin -h behaves the same as --help - virGetEnvBlockSUID replaced by virGetEnvAllowSUID - changed the prefix for default admin URI - close callback disposal now takes NULL for all relevant arguments - Makefile: simplified man page pattern rule - Makefile: fixed virt-admin recipes - Man: reworded or deleted suggested parts - proposed URI aliases support Erik Skultety (11): libvirt: Export libvirt config getters by moving them to util virt-admin: Introduce first working skeleton admin: Introduce virAdmConnectIsAlive admin: Move remote admin API version to a separate module admin: Do not generate remoteAdminConnect{Open,Close} admin: Add URI support and introduce virAdmGetDefaultURI livirt: Move URI alias matching to util admin: Add support for URI aliases admin: Add support for connection close callbacks admin: Introduce virAdmConnectGetLibVersion virt-admin: Provide a man page for virt-admin .gitignore | 1 + cfg.mk | 2 +- daemon/admin_server.c | 9 + include/libvirt/libvirt-admin.h | 27 ++ po/POTFILES.in | 2 + src/admin/admin_protocol.x | 15 +- src/admin/admin_remote.c | 216 ++++++++++++ src/admin_protocol-structs | 4 + src/datatypes.c | 26 ++ src/datatypes.h | 17 +- src/libvirt-admin.c | 423 ++++++++++++++++-------- src/libvirt.c | 133 +------- src/libvirt.conf | 7 +- src/libvirt_admin_private.syms | 1 + src/libvirt_admin_public.syms | 5 + src/libvirt_private.syms | 2 + src/rpc/gendispatch.pl | 11 +- src/util/virconf.c | 52 +++ src/util/virconf.h | 1 + src/util/viruri.c | 92 ++++++ src/util/viruri.h | 2 + tools/Makefile.am | 38 ++- tools/virt-admin.c | 712 ++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 46 +++ tools/virt-admin.pod | 255 ++++++++++++++ 25 files changed, 1801 insertions(+), 298 deletions(-) create mode 100644 src/admin/admin_remote.c create mode 100644 tools/virt-admin.c create mode 100644 tools/virt-admin.h create mode 100644 tools/virt-admin.pod -- 2.4.3

virConnectGetConfig and virConnectGetConfigPath were static libvirt methods, merely because there hasn't been any need for having them internally exported yet. Since libvirt-admin also needs to reference libvirt config file, 'xGetConfig' should be exported. Besides moving, this patch also renames the methods accordingly, as they are libvirt config specific. --- src/libvirt.c | 55 +----------------------------------------------- src/libvirt_private.syms | 1 + src/util/virconf.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ src/util/virconf.h | 1 + 4 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 25a0040..e3c35e4 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -908,59 +908,6 @@ virGetVersion(unsigned long *libVer, const char *type ATTRIBUTE_UNUSED, return -1; } - -static char * -virConnectGetConfigFilePath(void) -{ - char *path; - if (geteuid() == 0) { - if (virAsprintf(&path, "%s/libvirt/libvirt.conf", - SYSCONFDIR) < 0) - return NULL; - } else { - char *userdir = virGetUserConfigDirectory(); - if (!userdir) - return NULL; - - if (virAsprintf(&path, "%s/libvirt.conf", - userdir) < 0) { - VIR_FREE(userdir); - return NULL; - } - VIR_FREE(userdir); - } - - return path; -} - - -static int -virConnectGetConfigFile(virConfPtr *conf) -{ - char *filename = NULL; - int ret = -1; - - *conf = NULL; - - if (!(filename = virConnectGetConfigFilePath())) - goto cleanup; - - if (!virFileExists(filename)) { - ret = 0; - goto cleanup; - } - - VIR_DEBUG("Loading config file '%s'", filename); - if (!(*conf = virConfReadFile(filename, 0))) - goto cleanup; - - ret = 0; - - cleanup: - VIR_FREE(filename); - return ret; -} - #define URI_ALIAS_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-" @@ -1078,7 +1025,7 @@ do_open(const char *name, if (ret == NULL) return NULL; - if (virConnectGetConfigFile(&conf) < 0) + if (virConfLoadDefault(&conf) < 0) goto failed; if (name && name[0] == '\0') diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a835f18..98f3351 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1323,6 +1323,7 @@ virRun; virConfFree; virConfFreeValue; virConfGetValue; +virConfLoadDefault; virConfNew; virConfReadFile; virConfReadMem; diff --git a/src/util/virconf.c b/src/util/virconf.c index 9f2d116..792e23c 100644 --- a/src/util/virconf.c +++ b/src/util/virconf.c @@ -38,6 +38,7 @@ #include "viralloc.h" #include "virfile.h" #include "virstring.h" +#include "configmake.h" #define VIR_FROM_THIS VIR_FROM_CONF @@ -1053,3 +1054,54 @@ virConfWriteMem(char *memory, int *len, virConfPtr conf) *len = use; return use; } + +static char * +virConfLoadDefaultPath(void) +{ + char *path; + if (geteuid() == 0) { + if (virAsprintf(&path, "%s/libvirt/libvirt.conf", + SYSCONFDIR) < 0) + return NULL; + } else { + char *userdir = virGetUserConfigDirectory(); + if (!userdir) + return NULL; + + if (virAsprintf(&path, "%s/libvirt.conf", + userdir) < 0) { + VIR_FREE(userdir); + return NULL; + } + VIR_FREE(userdir); + } + + return path; +} + +int +virConfLoadDefault(virConfPtr *conf) +{ + char *filename = NULL; + int ret = -1; + + *conf = NULL; + + if (!(filename = virConfLoadDefaultPath())) + goto cleanup; + + if (!virFileExists(filename)) { + ret = 0; + goto cleanup; + } + + VIR_DEBUG("Loading config file '%s'", filename); + if (!(*conf = virConfReadFile(filename, 0))) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(filename); + return ret; +} diff --git a/src/util/virconf.h b/src/util/virconf.h index 8037956..7dab84d 100644 --- a/src/util/virconf.h +++ b/src/util/virconf.h @@ -96,5 +96,6 @@ int virConfWriteFile(const char *filename, int virConfWriteMem(char *memory, int *len, virConfPtr conf); +int virConfLoadDefault(virConfPtr *conf); #endif /* __VIR_CONF_H__ */ -- 2.4.3

On Fri, Nov 06, 2015 at 12:46:16PM +0100, Erik Skultety wrote:
virConnectGetConfig and virConnectGetConfigPath were static libvirt methods, merely because there hasn't been any need for having them internally exported yet. Since libvirt-admin also needs to reference libvirt config file, 'xGetConfig' should be exported. Besides moving, this patch also renames the methods accordingly, as they are libvirt config specific.
The libvirt.conf file is providing configuration for the libvirt.so client library. I'm not convinced we should use that same configuration file for the libvirt-admin.so library. Although it is also a client, it is essentially a completely separate library since it is speaking a different protocol and is targetted at different users. So I think we should have a libvirt-admin.conf file for the libvirt-admin.so client library. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Fri, Nov 06, 2015 at 12:46:16PM +0100, Erik Skultety wrote:
virConnectGetConfig and virConnectGetConfigPath were static libvirt methods, merely because there hasn't been any need for having them internally exported yet. Since libvirt-admin also needs to reference libvirt config file, 'xGetConfig' should be exported. Besides moving, this patch also renames the methods accordingly, as they are libvirt config specific. --- src/libvirt.c | 55 +----------------------------------------------- src/libvirt_private.syms | 1 + src/util/virconf.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ src/util/virconf.h | 1 + 4 files changed, 55 insertions(+), 54 deletions(-)
I agree with Daniel on the different config files. But that could be made easy (and it would make sure the path is consistent) if you kept this function here, but added a parameter (const char *name, for example) that would be appended with '-' after the "libvirt" string. That would differentiate between libvirt (name == NULL), admin (name == "admin") and could be used in the future for any other configuration files (name == "anything"). I know it's kind of ugly and hacky, but Either ACK with the following diff squashed in or send another version if you don't agree ;) If you fancy the former, please adjust the commit message as well. diff --git i/src/libvirt-admin.c w/src/libvirt-admin.c index 00f2a5457beb..9f94a50b8fdd 100644 --- i/src/libvirt-admin.c +++ w/src/libvirt-admin.c @@ -210,7 +210,7 @@ virAdmConnectOpen(const char *uri, unsigned int flags) if (!(conn = virAdmConnectNew())) goto error; - if (virConfLoadDefault(&conf) < 0) + if (virConfLoadDefault(&conf, "admin") < 0) goto error; if (!uri && !(default_uri = virAdmGetDefaultURI(conf))) diff --git i/src/libvirt.c w/src/libvirt.c index 2dd94bfee8fb..731e4adff375 100644 --- i/src/libvirt.c +++ w/src/libvirt.c @@ -949,7 +949,7 @@ do_open(const char *name, if (ret == NULL) return NULL; - if (virConfLoadDefault(&conf) < 0) + if (virConfLoadDefault(&conf, NULL) < 0) goto failed; if (name && name[0] == '\0') diff --git i/src/util/virconf.c w/src/util/virconf.c index 792e23cd2054..ba8e96715f07 100644 --- i/src/util/virconf.c +++ w/src/util/virconf.c @@ -1056,20 +1056,24 @@ virConfWriteMem(char *memory, int *len, virConfPtr conf) } static char * -virConfLoadDefaultPath(void) +virConfLoadDefaultPath(const char *name) { char *path; if (geteuid() == 0) { - if (virAsprintf(&path, "%s/libvirt/libvirt.conf", - SYSCONFDIR) < 0) + if (virAsprintf(&path, "%s/libvirt/libvirt%s%s.conf", + SYSCONFDIR, + name ? "-" : "", + name ? name : "") < 0) return NULL; } else { char *userdir = virGetUserConfigDirectory(); if (!userdir) return NULL; - if (virAsprintf(&path, "%s/libvirt.conf", - userdir) < 0) { + if (virAsprintf(&path, "%s/libvirt%s%s.conf", + userdir, + name ? "-" : "", + name ? name : "") < 0) { VIR_FREE(userdir); return NULL; } @@ -1080,14 +1084,14 @@ virConfLoadDefaultPath(void) } int -virConfLoadDefault(virConfPtr *conf) +virConfLoadDefault(virConfPtr *conf, const char *name) { char *filename = NULL; int ret = -1; *conf = NULL; - if (!(filename = virConfLoadDefaultPath())) + if (!(filename = virConfLoadDefaultPath(name))) goto cleanup; if (!virFileExists(filename)) { diff --git i/src/util/virconf.h w/src/util/virconf.h index 7dab84ded4cb..2e7449f8869d 100644 --- i/src/util/virconf.h +++ w/src/util/virconf.h @@ -96,6 +96,7 @@ int virConfWriteFile(const char *filename, int virConfWriteMem(char *memory, int *len, virConfPtr conf); -int virConfLoadDefault(virConfPtr *conf); +int virConfLoadDefault(virConfPtr *conf, + const char *name); #endif /* __VIR_CONF_H__ */ --

This patch introduces virt-admin client which is based on virsh client, but had to reimplement several methods to meet virt-admin specific needs or remove unnecessary virsh specific logic. --- .gitignore | 1 + po/POTFILES.in | 1 + tools/Makefile.am | 26 ++- tools/virt-admin.c | 556 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 46 +++++ 5 files changed, 628 insertions(+), 2 deletions(-) create mode 100644 tools/virt-admin.c create mode 100644 tools/virt-admin.h diff --git a/.gitignore b/.gitignore index 2d52a8f..a776947 100644 --- a/.gitignore +++ b/.gitignore @@ -176,6 +176,7 @@ /tools/virt-login-shell /tools/virsh /tools/virsh-*-edit.c +/tools/virt-admin /tools/virt-*-validate /tools/virt-sanlock-cleanup /tools/wireshark/src/plugin.c diff --git a/po/POTFILES.in b/po/POTFILES.in index 0cc5b99..d0840f4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -270,6 +270,7 @@ tools/virsh-pool.c tools/virsh-secret.c tools/virsh-snapshot.c tools/virsh-volume.c +tools/virt-admin.c tools/virt-host-validate-common.c tools/virt-host-validate-lxc.c tools/virt-host-validate-qemu.c diff --git a/tools/Makefile.am b/tools/Makefile.am index d5638d9..62073f9 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,4 +1,4 @@ -## Copyright (C) 2005-2014 Red Hat, Inc. +## Copyright (C) 2005-2015 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 @@ -63,7 +63,7 @@ confdir = $(sysconfdir)/libvirt conf_DATA = bin_SCRIPTS = virt-xml-validate virt-pki-validate -bin_PROGRAMS = virsh virt-host-validate +bin_PROGRAMS = virsh virt-host-validate virt-admin libexec_SCRIPTS = libvirt-guests.sh if WITH_SANLOCK @@ -230,6 +230,28 @@ virsh_CFLAGS = \ $(PIE_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(LIBXML_CFLAGS) + +virt_admin_SOURCES = \ + virt-admin.c virt-admin.h \ + $(NULL) + +virt_admin_LDFLAGS = \ + $(AM_LDFLAGS) \ + $(COVERAGE_LDFLAGS) \ + $(STATIC_BINARIES) \ + $(PIE_LDFLAGS) \ + $(NULL) +virt_admin_LDADD = \ + ../src/libvirt-admin.la \ + libvirt_shell.la \ + $(LIBXML_LIBS) \ + $(NULL) +virt_admin_CFLAGS = \ + $(WARN_CFLAGS) \ + $(PIE_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(LIBXML_CFLAGS) \ + $(READLINE_CFLAGS) BUILT_SOURCES = if WITH_WIN_ICON diff --git a/tools/virt-admin.c b/tools/virt-admin.c new file mode 100644 index 0000000..ddfba91 --- /dev/null +++ b/tools/virt-admin.c @@ -0,0 +1,556 @@ +/* + * virt-admin.c: a shell to exercise the libvirt admin API + * + * Copyright (C) 2015 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 + * <http://www.gnu.org/licenses/>. + * + * Erik Skultety <eskultet@redhat.com> + */ + +#include <config.h> +#include "virt-admin.h" + +#include <errno.h> +#include <getopt.h> +#include <locale.h> + +#if WITH_READLINE +# include <readline/readline.h> +# include <readline/history.h> +#endif + +#include "configmake.h" +#include "internal.h" +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virstring.h" +#include "virthread.h" + +/* Gnulib doesn't guarantee SA_SIGINFO support. */ +#ifndef SA_SIGINFO +# define SA_SIGINFO 0 +#endif + +#define VIRT_ADMIN_PROMPT "virt-admin # " + +static char *progname; + +static const vshCmdGrp cmdGroups[]; +static const vshClientHooks hooks; + +static int +vshAdmConnect(vshControl *ctl, unsigned int flags) +{ + vshAdmControlPtr priv = ctl->privData; + + priv->conn = virAdmConnectOpen(ctl->connname, flags); + + if (!priv->conn) { + if (priv->wantReconnect) + vshError(ctl, "%s", _("Failed to reconnect to the admin server")); + else + vshError(ctl, "%s", _("Failed to connect to the admin server")); + return -1; + } else { + if (priv->wantReconnect) + vshPrint(ctl, "%s\n", _("Reconnected to the admin server")); + else + vshPrint(ctl, "%s\n", _("Connected to the admin server")); + } + + return 0; +} + +static int +vshAdmDisconnect(vshControl *ctl) +{ + int ret = 0; + vshAdmControlPtr priv = ctl->privData; + + if (!priv->conn) + return ret; + + ret = virAdmConnectClose(priv->conn); + if (ret < 0) + vshError(ctl, "%s", _("Failed to disconnect from the admin server")); + else if (ret > 0) + vshError(ctl, "%s", _("One or more references were leaked after " + "disconnect from the hypervisor")); + priv->conn = NULL; + return ret; +} + +/* + * vshAdmReconnect: + * + * Reconnect to a daemon's admin server + * + */ +static void +vshAdmReconnect(vshControl *ctl) +{ + vshAdmControlPtr priv = ctl->privData; + if (priv->conn) + priv->wantReconnect = true; + + vshAdmDisconnect(ctl); + vshAdmConnect(ctl, 0); + + priv->wantReconnect = false; +} + +/* --------------- + * Command Connect + * --------------- + */ + +static const vshCmdOptDef opts_connect[] = { + {.name = "name", + .type = VSH_OT_STRING, + .flags = VSH_OFLAG_EMPTY_OK, + .help = N_("daemon's admin server connection URI") + }, + {.name = NULL} +}; + +static const vshCmdInfo info_connect[] = { + {.name = "help", + .data = N_("connect to daemon's admin server") + }, + {.name = "desc", + .data = N_("Connect to a daemon's administrating server.") + }, + {.name = NULL} +}; + +static bool +cmdConnect(vshControl *ctl, const vshCmd *cmd) +{ + const char *name = NULL; + vshAdmControlPtr priv = ctl->privData; + + VIR_FREE(ctl->connname); + if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) + return false; + + ctl->connname = vshStrdup(ctl, name); + + vshAdmReconnect(ctl); + + return !!priv->conn; +} + +static void * +vshAdmConnectionHandler(vshControl *ctl) +{ + vshAdmControlPtr priv = ctl->privData; + + if (!priv->conn) + vshAdmReconnect(ctl); + + if (!priv->conn) { + vshError(ctl, "%s", _("no valid connection")); + return NULL; + } + + return priv->conn; +} + +/* + * Initialize connection. + */ +static bool +vshAdmInit(vshControl *ctl) +{ + vshAdmControlPtr priv = ctl->privData; + + /* Since we have the commandline arguments parsed, we need to + * reload our initial settings to make debugging and readline + * work properly */ + vshInitReload(ctl); + + if (priv->conn) + return false; + + /* set up the library error handler */ + virSetErrorFunc(NULL, vshErrorHandler); + + if (virEventRegisterDefaultImpl() < 0) + return false; + + if (virThreadCreate(&ctl->eventLoop, true, vshEventLoop, ctl) < 0) + return false; + ctl->eventLoopStarted = true; + + if ((ctl->eventTimerId = virEventAddTimeout(-1, vshEventTimeout, ctl, + NULL)) < 0) + return false; + + if (ctl->connname) { + vshAdmReconnect(ctl); + /* Connecting to a named connection must succeed, but we delay + * connecting to the default connection until we need it + * (since the first command might be 'connect' which allows a + * non-default connection, or might be 'help' which needs no + * connection). + */ + if (!priv->conn) { + vshReportError(ctl); + return false; + } + } + + return true; +} + +static void +vshAdmDeinitTimer(int timer ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED) +{ + /* nothing to be done here */ +} + +/* + * Deinitialize virt-admin + */ +static bool +vshAdmDeinit(vshControl *ctl) +{ + vshAdmControlPtr priv = ctl->privData; + + vshDeinit(ctl); + VIR_FREE(ctl->connname); + + if (priv->conn) + vshAdmDisconnect(ctl); + + virResetLastError(); + + if (ctl->eventLoopStarted) { + int timer; + + virMutexLock(&ctl->lock); + ctl->quit = true; + /* HACK: Add a dummy timeout to break event loop */ + timer = virEventAddTimeout(0, vshAdmDeinitTimer, NULL, NULL); + virMutexUnlock(&ctl->lock); + + virThreadJoin(&ctl->eventLoop); + + if (timer != -1) + virEventRemoveTimeout(timer); + + if (ctl->eventTimerId != -1) + virEventRemoveTimeout(ctl->eventTimerId); + + ctl->eventLoopStarted = false; + } + + virMutexDestroy(&ctl->lock); + + return true; +} + +/* + * Print usage + */ +static void +vshAdmUsage(void) +{ + const vshCmdGrp *grp; + const vshCmdDef *cmd; + + fprintf(stdout, _("\n%s [options]... [<command_string>]" + "\n%s [options]... <command> [args...]\n\n" + " options:\n" + " -c | --connect=URI daemon admin connection URI\n" + " -d | --debug=NUM debug level [0-4]\n" + " -h | --help this help\n" + " -l | --log=FILE output logging to file\n" + " -q | --quiet quiet mode\n" + " -v short version\n" + " -V long version\n" + " --version[=TYPE] version, TYPE is short or long (default short)\n" + " commands (non interactive mode):\n\n"), progname, + progname); + + for (grp = cmdGroups; grp->name; grp++) { + fprintf(stdout, _(" %s (help keyword '%s')\n"), + grp->name, grp->keyword); + for (cmd = grp->commands; cmd->name; cmd++) { + if (cmd->flags & VSH_CMD_FLAG_ALIAS) + continue; + fprintf(stdout, + " %-30s %s\n", cmd->name, + _(vshCmddefGetInfo(cmd, "help"))); + } + fprintf(stdout, "\n"); + } + + fprintf(stdout, "%s", + _("\n (specify help <group> for details about the commands in the group)\n")); + fprintf(stdout, "%s", + _("\n (specify help <command> for details about the command)\n\n")); + return; +} + +/* + * Show version and options compiled in + */ +static void +vshAdmShowVersion(vshControl *ctl ATTRIBUTE_UNUSED) +{ + /* FIXME - list a copyright blurb, as in GNU programs? */ + vshPrint(ctl, _("Virt-admin command line tool of libvirt %s\n"), VERSION); + vshPrint(ctl, _("See web site at %s\n\n"), "http://libvirt.org/"); + + vshPrint(ctl, "%s", _("Compiled with support for:")); +#ifdef WITH_LIBVIRTD + vshPrint(ctl, " Daemon"); +#endif +#ifdef ENABLE_DEBUG + vshPrint(ctl, " Debug"); +#endif +#if WITH_READLINE + vshPrint(ctl, " Readline"); +#endif + vshPrint(ctl, "\n"); +} + +static bool +vshAdmParseArgv(vshControl *ctl, int argc, char **argv) +{ + int arg, debug; + size_t i; + int longindex = -1; + struct option opt[] = { + {"connect", required_argument, NULL, 'c'}, + {"debug", required_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'h'}, + {"log", required_argument, NULL, 'l'}, + {"quiet", no_argument, NULL, 'q'}, + {"version", optional_argument, NULL, 'v'}, + {NULL, 0, NULL, 0} + }; + + /* Standard (non-command) options. The leading + ensures that no + * argument reordering takes place, so that command options are + * not confused with top-level virt-admin options. */ + while ((arg = getopt_long(argc, argv, "+:c:d:hl:qvV", opt, &longindex)) != -1) { + switch (arg) { + case 'c': + VIR_FREE(ctl->connname); + ctl->connname = vshStrdup(ctl, optarg); + break; + case 'd': + if (virStrToLong_i(optarg, NULL, 10, &debug) < 0) { + vshError(ctl, _("option %s takes a numeric argument"), + longindex == -1 ? "-d" : "--debug"); + exit(EXIT_FAILURE); + } + if (debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR) + vshError(ctl, _("ignoring debug level %d out of range [%d-%d]"), + debug, VSH_ERR_DEBUG, VSH_ERR_ERROR); + else + ctl->debug = debug; + break; + case 'h': + vshAdmUsage(); + exit(EXIT_SUCCESS); + break; + case 'l': + vshCloseLogFile(ctl); + ctl->logfile = vshStrdup(ctl, optarg); + vshOpenLogFile(ctl); + break; + case 'q': + ctl->quiet = true; + break; + case 'v': + if (STRNEQ_NULLABLE(optarg, "long")) { + puts(VERSION); + exit(EXIT_SUCCESS); + } + /* fall through */ + case 'V': + vshAdmShowVersion(ctl); + exit(EXIT_SUCCESS); + case ':': + for (i = 0; opt[i].name != NULL; i++) { + if (opt[i].val == optopt) + break; + } + if (opt[i].name) + vshError(ctl, _("option '-%c'/'--%s' requires an argument"), + optopt, opt[i].name); + else + vshError(ctl, _("option '-%c' requires an argument"), optopt); + exit(EXIT_FAILURE); + case '?': + if (optopt) + vshError(ctl, _("unsupported option '-%c'. See --help."), optopt); + else + vshError(ctl, _("unsupported option '%s'. See --help."), argv[optind - 1]); + exit(EXIT_FAILURE); + default: + vshError(ctl, _("unknown option")); + exit(EXIT_FAILURE); + } + longindex = -1; + } + + if (argc == optind) { + ctl->imode = true; + } else { + /* parse command */ + ctl->imode = false; + if (argc - optind == 1) { + vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]); + return vshCommandStringParse(ctl, argv[optind]); + } else { + return vshCommandArgvParse(ctl, argc - optind, argv + optind); + } + } + return true; +} + +static const vshCmdDef vshAdmCmds[] = { + VSH_CMD_CD, + VSH_CMD_ECHO, + VSH_CMD_EXIT, + VSH_CMD_HELP, + VSH_CMD_PWD, + VSH_CMD_QUIT, + {.name = "connect", + .handler = cmdConnect, + .opts = opts_connect, + .info = info_connect, + .flags = VSH_CMD_FLAG_NOCONNECT + }, + {.name = NULL} +}; + +static const vshCmdGrp cmdGroups[] = { + {"Virt-admin itself", "virt-admin", vshAdmCmds}, + {NULL, NULL, NULL} +}; + +static const vshClientHooks hooks = { + .connHandler = vshAdmConnectionHandler +}; + +int +main(int argc, char **argv) +{ + vshControl _ctl, *ctl = &_ctl; + vshAdmControl virtAdminCtl; + const char *defaultConn; + bool ret = true; + + memset(ctl, 0, sizeof(vshControl)); + memset(&virtAdminCtl, 0, sizeof(vshAdmControl)); + ctl->name = "virt-admin"; /* hardcoded name of the binary */ + ctl->log_fd = -1; /* Initialize log file descriptor */ + ctl->debug = VSH_DEBUG_DEFAULT; + ctl->hooks = &hooks; + + ctl->eventPipe[0] = -1; + ctl->eventPipe[1] = -1; + ctl->eventTimerId = -1; + ctl->privData = &virtAdminCtl; + + if (!(progname = strrchr(argv[0], '/'))) + progname = argv[0]; + else + progname++; + ctl->progname = progname; + + if (!setlocale(LC_ALL, "")) { + perror("setlocale"); + /* failure to setup locale is not fatal */ + } + if (!bindtextdomain(PACKAGE, LOCALEDIR)) { + perror("bindtextdomain"); + return EXIT_FAILURE; + } + if (!textdomain(PACKAGE)) { + perror("textdomain"); + return EXIT_FAILURE; + } + + if (isatty(STDIN_FILENO)) { + ctl->istty = true; + +#ifndef WIN32 + if (tcgetattr(STDIN_FILENO, &ctl->termattr) < 0) + ctl->istty = false; +#endif + } + + if (virMutexInit(&ctl->lock) < 0) { + vshError(ctl, "%s", _("Failed to initialize mutex")); + return EXIT_FAILURE; + } + + virFileActivateDirOverride(argv[0]); + + if ((defaultConn = virGetEnvBlockSUID("LIBVIRT_DEFAULT_ADMIN_URI"))) + ctl->connname = vshStrdup(ctl, defaultConn); + + if (!vshInit(ctl, cmdGroups, NULL)) + exit(EXIT_FAILURE); + + if (!vshAdmParseArgv(ctl, argc, argv) || + !vshAdmInit(ctl)) { + vshAdmDeinit(ctl); + exit(EXIT_FAILURE); + } + + if (!ctl->imode) { + ret = vshCommandRun(ctl, ctl->cmd); + } else { + /* interactive mode */ + if (!ctl->quiet) { + vshPrint(ctl, + _("Welcome to %s, the administrating virtualization " + "interactive terminal.\n\n"), + progname); + vshPrint(ctl, "%s", + _("Type: 'help' for help with commands\n" + " 'quit' to quit\n\n")); + } + + do { + ctl->cmdstr = vshReadline(ctl, VIRT_ADMIN_PROMPT); + if (ctl->cmdstr == NULL) + break; /* EOF */ + if (*ctl->cmdstr) { +#if WITH_READLINE + add_history(ctl->cmdstr); +#endif + if (vshCommandStringParse(ctl, ctl->cmdstr)) + vshCommandRun(ctl, ctl->cmd); + } + VIR_FREE(ctl->cmdstr); + } while (ctl->imode); + + if (ctl->cmdstr == NULL) + fputc('\n', stdout); /* line break after alone prompt */ + } + + vshAdmDeinit(ctl); + exit(ret ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/tools/virt-admin.h b/tools/virt-admin.h new file mode 100644 index 0000000..62228cc --- /dev/null +++ b/tools/virt-admin.h @@ -0,0 +1,46 @@ +/* + * virt-admin.h: a shell to exercise the libvirt admin API + * + * Copyright (C) 2015 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 + * <http://www.gnu.org/licenses/>. + * + * Erik Skultety <eskultet@redhat.com> + */ + +#ifndef VIRT_ADMIN_H +# define VIRT_ADMIN_H + +# include "internal.h" +# include "vsh.h" + +# define VIR_FROM_THIS VIR_FROM_NONE + +/* + * Command group types + */ + +typedef struct _vshAdmControl vshAdmControl; +typedef vshAdmControl *vshAdmControlPtr; + +/* + * adminControl + */ +struct _vshAdmControl { + virAdmConnectPtr conn; /* connection to a daemon's admin server */ + bool wantReconnect; +}; + +#endif /* VIRT_ADMIN_H */ -- 2.4.3

On Fri, Nov 06, 2015 at 12:46:17PM +0100, Erik Skultety wrote:
This patch introduces virt-admin client which is based on virsh client, but had to reimplement several methods to meet virt-admin specific needs or remove unnecessary virsh specific logic. --- .gitignore | 1 + po/POTFILES.in | 1 + tools/Makefile.am | 26 ++- tools/virt-admin.c | 556 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 46 +++++ 5 files changed, 628 insertions(+), 2 deletions(-) create mode 100644 tools/virt-admin.c create mode 100644 tools/virt-admin.h
I see you haven't added it to the specfile, which is good for now (although we install it with make install), but we need to make sure it is added there when we allow the admin interface.
diff --git a/tools/virt-admin.c b/tools/virt-admin.c new file mode 100644 index 0000000..ddfba91 --- /dev/null +++ b/tools/virt-admin.c [...] +static void +vshAdmDeinitTimer(int timer ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED) +{ + /* nothing to be done here */ +} + +/* + * Deinitialize virt-admin + */ +static bool +vshAdmDeinit(vshControl *ctl)
No need for it to be bool, change it to void. ACK with that changed. Martin

On Fri, Nov 06, 2015 at 12:46:17PM +0100, Erik Skultety wrote:
This patch introduces virt-admin client which is based on virsh client, but had to reimplement several methods to meet virt-admin specific needs or remove unnecessary virsh specific logic. --- .gitignore | 1 + po/POTFILES.in | 1 + tools/Makefile.am | 26 ++- tools/virt-admin.c | 556 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 46 +++++ 5 files changed, 628 insertions(+), 2 deletions(-) create mode 100644 tools/virt-admin.c create mode 100644 tools/virt-admin.h
diff --git a/tools/virt-admin.c b/tools/virt-admin.c new file mode 100644 index 0000000..ddfba91 --- /dev/null +++ b/tools/virt-admin.c [...] +static bool +vshAdmInit(vshControl *ctl) +{ + vshAdmControlPtr priv = ctl->privData; + + /* Since we have the commandline arguments parsed, we need to + * reload our initial settings to make debugging and readline + * work properly */ + vshInitReload(ctl); + + if (priv->conn) + return false; + + /* set up the library error handler */ + virSetErrorFunc(NULL, vshErrorHandler); + + if (virEventRegisterDefaultImpl() < 0) + return false; + + if (virThreadCreate(&ctl->eventLoop, true, vshEventLoop, ctl) < 0) + return false; + ctl->eventLoopStarted = true; + + if ((ctl->eventTimerId = virEventAddTimeout(-1, vshEventTimeout, ctl,
I forgot to mention that you could allow virEventAddTimeout() to allow NULL callback and then get rid of the vshEventTimeout here. It confuses readers. Martin

On 06.11.2015 12:46, Erik Skultety wrote:
This patch introduces virt-admin client which is based on virsh client, but had to reimplement several methods to meet virt-admin specific needs or remove unnecessary virsh specific logic. --- .gitignore | 1 + po/POTFILES.in | 1 + tools/Makefile.am | 26 ++- tools/virt-admin.c | 556 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 46 +++++ 5 files changed, 628 insertions(+), 2 deletions(-) create mode 100644 tools/virt-admin.c create mode 100644 tools/virt-admin.h
diff --git a/tools/virt-admin.c b/tools/virt-admin.c new file mode 100644 index 0000000..ddfba91 --- /dev/null +++ b/tools/virt-admin.c @@ -0,0 +1,556 @@ +/* + * virt-admin.c: a shell to exercise the libvirt admin API + * + * Copyright (C) 2015 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 + * <http://www.gnu.org/licenses/>. + * + * Erik Skultety <eskultet@redhat.com>
We usually prepend this line with "Authors: ".
+ */ + +#include <config.h> +#include "virt-admin.h" + +#include <errno.h> +#include <getopt.h> +#include <locale.h> + +#if WITH_READLINE +# include <readline/readline.h> +# include <readline/history.h> +#endif + +#include "configmake.h" +#include "internal.h" +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virstring.h" +#include "virthread.h" + +/* Gnulib doesn't guarantee SA_SIGINFO support. */ +#ifndef SA_SIGINFO +# define SA_SIGINFO 0 +#endif + +#define VIRT_ADMIN_PROMPT "virt-admin # " + +static char *progname; + +static const vshCmdGrp cmdGroups[]; +static const vshClientHooks hooks; + +static int +vshAdmConnect(vshControl *ctl, unsigned int flags) +{ + vshAdmControlPtr priv = ctl->privData; + + priv->conn = virAdmConnectOpen(ctl->connname, flags); + + if (!priv->conn) { + if (priv->wantReconnect) + vshError(ctl, "%s", _("Failed to reconnect to the admin server")); + else + vshError(ctl, "%s", _("Failed to connect to the admin server")); + return -1; + } else { + if (priv->wantReconnect) + vshPrint(ctl, "%s\n", _("Reconnected to the admin server")); + else + vshPrint(ctl, "%s\n", _("Connected to the admin server")); + } + + return 0; +} + +static int +vshAdmDisconnect(vshControl *ctl) +{ + int ret = 0; + vshAdmControlPtr priv = ctl->privData; + + if (!priv->conn) + return ret; + + ret = virAdmConnectClose(priv->conn); + if (ret < 0) + vshError(ctl, "%s", _("Failed to disconnect from the admin server")); + else if (ret > 0) + vshError(ctl, "%s", _("One or more references were leaked after " + "disconnect from the hypervisor")); + priv->conn = NULL; + return ret; +} + +/* + * vshAdmReconnect: + * + * Reconnect to a daemon's admin server + * + */ +static void +vshAdmReconnect(vshControl *ctl) +{ + vshAdmControlPtr priv = ctl->privData; + if (priv->conn) + priv->wantReconnect = true; + + vshAdmDisconnect(ctl); + vshAdmConnect(ctl, 0); + + priv->wantReconnect = false; +} + +/* --------------- + * Command Connect + * --------------- + */ + +static const vshCmdOptDef opts_connect[] = { + {.name = "name", + .type = VSH_OT_STRING, + .flags = VSH_OFLAG_EMPTY_OK, + .help = N_("daemon's admin server connection URI") + }, + {.name = NULL} +}; + +static const vshCmdInfo info_connect[] = { + {.name = "help", + .data = N_("connect to daemon's admin server") + }, + {.name = "desc", + .data = N_("Connect to a daemon's administrating server.") + }, + {.name = NULL} +}; + +static bool +cmdConnect(vshControl *ctl, const vshCmd *cmd) +{ + const char *name = NULL; + vshAdmControlPtr priv = ctl->privData; + + VIR_FREE(ctl->connname); + if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) + return false;
I think the VIR_FREE() should go after option acquiring. Otherwise we will lose it if vshCommand... fails.
+ + ctl->connname = vshStrdup(ctl, name); + + vshAdmReconnect(ctl); + + return !!priv->conn; +}
ACK Michal

+ * 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/>. + * + * Erik Skultety <eskultet@redhat.com>
We usually prepend this line with "Authors: ".
How can I keep forgetting these things?! I'll fix it, thanks.
+static bool +cmdConnect(vshControl *ctl, const vshCmd *cmd) +{ + const char *name = NULL; + vshAdmControlPtr priv = ctl->privData; + + VIR_FREE(ctl->connname); + if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) + return false;
I think the VIR_FREE() should go after option acquiring. Otherwise we will lose it if vshCommand... fails.
Spurious as it might be, this could only fail, if we decided that --name argument to virsh/virt-admin connect command should be required, but since it's optional, vshCommandOptStringReq can never fail in this specific case (I guess I already mentioned this in one of my earlier replies to previous versions). But I do agree with you that it would be nice to fix the logic, so no future questions about this snippet of code will arise.
+ ctl->connname = vshStrdup(ctl, name); + + vshAdmReconnect(ctl); + + return !!priv->conn; +}
ACK
Michal
Thanks, I'll adjust the patch and push it once the series is complete. Erik

Since most of our APIs rely on an acive functional connection to a daemon and we have such a mechanism in libvirt already, there's need to have such a way in libvirt-admin as well. By introducing a new public API, this patch provides support to check for an active connection. --- include/libvirt/libvirt-admin.h | 1 + src/libvirt-admin.c | 29 +++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 1 + tools/virt-admin.c | 4 ++-- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 9997cc2..72671c6 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -52,6 +52,7 @@ virAdmConnectPtr virAdmConnectOpen(const char *name, unsigned int flags); int virAdmConnectClose(virAdmConnectPtr conn); int virAdmConnectRef(virAdmConnectPtr conn); +int virAdmConnectIsAlive(virAdmConnectPtr conn); # ifdef __cplusplus } diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 5a4fc48..105727f 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -386,3 +386,32 @@ virAdmConnectRef(virAdmConnectPtr conn) return 0; } + +/** + * virAdmConnectIsAlive: + * @conn: connection to admin server + * + * Decide whether the connection to the admin server is alive or not. + * Connection is considered alive if the channel it is running over is not + * closed. + * + * Returns 1, if the connection is alive, 0 if the channel has already + * been closed or -1 on error. + */ +int +virAdmConnectIsAlive(virAdmConnectPtr conn) +{ + bool ret; + remoteAdminPrivPtr priv = conn->privateData; + + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + virCheckAdmConnectReturn(conn, -1); + + virObjectLock(priv); + ret = virNetClientIsOpen(priv->client); + virObjectUnlock(priv); + + return ret; +} diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index d9e3c0b..16cfd42 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -15,4 +15,5 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectOpen; virAdmConnectClose; virAdmConnectRef; + virAdmConnectIsAlive; }; diff --git a/tools/virt-admin.c b/tools/virt-admin.c index ddfba91..6b009ea 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -159,10 +159,10 @@ vshAdmConnectionHandler(vshControl *ctl) { vshAdmControlPtr priv = ctl->privData; - if (!priv->conn) + if (!priv->conn || !virAdmConnectIsAlive(priv->conn)) vshAdmReconnect(ctl); - if (!priv->conn) { + if (!priv->conn || !virAdmConnectIsAlive(priv->conn)) { vshError(ctl, "%s", _("no valid connection")); return NULL; } -- 2.4.3

On Fri, Nov 06, 2015 at 12:46:18PM +0100, Erik Skultety wrote:
Since most of our APIs rely on an acive functional connection to a daemon and we have such a mechanism in libvirt already, there's need to have such a way in libvirt-admin as well. By introducing a new public API, this patch provides support to check for an active connection. --- include/libvirt/libvirt-admin.h | 1 + src/libvirt-admin.c | 29 +++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 1 + tools/virt-admin.c | 4 ++-- 4 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 9997cc2..72671c6 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -52,6 +52,7 @@ virAdmConnectPtr virAdmConnectOpen(const char *name, unsigned int flags); int virAdmConnectClose(virAdmConnectPtr conn);
int virAdmConnectRef(virAdmConnectPtr conn); +int virAdmConnectIsAlive(virAdmConnectPtr conn);
# ifdef __cplusplus } diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 5a4fc48..105727f 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -386,3 +386,32 @@ virAdmConnectRef(virAdmConnectPtr conn)
return 0; } + +/** + * virAdmConnectIsAlive: + * @conn: connection to admin server + * + * Decide whether the connection to the admin server is alive or not. + * Connection is considered alive if the channel it is running over is not + * closed. + * + * Returns 1, if the connection is alive, 0 if the channel has already + * been closed or -1 on error. + */ +int +virAdmConnectIsAlive(virAdmConnectPtr conn) +{ + bool ret; + remoteAdminPrivPtr priv = conn->privateData; +
I know it wouldn't be consistent with virConnectIsAlive(), but we could handle NULLs nicely, e.g. just return 0. I guess -1 is OK as well because everyone will check for '== 1' anyway (if at all ever).
+ VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + virCheckAdmConnectReturn(conn, -1); + + virObjectLock(priv); + ret = virNetClientIsOpen(priv->client); + virObjectUnlock(priv); + + return ret; +} diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index d9e3c0b..16cfd42 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -15,4 +15,5 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectOpen; virAdmConnectClose; virAdmConnectRef; + virAdmConnectIsAlive; }; diff --git a/tools/virt-admin.c b/tools/virt-admin.c index ddfba91..6b009ea 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -159,10 +159,10 @@ vshAdmConnectionHandler(vshControl *ctl) { vshAdmControlPtr priv = ctl->privData;
- if (!priv->conn) + if (!priv->conn || !virAdmConnectIsAlive(priv->conn)) vshAdmReconnect(ctl);
you could then drop the '!priv->conn' here... ACK either way if nobody else has an opinion on that for some time. Just remember that, you need to adjust the documentation for virAdmConnectIsAlive() if you change its behaviour.
- if (!priv->conn) { + if (!priv->conn || !virAdmConnectIsAlive(priv->conn)) { vshError(ctl, "%s", _("no valid connection")); return NULL; } -- 2.4.3
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

By moving the remote version into a separate module, we gain a slightly better maintainability in the long run than just by leaving it in one place with the existing libvirt-admin library which can start getting pretty messy later on. --- src/admin/admin_remote.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++ src/libvirt-admin.c | 115 +-------------------------------------- 2 files changed, 137 insertions(+), 114 deletions(-) create mode 100644 src/admin/admin_remote.c diff --git a/src/admin/admin_remote.c b/src/admin/admin_remote.c new file mode 100644 index 0000000..b8e6607 --- /dev/null +++ b/src/admin/admin_remote.c @@ -0,0 +1,136 @@ +/* + * admin_remote.c + * + * Copyright (C) 2015 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 + * <http://www.gnu.org/licenses/>. + * + * Author: Erik Skultety <eskultet@redhat.com> + */ + +#include <config.h> +#include <rpc/rpc.h> +#include "admin_protocol.h" + +typedef struct _remoteAdminPriv remoteAdminPriv; +typedef remoteAdminPriv *remoteAdminPrivPtr; + +struct _remoteAdminPriv { + virObjectLockable parent; + + int counter; + virNetClientPtr client; + virNetClientProgramPtr program; +}; + +static virClassPtr remoteAdminPrivClass; + +static void +remoteAdminPrivDispose(void *opaque) +{ + remoteAdminPrivPtr priv = opaque; + + virObjectUnref(priv->program); + virObjectUnref(priv->client); +} + + +static int +callFull(virAdmConnectPtr conn ATTRIBUTE_UNUSED, + remoteAdminPrivPtr priv, + int *fdin, + size_t fdinlen, + int **fdout, + size_t *fdoutlen, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) +{ + int rv; + virNetClientProgramPtr prog = priv->program; + int counter = priv->counter++; + virNetClientPtr client = priv->client; + + /* Unlock, so that if we get any async events/stream data + * while processing the RPC, we don't deadlock when our + * callbacks for those are invoked + */ + virObjectRef(priv); + virObjectUnlock(priv); + + rv = virNetClientProgramCall(prog, + client, + counter, + proc_nr, + fdinlen, fdin, + fdoutlen, fdout, + args_filter, args, + ret_filter, ret); + + virObjectLock(priv); + virObjectUnref(priv); + + return rv; +} + +static int +call(virAdmConnectPtr conn, + unsigned int flags, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) +{ + virCheckFlags(0, -1); + + return callFull(conn, conn->privateData, + NULL, 0, NULL, NULL, proc_nr, + args_filter, args, ret_filter, ret); +} + +#include "admin_client.h" + +static void +remoteAdminPrivFree(void *opaque) +{ + virAdmConnectPtr conn = opaque; + + remoteAdminConnectClose(conn); + virObjectUnref(conn->privateData); +} + +static remoteAdminPrivPtr +remoteAdminPrivNew(const char *sock_path) +{ + remoteAdminPrivPtr priv = NULL; + + if (!(priv = virObjectLockableNew(remoteAdminPrivClass))) + goto error; + + if (!(priv->client = virNetClientNewUNIX(sock_path, false, NULL))) + goto error; + + if (!(priv->program = virNetClientProgramNew(ADMIN_PROGRAM, + ADMIN_PROTOCOL_VERSION, + NULL, 0, NULL))) + goto error; + + if (virNetClientAddProgram(priv->client, priv->program) < 0) + goto error; + + return priv; + error: + virObjectUnref(priv); + return NULL; +} diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 105727f..1367164 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -22,8 +22,6 @@ #include <config.h> -#include <rpc/rpc.h> - #include "internal.h" #include "datatypes.h" #include "configmake.h" @@ -44,123 +42,12 @@ VIR_LOG_INIT("libvirt-admin"); - -typedef struct _remoteAdminPriv remoteAdminPriv; -typedef remoteAdminPriv *remoteAdminPrivPtr; - -struct _remoteAdminPriv { - virObjectLockable parent; - - int counter; - virNetClientPtr client; - virNetClientProgramPtr program; -}; - -static virClassPtr remoteAdminPrivClass; - -static void -remoteAdminPrivDispose(void *opaque) -{ - remoteAdminPrivPtr priv = opaque; - - virObjectUnref(priv->program); - virObjectUnref(priv->client); -} - - -static int -callFull(virAdmConnectPtr conn ATTRIBUTE_UNUSED, - remoteAdminPrivPtr priv, - int *fdin, - size_t fdinlen, - int **fdout, - size_t *fdoutlen, - int proc_nr, - xdrproc_t args_filter, char *args, - xdrproc_t ret_filter, char *ret) -{ - int rv; - virNetClientProgramPtr prog = priv->program; - int counter = priv->counter++; - virNetClientPtr client = priv->client; - - /* Unlock, so that if we get any async events/stream data - * while processing the RPC, we don't deadlock when our - * callbacks for those are invoked - */ - virObjectRef(priv); - virObjectUnlock(priv); - - rv = virNetClientProgramCall(prog, - client, - counter, - proc_nr, - fdinlen, fdin, - fdoutlen, fdout, - args_filter, args, - ret_filter, ret); - - virObjectLock(priv); - virObjectUnref(priv); - - return rv; -} - -static int -call(virAdmConnectPtr conn, - unsigned int flags, - int proc_nr, - xdrproc_t args_filter, char *args, - xdrproc_t ret_filter, char *ret) -{ - virCheckFlags(0, -1); - - return callFull(conn, conn->privateData, - NULL, 0, NULL, NULL, proc_nr, - args_filter, args, ret_filter, ret); -} - -#include "admin_protocol.h" -#include "admin_client.h" +#include "admin_remote.c" static bool virAdmGlobalError; static virOnceControl virAdmGlobalOnce = VIR_ONCE_CONTROL_INITIALIZER; static void -remoteAdminPrivFree(void *opaque) -{ - virAdmConnectPtr conn = opaque; - - remoteAdminConnectClose(conn); - virObjectUnref(conn->privateData); -} - -static remoteAdminPrivPtr -remoteAdminPrivNew(const char *sock_path) -{ - remoteAdminPrivPtr priv = NULL; - - if (!(priv = virObjectLockableNew(remoteAdminPrivClass))) - goto error; - - if (!(priv->client = virNetClientNewUNIX(sock_path, false, NULL))) - goto error; - - if (!(priv->program = virNetClientProgramNew(ADMIN_PROGRAM, - ADMIN_PROTOCOL_VERSION, - NULL, 0, NULL))) - goto error; - - if (virNetClientAddProgram(priv->client, priv->program) < 0) - goto error; - - return priv; - error: - virObjectUnref(priv); - return NULL; -} - -static void virAdmGlobalInit(void) { /* It would be nice if we could trace the use of this call, to -- 2.4.3

As we plan to add more and more logic to remote connecting methods, these cannot be generated from admin_protocol.x anymore. Instead, this patch implements these to methods explicitly. --- src/admin/admin_protocol.x | 4 ++-- src/admin/admin_remote.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/admin/admin_protocol.x b/src/admin/admin_protocol.x index cfc92ff..d0ca1a3 100644 --- a/src/admin/admin_protocol.x +++ b/src/admin/admin_protocol.x @@ -64,12 +64,12 @@ enum admin_procedure { * in the function parameter list. */ /** - * @generate: client + * @generate: none */ ADMIN_PROC_CONNECT_OPEN = 1, /** - * @generate: client + * @generate: none */ ADMIN_PROC_CONNECT_CLOSE = 2 }; diff --git a/src/admin/admin_remote.c b/src/admin/admin_remote.c index b8e6607..2c02ba6 100644 --- a/src/admin/admin_remote.c +++ b/src/admin/admin_remote.c @@ -101,6 +101,51 @@ call(virAdmConnectPtr conn, #include "admin_client.h" +static int +remoteAdminConnectOpen(virAdmConnectPtr conn, unsigned int flags) +{ + int rv = -1; + remoteAdminPrivPtr priv = conn->privateData; + admin_connect_open_args args; + + virObjectLock(priv); + + args.flags = flags; + + if (call(conn, 0, ADMIN_PROC_CONNECT_OPEN, + (xdrproc_t)xdr_admin_connect_open_args, (char *)&args, + (xdrproc_t)xdr_void, (char *)NULL) == -1) { + goto done; + } + + rv = 0; + + done: + virObjectUnlock(priv); + return rv; +} + +static int +remoteAdminConnectClose(virAdmConnectPtr conn) +{ + int rv = -1; + remoteAdminPrivPtr priv = conn->privateData; + + virObjectLock(priv); + + if (call(conn, 0, ADMIN_PROC_CONNECT_CLOSE, + (xdrproc_t)xdr_void, (char *)NULL, + (xdrproc_t)xdr_void, (char *)NULL) == -1) { + goto done; + } + + rv = 0; + + done: + virObjectUnlock(priv); + return rv; +} + static void remoteAdminPrivFree(void *opaque) { -- 2.4.3

Since virt-admin should be able to connect to various admin servers on hosted different daemons, we need to provide URI support to libvirt-admin. --- include/libvirt/libvirt-admin.h | 2 + src/datatypes.c | 2 + src/datatypes.h | 1 + src/libvirt-admin.c | 129 ++++++++++++++++++++++++++++++---------- src/libvirt.conf | 7 ++- src/libvirt_admin_public.syms | 1 + tools/virt-admin.c | 39 ++++++++++++ 7 files changed, 146 insertions(+), 35 deletions(-) diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 72671c6..145720d 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -54,6 +54,8 @@ int virAdmConnectClose(virAdmConnectPtr conn); int virAdmConnectRef(virAdmConnectPtr conn); int virAdmConnectIsAlive(virAdmConnectPtr conn); +char *virAdmConnectGetURI(virAdmConnectPtr conn); + # ifdef __cplusplus } # endif diff --git a/src/datatypes.c b/src/datatypes.c index 12bcfc1..aeac301 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -832,4 +832,6 @@ virAdmConnectDispose(void *obj) if (conn->privateDataFreeFunc) conn->privateDataFreeFunc(conn); + + virURIFree(conn->uri); } diff --git a/src/datatypes.h b/src/datatypes.h index be108fe..b16bdb1 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -397,6 +397,7 @@ struct _virConnect { */ struct _virAdmConnect { virObjectLockable object; + virURIPtr uri; void *privateData; virFreeCallback privateDataFreeFunc; diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 1367164..599c30a 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -27,6 +27,7 @@ #include "configmake.h" #include "viralloc.h" +#include "virconf.h" #include "virlog.h" #include "virnetclient.h" #include "virobject.h" @@ -95,60 +96,54 @@ virAdmInitialize(void) } static char * -getSocketPath(const char *name) +getSocketPath(virURIPtr uri) { char *rundir = virGetUserRuntimeDirectory(); char *sock_path = NULL; size_t i = 0; - virURIPtr uri = NULL; - if (name) { - if (!(uri = virURIParse(name))) - goto error; + if (!uri) + goto cleanup; - if (STRNEQ(uri->scheme, "admin") || - uri->server || uri->user || uri->fragment) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Invalid connection name '%s'"), name); - goto error; - } - for (i = 0; i < uri->paramsCount; i++) { - virURIParamPtr param = &uri->params[i]; + for (i = 0; i < uri->paramsCount; i++) { + virURIParamPtr param = &uri->params[i]; - if (STREQ(param->name, "socket")) { - VIR_FREE(sock_path); - if (VIR_STRDUP(sock_path, param->value) < 0) - goto error; - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unknown URI parameter '%s'"), param->name); + if (STREQ(param->name, "socket")) { + VIR_FREE(sock_path); + if (VIR_STRDUP(sock_path, param->value) < 0) goto error; - } + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unknown URI parameter '%s'"), param->name); + goto error; } } if (!sock_path) { - if (!uri || !uri->path || STREQ(uri->path, "/system")) { + if (STRNEQ(uri->scheme, "libvirtd")) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported URI scheme '%s'"), + uri->scheme); + goto error; + } + if (STREQ_NULLABLE(uri->path, "/system")) { if (VIR_STRDUP(sock_path, LIBVIRTD_ADMIN_UNIX_SOCKET) < 0) goto error; } else if (STREQ_NULLABLE(uri->path, "/session")) { - if (!rundir) - goto error; - - if (virAsprintf(&sock_path, - "%s%s", rundir, LIBVIRTD_ADMIN_SOCK_NAME) < 0) + if (!rundir || virAsprintf(&sock_path, "%s%s", rundir, + LIBVIRTD_ADMIN_SOCK_NAME) < 0) goto error; } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Invalid URI path '%s'"), uri->path); + _("Invalid URI path '%s', try '/system'"), + uri->path ? uri->path : ""); goto error; } } cleanup: VIR_FREE(rundir); - virURIFree(uri); return sock_path; error: @@ -156,9 +151,40 @@ getSocketPath(const char *name) goto cleanup; } +static const char * +virAdmGetDefaultURI(virConfPtr conf) +{ + virConfValuePtr value = NULL; + const char *uristr = NULL; + + uristr = virGetEnvAllowSUID("LIBVIRT_ADMIN_DEFAULT_URI"); + if (uristr && *uristr) { + VIR_DEBUG("Using LIBVIRT_ADMIN_DEFAULT_URI '%s'", uristr); + } else if ((value = virConfGetValue(conf, "admin_uri_default"))) { + if (value->type != VIR_CONF_STRING) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected a string for 'admin_uri_default' config " + "parameter")); + return NULL; + } + + VIR_DEBUG("Using config file uri '%s'", value->str); + uristr = value->str; + } else { + /* Since we can't probe connecting via any hypervisor driver as libvirt + * does, if no explicit URI was given and neither the environment + * variable, nor the configuration parameter had previously been set, + * we set the default admin server URI to 'libvirtd://system'. + */ + uristr = "libvirtd:///system"; + } + + return uristr; +} + /** * virAdmConnectOpen: - * @name: uri of the daemon to connect to, NULL for default + * @uri: uri of the daemon to connect to, NULL for default * @flags: unused, must be 0 * * Opens connection to admin interface of the daemon. @@ -166,10 +192,12 @@ getSocketPath(const char *name) * Returns @virAdmConnectPtr object or NULL on error */ virAdmConnectPtr -virAdmConnectOpen(const char *name, unsigned int flags) +virAdmConnectOpen(const char *uri, unsigned int flags) { char *sock_path = NULL; + const char *default_uri = NULL; virAdmConnectPtr conn = NULL; + virConfPtr conf = NULL; if (virAdmInitialize() < 0) goto error; @@ -180,7 +208,16 @@ virAdmConnectOpen(const char *name, unsigned int flags) if (!(conn = virAdmConnectNew())) goto error; - if (!(sock_path = getSocketPath(name))) + if (virConfLoadDefault(&conf) < 0) + goto error; + + if (!uri && !(default_uri = virAdmGetDefaultURI(conf))) + goto error; + + if (!(conn->uri = virURIParse(uri ? uri : default_uri))) + goto error; + + if (!(sock_path = getSocketPath(conn->uri))) goto error; if (!(conn->privateData = remoteAdminPrivNew(sock_path))) @@ -193,6 +230,7 @@ virAdmConnectOpen(const char *name, unsigned int flags) cleanup: VIR_FREE(sock_path); + virConfFree(conf); return conn; error: @@ -302,3 +340,30 @@ virAdmConnectIsAlive(virAdmConnectPtr conn) return ret; } + +/** + * virAdmConnectGetURI: + * @conn: pointer to an admin connection + * + * String returned by this method is normally the same as the string passed + * to the virAdmConnectOpen. Even if NULL was passed to virAdmConnectOpen, + * this method returns a non-null URI string. + * + * Returns an URI string related to the connection or NULL in case of an error. + * Caller is responsible for freeing the string. + */ +char * +virAdmConnectGetURI(virAdmConnectPtr conn) +{ + char *uri = NULL; + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + virCheckAdmConnectReturn(conn, NULL); + + if (!(uri = virURIFormat(conn->uri))) + virDispatchError(NULL); + + return uri; +} diff --git a/src/libvirt.conf b/src/libvirt.conf index 016cd24..a6e16ae 100644 --- a/src/libvirt.conf +++ b/src/libvirt.conf @@ -12,7 +12,8 @@ #] # -# This can be used to prevent probing of the hypervisor -# driver when no URI is supplied by the application. - +# These can be used in cases when no URI is supplied by the application +# (@uri_default also prevents probing of the hypervisor driver). +# #uri_default = "qemu:///system" +#uri_default_admin = "libvirtd:///system" diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index 16cfd42..069508c 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -16,4 +16,5 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectClose; virAdmConnectRef; virAdmConnectIsAlive; + virAdmConnectGetURI; }; diff --git a/tools/virt-admin.c b/tools/virt-admin.c index 6b009ea..ce36303 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -113,6 +113,39 @@ vshAdmReconnect(vshControl *ctl) priv->wantReconnect = false; } +/* + * 'uri' command + */ + +static const vshCmdInfo info_uri[] = { + {.name = "help", + .data = N_("print the admin server URI") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static bool +cmdURI(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + char *uri; + vshAdmControlPtr priv = ctl->privData; + + uri = virAdmConnectGetURI(priv->conn); + if (!uri) { + vshError(ctl, "%s", _("failed to get URI")); + return false; + } + + vshPrint(ctl, "%s\n", uri); + VIR_FREE(uri); + + return true; +} + + /* --------------- * Command Connect * --------------- @@ -434,6 +467,12 @@ static const vshCmdDef vshAdmCmds[] = { VSH_CMD_HELP, VSH_CMD_PWD, VSH_CMD_QUIT, + {.name = "uri", + .handler = cmdURI, + .opts = NULL, + .info = info_uri, + .flags = 0 + }, {.name = "connect", .handler = cmdConnect, .opts = opts_connect, -- 2.4.3

On Fri, Nov 06, 2015 at 12:46:21PM +0100, Erik Skultety wrote:
Since virt-admin should be able to connect to various admin servers on hosted different daemons, we need to provide URI support to libvirt-admin. --- include/libvirt/libvirt-admin.h | 2 + src/datatypes.c | 2 + src/datatypes.h | 1 + src/libvirt-admin.c | 129 ++++++++++++++++++++++++++++++---------- src/libvirt.conf | 7 ++- src/libvirt_admin_public.syms | 1 + tools/virt-admin.c | 39 ++++++++++++ 7 files changed, 146 insertions(+), 35 deletions(-)
diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 72671c6..145720d 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -54,6 +54,8 @@ int virAdmConnectClose(virAdmConnectPtr conn); int virAdmConnectRef(virAdmConnectPtr conn); int virAdmConnectIsAlive(virAdmConnectPtr conn);
+char *virAdmConnectGetURI(virAdmConnectPtr conn); + # ifdef __cplusplus } # endif diff --git a/src/datatypes.c b/src/datatypes.c index 12bcfc1..aeac301 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -832,4 +832,6 @@ virAdmConnectDispose(void *obj)
if (conn->privateDataFreeFunc) conn->privateDataFreeFunc(conn); + + virURIFree(conn->uri); } diff --git a/src/datatypes.h b/src/datatypes.h index be108fe..b16bdb1 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -397,6 +397,7 @@ struct _virConnect { */ struct _virAdmConnect { virObjectLockable object; + virURIPtr uri;
void *privateData; virFreeCallback privateDataFreeFunc; diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 1367164..599c30a 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -27,6 +27,7 @@ #include "configmake.h"
#include "viralloc.h" +#include "virconf.h" #include "virlog.h" #include "virnetclient.h" #include "virobject.h" @@ -95,60 +96,54 @@ virAdmInitialize(void) }
static char * -getSocketPath(const char *name) +getSocketPath(virURIPtr uri)
Oh, you will hate me for this... I know I made you change the "name" to "uri" or the other way around or whatever, it doesn't matter. I just noticed that we're not consistent. I don't care what word we use, as long as it's consistent. I just see that virt-admin uses connname (as virsh does) and maybe it looks better considering the consistency between libvirt and libvirt-admin.
{ char *rundir = virGetUserRuntimeDirectory(); char *sock_path = NULL; size_t i = 0; - virURIPtr uri = NULL;
- if (name) { - if (!(uri = virURIParse(name))) - goto error; + if (!uri) + goto cleanup;
- if (STRNEQ(uri->scheme, "admin") || - uri->server || uri->user || uri->fragment) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Invalid connection name '%s'"), name); - goto error; - }
- for (i = 0; i < uri->paramsCount; i++) { - virURIParamPtr param = &uri->params[i]; + for (i = 0; i < uri->paramsCount; i++) { + virURIParamPtr param = &uri->params[i];
- if (STREQ(param->name, "socket")) { - VIR_FREE(sock_path); - if (VIR_STRDUP(sock_path, param->value) < 0) - goto error; - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unknown URI parameter '%s'"), param->name); + if (STREQ(param->name, "socket")) { + VIR_FREE(sock_path); + if (VIR_STRDUP(sock_path, param->value) < 0) goto error; - } + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unknown URI parameter '%s'"), param->name); + goto error; } }
if (!sock_path) { - if (!uri || !uri->path || STREQ(uri->path, "/system")) { + if (STRNEQ(uri->scheme, "libvirtd")) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported URI scheme '%s'"), + uri->scheme); + goto error; + } + if (STREQ_NULLABLE(uri->path, "/system")) { if (VIR_STRDUP(sock_path, LIBVIRTD_ADMIN_UNIX_SOCKET) < 0) goto error; } else if (STREQ_NULLABLE(uri->path, "/session")) { - if (!rundir) - goto error; - - if (virAsprintf(&sock_path, - "%s%s", rundir, LIBVIRTD_ADMIN_SOCK_NAME) < 0) + if (!rundir || virAsprintf(&sock_path, "%s%s", rundir, + LIBVIRTD_ADMIN_SOCK_NAME) < 0) goto error; } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Invalid URI path '%s'"), uri->path); + _("Invalid URI path '%s', try '/system'"), + uri->path ? uri->path : ""); goto error; } }
cleanup: VIR_FREE(rundir); - virURIFree(uri); return sock_path;
error: @@ -156,9 +151,40 @@ getSocketPath(const char *name) goto cleanup; }
+static const char * +virAdmGetDefaultURI(virConfPtr conf) +{ + virConfValuePtr value = NULL; + const char *uristr = NULL; + + uristr = virGetEnvAllowSUID("LIBVIRT_ADMIN_DEFAULT_URI"); + if (uristr && *uristr) { + VIR_DEBUG("Using LIBVIRT_ADMIN_DEFAULT_URI '%s'", uristr); + } else if ((value = virConfGetValue(conf, "admin_uri_default"))) { + if (value->type != VIR_CONF_STRING) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected a string for 'admin_uri_default' config " + "parameter")); + return NULL; + } + + VIR_DEBUG("Using config file uri '%s'", value->str); + uristr = value->str; + } else { + /* Since we can't probe connecting via any hypervisor driver as libvirt + * does, if no explicit URI was given and neither the environment + * variable, nor the configuration parameter had previously been set, + * we set the default admin server URI to 'libvirtd://system'. + */ + uristr = "libvirtd:///system"; + } + + return uristr; +} + /** * virAdmConnectOpen: - * @name: uri of the daemon to connect to, NULL for default + * @uri: uri of the daemon to connect to, NULL for default * @flags: unused, must be 0 * * Opens connection to admin interface of the daemon. @@ -166,10 +192,12 @@ getSocketPath(const char *name) * Returns @virAdmConnectPtr object or NULL on error */ virAdmConnectPtr -virAdmConnectOpen(const char *name, unsigned int flags) +virAdmConnectOpen(const char *uri, unsigned int flags) { char *sock_path = NULL; + const char *default_uri = NULL;
You don't need this...
virAdmConnectPtr conn = NULL; + virConfPtr conf = NULL;
if (virAdmInitialize() < 0) goto error; @@ -180,7 +208,16 @@ virAdmConnectOpen(const char *name, unsigned int flags) if (!(conn = virAdmConnectNew())) goto error;
- if (!(sock_path = getSocketPath(name))) + if (virConfLoadDefault(&conf) < 0) + goto error; + + if (!uri && !(default_uri = virAdmGetDefaultURI(conf)))
...you can save the virAdmGetDefaultURI() to @uri...
+ goto error; + + if (!(conn->uri = virURIParse(uri ? uri : default_uri)))
...and get rid of this ternary operator. ACK with that fixed (and consistency of uri<->name kept ;) ). also ACK to patches 4 and 5 (mentioning here just to save your inbox)

As we need to provide support for URI aliases in libvirt-admin as well, URI alias matching needs to be internally visible. Since virConnectOpenResolveURIAlias does have a compatible signature, it could be easily reused by libvirt-admin. This patch moves URI alias matching to util, renaming it accordingly. --- src/libvirt.c | 78 +--------------------------------------- src/libvirt_private.syms | 1 + src/util/viruri.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util/viruri.h | 2 ++ 4 files changed, 96 insertions(+), 77 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index e3c35e4..2dd94bf 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -908,82 +908,6 @@ virGetVersion(unsigned long *libVer, const char *type ATTRIBUTE_UNUSED, return -1; } -#define URI_ALIAS_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-" - - -static int -virConnectOpenFindURIAliasMatch(virConfValuePtr value, const char *alias, - char **uri) -{ - virConfValuePtr entry; - size_t alias_len; - - if (value->type != VIR_CONF_LIST) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Expected a list for 'uri_aliases' config parameter")); - return -1; - } - - entry = value->list; - alias_len = strlen(alias); - while (entry) { - char *offset; - size_t safe; - - if (entry->type != VIR_CONF_STRING) { - virReportError(VIR_ERR_CONF_SYNTAX, "%s", - _("Expected a string for 'uri_aliases' config parameter list entry")); - return -1; - } - - if (!(offset = strchr(entry->str, '='))) { - virReportError(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)) { - virReportError(VIR_ERR_CONF_SYNTAX, - _("Malformed 'uri_aliases' config entry '%s', aliases may only contain 'a-Z, 0-9, _, -'"), - entry->str); - return -1; - } - - if (alias_len == (offset - entry->str) && - STREQLEN(entry->str, alias, alias_len)) { - VIR_DEBUG("Resolved alias '%s' to '%s'", - alias, offset+1); - return VIR_STRDUP(*uri, offset+1); - } - - entry = entry->next; - } - - VIR_DEBUG("No alias found for '%s', passing through to drivers", - alias); - return 0; -} - - -static int -virConnectOpenResolveURIAlias(virConfPtr conf, - const char *alias, char **uri) -{ - int ret = -1; - virConfValuePtr value = NULL; - - *uri = NULL; - - if ((value = virConfGetValue(conf, "uri_aliases"))) - ret = virConnectOpenFindURIAliasMatch(value, alias, uri); - else - ret = 0; - - return ret; -} - static int virConnectGetDefaultURI(virConfPtr conf, @@ -1059,7 +983,7 @@ do_open(const char *name, name = "xen:///"; if (!(flags & VIR_CONNECT_NO_ALIASES) && - virConnectOpenResolveURIAlias(conf, name, &alias) < 0) + virURIResolveAlias(conf, name, &alias) < 0) goto failed; if (!(ret->uri = virURIParse(alias ? alias : name))) { diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 98f3351..02a8d7e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2322,6 +2322,7 @@ virURIFormat; virURIFormatParams; virURIFree; virURIParse; +virURIResolveAlias; # util/virusb.h diff --git a/src/util/viruri.c b/src/util/viruri.c index 6166c37..16d27db 100644 --- a/src/util/viruri.c +++ b/src/util/viruri.c @@ -25,10 +25,14 @@ #include "viralloc.h" #include "virerror.h" #include "virbuffer.h" +#include "virlog.h" #include "virstring.h" +#include "virutil.h" #define VIR_FROM_THIS VIR_FROM_URI +VIR_LOG_INIT("util.uri"); + static int virURIParamAppend(virURIPtr uri, const char *name, @@ -311,3 +315,91 @@ void virURIFree(virURIPtr uri) VIR_FREE(uri); } + + +#define URI_ALIAS_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-" + +static int +virURIFindAliasMatch(virConfValuePtr value, const char *alias, + char **uri) +{ + virConfValuePtr entry; + size_t alias_len; + + if (value->type != VIR_CONF_LIST) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected a list for 'uri_aliases' config parameter")); + return -1; + } + + entry = value->list; + alias_len = strlen(alias); + while (entry) { + char *offset; + size_t safe; + + if (entry->type != VIR_CONF_STRING) { + virReportError(VIR_ERR_CONF_SYNTAX, "%s", + _("Expected a string for 'uri_aliases' config " + "parameter list entry")); + return -1; + } + + if (!(offset = strchr(entry->str, '='))) { + virReportError(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)) { + virReportError(VIR_ERR_CONF_SYNTAX, + _("Malformed 'uri_aliases' config entry '%s', " + "aliases may only contain 'a-Z, 0-9, _, -'"), + entry->str); + return -1; + } + + if (alias_len == (offset - entry->str) && + STREQLEN(entry->str, alias, alias_len)) { + VIR_DEBUG("Resolved alias '%s' to '%s'", + alias, offset+1); + return VIR_STRDUP(*uri, offset+1); + } + + entry = entry->next; + } + + VIR_DEBUG("No alias found for '%s', continuing...", + alias); + return 0; +} + + +/** + * virURIResolveAlias: + * @conf: configuration file handler + * @alias: URI alias to be resolved + * @uri: URI object reference where the resolved URI should be stored + * + * Resolves @alias to a canonical URI according to our configuration + * file. + * + * Returns 0 on success, -1 on error. + */ +int +virURIResolveAlias(virConfPtr conf, const char *alias, char **uri) +{ + int ret = -1; + virConfValuePtr value = NULL; + + *uri = NULL; + + if ((value = virConfGetValue(conf, "uri_aliases"))) + ret = virURIFindAliasMatch(value, alias, uri); + else + ret = 0; + + return ret; +} diff --git a/src/util/viruri.h b/src/util/viruri.h index 3cfc7d3..1e53abb 100644 --- a/src/util/viruri.h +++ b/src/util/viruri.h @@ -24,6 +24,7 @@ # include <libxml/uri.h> # include "internal.h" +# include "virconf.h" typedef struct _virURI virURI; typedef virURI *virURIPtr; @@ -59,6 +60,7 @@ char *virURIFormat(virURIPtr uri) char *virURIFormatParams(virURIPtr uri); void virURIFree(virURIPtr uri); +int virURIResolveAlias(virConfPtr conf, const char *alias, char **uri); # define VIR_URI_SERVER(uri) ((uri) && (uri)->server ? (uri)->server : "localhost") -- 2.4.3

On Fri, Nov 06, 2015 at 12:46:22PM +0100, Erik Skultety wrote:
As we need to provide support for URI aliases in libvirt-admin as well, URI alias matching needs to be internally visible. Since virConnectOpenResolveURIAlias does have a compatible signature, it could be easily reused by libvirt-admin. This patch moves URI alias matching to util, renaming it accordingly.
This illustrates the reason why we should not have the same libvirt.conf file used by both libvirt.so and libvirt-admin.so. The URIs you need to pass to libvirt-admin.so are a different scheme from the URIs you need to pass to libvirt.so. So we don't want the same uri_aliases conf parameter mixing up 2 different sets of URIs. So we definitely need a separate libvirt-admin.conf file Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Fri, Nov 06, 2015 at 12:46:22PM +0100, Erik Skultety wrote:
As we need to provide support for URI aliases in libvirt-admin as well, URI alias matching needs to be internally visible. Since virConnectOpenResolveURIAlias does have a compatible signature, it could be easily reused by libvirt-admin. This patch moves URI alias matching to util, renaming it accordingly. --- src/libvirt.c | 78 +--------------------------------------- src/libvirt_private.syms | 1 + src/util/viruri.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util/viruri.h | 2 ++ 4 files changed, 96 insertions(+), 77 deletions(-)
diff --git a/src/util/viruri.c b/src/util/viruri.c index 6166c37..16d27db 100644 --- a/src/util/viruri.c +++ b/src/util/viruri.c @@ -311,3 +315,91 @@ void virURIFree(virURIPtr uri)
VIR_FREE(uri); } + + +#define URI_ALIAS_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-" + +static int +virURIFindAliasMatch(virConfValuePtr value, const char *alias, + char **uri) +{ + virConfValuePtr entry; + size_t alias_len; + + if (value->type != VIR_CONF_LIST) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected a list for 'uri_aliases' config parameter")); + return -1; + } + + entry = value->list; + alias_len = strlen(alias); + while (entry) { + char *offset; + size_t safe; + + if (entry->type != VIR_CONF_STRING) { + virReportError(VIR_ERR_CONF_SYNTAX, "%s", + _("Expected a string for 'uri_aliases' config " + "parameter list entry")); + return -1; + } + + if (!(offset = strchr(entry->str, '='))) { + virReportError(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);
Double whitespace after 'safe' ^^. ACK with that fixed.

Now that we introduced URI support in libvirt-admin, we should also support URI aliases during connection establishment phase. After applying this patch, virAdmConnectOpen will also support VIR_CONNECT_NO_ALIASES flag. --- src/libvirt-admin.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 599c30a..d9a20fc 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -196,6 +196,7 @@ virAdmConnectOpen(const char *uri, unsigned int flags) { char *sock_path = NULL; const char *default_uri = NULL; + char *alias = NULL; virAdmConnectPtr conn = NULL; virConfPtr conf = NULL; @@ -204,6 +205,7 @@ virAdmConnectOpen(const char *uri, unsigned int flags) VIR_DEBUG("flags=%x", flags); virResetLastError(); + virCheckFlags(VIR_CONNECT_NO_ALIASES, NULL); if (!(conn = virAdmConnectNew())) goto error; @@ -214,7 +216,11 @@ virAdmConnectOpen(const char *uri, unsigned int flags) if (!uri && !(default_uri = virAdmGetDefaultURI(conf))) goto error; - if (!(conn->uri = virURIParse(uri ? uri : default_uri))) + if ((!(flags & VIR_CONNECT_NO_ALIASES) && + virURIResolveAlias(conf, uri ? uri : default_uri, &alias) < 0)) + goto error; + + if (!(conn->uri = virURIParse(alias ? alias : (uri ? uri : default_uri)))) goto error; if (!(sock_path = getSocketPath(conn->uri))) @@ -230,6 +236,7 @@ virAdmConnectOpen(const char *uri, unsigned int flags) cleanup: VIR_FREE(sock_path); + VIR_FREE(alias); virConfFree(conf); return conn; -- 2.4.3

On Fri, Nov 06, 2015 at 12:46:23PM +0100, Erik Skultety wrote:
Now that we introduced URI support in libvirt-admin, we should also support URI aliases during connection establishment phase. After applying this patch, virAdmConnectOpen will also support VIR_CONNECT_NO_ALIASES flag. --- src/libvirt-admin.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 599c30a..d9a20fc 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -196,6 +196,7 @@ virAdmConnectOpen(const char *uri, unsigned int flags) { char *sock_path = NULL; const char *default_uri = NULL; + char *alias = NULL; virAdmConnectPtr conn = NULL; virConfPtr conf = NULL;
@@ -204,6 +205,7 @@ virAdmConnectOpen(const char *uri, unsigned int flags)
VIR_DEBUG("flags=%x", flags); virResetLastError(); + virCheckFlags(VIR_CONNECT_NO_ALIASES, NULL);
if (!(conn = virAdmConnectNew())) goto error; @@ -214,7 +216,11 @@ virAdmConnectOpen(const char *uri, unsigned int flags) if (!uri && !(default_uri = virAdmGetDefaultURI(conf))) goto error;
- if (!(conn->uri = virURIParse(uri ? uri : default_uri))) + if ((!(flags & VIR_CONNECT_NO_ALIASES) && + virURIResolveAlias(conf, uri ? uri : default_uri, &alias) < 0))
this should also be fixed (with what I mentioned in previous review).
+ goto error; + + if (!(conn->uri = virURIParse(alias ? alias : (uri ? uri : default_uri))))
Also, if you modify virURIResolveAlias() to simply copy the string over to @alias if the alias is not found, that would be pretty nice syntax-sugar and spare our souls from this ugly thing =) ACK with that fixed.

- if (!(conn->uri = virURIParse(uri ? uri : default_uri))) + if ((!(flags & VIR_CONNECT_NO_ALIASES) && + virURIResolveAlias(conf, uri ? uri : default_uri, &alias) < 0))
this should also be fixed (with what I mentioned in previous review).
Fixed.
+ goto error; + + if (!(conn->uri = virURIParse(alias ? alias : (uri ? uri : default_uri))))
Also, if you modify virURIResolveAlias() to simply copy the string over to @alias if the alias is not found, that would be pretty nice syntax-sugar and spare our souls from this ugly thing =)
ACK with that fixed.
Hmm, I think that in ideal world 1 API should do exactly 1 thing and 1 thing only, but we're not living in an ideal world, do we?! However, in this specific case I don't think it's worth changing alias resolving in a way that would copy the searched alias into URI string if the alias wasn't matched. I could do a wrapper encompassing the alias resolving and this proposed syntactic sugar, but it still wouldn't make it worth I guess, since the resolver isn't executed every time unless appropriate flags have been set. Moreover, since I fixed the ternary operator you suggested above, the other one also becomes more readable: virURIParse(alias ? alias : uri)....what do you think? PS: thanks for the ACK though Erik

On Thu, Nov 19, 2015 at 02:00:16PM +0100, Erik Skultety wrote:
- if (!(conn->uri = virURIParse(uri ? uri : default_uri))) + if ((!(flags & VIR_CONNECT_NO_ALIASES) && + virURIResolveAlias(conf, uri ? uri : default_uri, &alias) < 0))
this should also be fixed (with what I mentioned in previous review).
Fixed.
+ goto error; + + if (!(conn->uri = virURIParse(alias ? alias : (uri ? uri : default_uri))))
Also, if you modify virURIResolveAlias() to simply copy the string over to @alias if the alias is not found, that would be pretty nice syntax-sugar and spare our souls from this ugly thing =)
ACK with that fixed.
Hmm, I think that in ideal world 1 API should do exactly 1 thing and 1 thing only, but we're not living in an ideal world, do we?! However, in this specific case I don't think it's worth changing alias resolving in a way that would copy the searched alias into URI string if the alias wasn't matched. I could do a wrapper encompassing the alias resolving and this proposed syntactic sugar, but it still wouldn't make it worth I guess, since the resolver isn't executed every time unless appropriate flags have been set. Moreover, since I fixed the ternary operator you suggested above, the other one also becomes more readable: virURIParse(alias ? alias : uri)....what do you think?
Well, one is better than two in this case, so at least one is fixed. You don't need to change that just for the purpose of creating another syntactic sugar, that was just a hint if you would like to do that.
PS: thanks for the ACK though
Erik

As we need a client disconnect handler, we also need a mechanism to register such handlers for a client. This patch introduced both the close callbacks and also the client vshAdmCatchDisconnect handler to be registered with it. By registering the handler we still need to make sure the client can react to daemon's events like disconnect or keepalive, so asynchronous I/O event polling is necessary to be enabled too. --- cfg.mk | 2 +- include/libvirt/libvirt-admin.h | 21 ++++++++ src/admin/admin_remote.c | 35 +++++++++++++ src/datatypes.c | 24 +++++++++ src/datatypes.h | 16 +++++- src/libvirt-admin.c | 112 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 2 + tools/virt-admin.c | 50 ++++++++++++++++++ 8 files changed, 260 insertions(+), 2 deletions(-) diff --git a/cfg.mk b/cfg.mk index a9bba38..43ee945 100644 --- a/cfg.mk +++ b/cfg.mk @@ -1192,7 +1192,7 @@ exclude_file_name_regexp--sc_prohibit_include_public_headers_quote = \ ^(src/internal\.h$$|tools/wireshark/src/packet-libvirt.h$$) exclude_file_name_regexp--sc_prohibit_include_public_headers_brackets = \ - ^(tools/|examples/|include/libvirt/(virterror|libvirt-(qemu|lxc))\.h$$) + ^(tools/|examples/|include/libvirt/(virterror|libvirt-(admin|qemu|lxc))\.h$$) exclude_file_name_regexp--sc_prohibit_int_ijk = \ ^(src/remote_protocol-structs|src/remote/remote_protocol.x|cfg.mk|include/)$ diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 145720d..5f9841c 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -26,6 +26,8 @@ #ifndef __VIR_ADMIN_H__ # define __VIR_ADMIN_H__ +# include <libvirt/libvirt.h> + # ifdef __cplusplus extern "C" { # endif @@ -56,6 +58,25 @@ int virAdmConnectIsAlive(virAdmConnectPtr conn); char *virAdmConnectGetURI(virAdmConnectPtr conn); +/** + * virAdmConnectCloseFunc: + * @conn: virAdmConnect connection + * @reason: reason why the connection was closed (see virConnectCloseReason) + * @opaque: opaque client data + * + * A callback to be registered, in case a connection was closed. + */ +typedef void (*virAdmConnectCloseFunc)(virAdmConnectPtr conn, + int reason, + void *opaque); + +int virAdmConnectRegisterCloseCallback(virAdmConnectPtr conn, + virAdmConnectCloseFunc cb, + void *opaque, + virFreeCallback freecb); +int virAdmConnectUnregisterCloseCallback(virAdmConnectPtr conn, + virAdmConnectCloseFunc cb); + # ifdef __cplusplus } # endif diff --git a/src/admin/admin_remote.c b/src/admin/admin_remote.c index 2c02ba6..7b40ea1 100644 --- a/src/admin/admin_remote.c +++ b/src/admin/admin_remote.c @@ -101,6 +101,28 @@ call(virAdmConnectPtr conn, #include "admin_client.h" +static void +remoteAdminClientCloseFunc(virNetClientPtr client ATTRIBUTE_UNUSED, + int reason, + void *opaque) +{ + virAdmConnectCloseCallbackDataPtr cbdata = opaque; + + virObjectLock(cbdata); + + if (cbdata->callback) { + VIR_DEBUG("Triggering connection close callback %p reason=%d, opaque=%p", + cbdata->callback, reason, cbdata->opaque); + cbdata->callback(cbdata->conn, reason, cbdata->opaque); + + if (cbdata->freeCallback) + cbdata->freeCallback(cbdata->opaque); + cbdata->callback = NULL; + cbdata->freeCallback = NULL; + } + virObjectUnlock(cbdata); +} + static int remoteAdminConnectOpen(virAdmConnectPtr conn, unsigned int flags) { @@ -112,6 +134,17 @@ remoteAdminConnectOpen(virAdmConnectPtr conn, unsigned int flags) args.flags = flags; + if (virNetClientRegisterAsyncIO(priv->client) < 0) { + VIR_DEBUG("Failed to add event watch, disabling events and support for" + " keepalive messages"); + virResetLastError(); + } + + virObjectRef(conn->closeCallback); + virNetClientSetCloseCallback(priv->client, remoteAdminClientCloseFunc, + conn->closeCallback, + virObjectFreeCallback); + if (call(conn, 0, ADMIN_PROC_CONNECT_OPEN, (xdrproc_t)xdr_admin_connect_open_args, (char *)&args, (xdrproc_t)xdr_void, (char *)NULL) == -1) { @@ -139,6 +172,8 @@ remoteAdminConnectClose(virAdmConnectPtr conn) goto done; } + virNetClientSetCloseCallback(priv->client, NULL, NULL, NULL); + rv = 0; done: diff --git a/src/datatypes.c b/src/datatypes.c index aeac301..c832d80 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -60,8 +60,10 @@ static void virStorageVolDispose(void *obj); static void virStoragePoolDispose(void *obj); virClassPtr virAdmConnectClass; +virClassPtr virAdmConnectCloseCallbackDataClass; static void virAdmConnectDispose(void *obj); +static void virAdmConnectCloseCallbackDataDispose(void *obj); static int virDataTypesOnceInit(void) @@ -91,6 +93,7 @@ virDataTypesOnceInit(void) DECLARE_CLASS(virStoragePool); DECLARE_CLASS_LOCKABLE(virAdmConnect); + DECLARE_CLASS_LOCKABLE(virAdmConnectCloseCallbackData); #undef DECLARE_CLASS_COMMON #undef DECLARE_CLASS_LOCKABLE @@ -822,7 +825,14 @@ virAdmConnectNew(void) if (!(ret = virObjectLockableNew(virAdmConnectClass))) return NULL; + if (!(ret->closeCallback = virObjectLockableNew(virAdmConnectCloseCallbackDataClass))) + goto error; + return ret; + + error: + virObjectUnref(ret); + return NULL; } static void @@ -834,4 +844,18 @@ virAdmConnectDispose(void *obj) conn->privateDataFreeFunc(conn); virURIFree(conn->uri); + virObjectUnref(conn->closeCallback); +} + +static void +virAdmConnectCloseCallbackDataDispose(void *obj) +{ + virAdmConnectCloseCallbackDataPtr cb_data = obj; + + virObjectLock(cb_data); + + if (cb_data->freeCallback) + cb_data->freeCallback(cb_data->opaque); + + virObjectUnlock(cb_data); } diff --git a/src/datatypes.h b/src/datatypes.h index b16bdb1..1b1777d 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -331,9 +331,11 @@ extern virClassPtr virAdmConnectClass; typedef struct _virConnectCloseCallbackData virConnectCloseCallbackData; typedef virConnectCloseCallbackData *virConnectCloseCallbackDataPtr; +typedef struct _virAdmConnectCloseCallbackData virAdmConnectCloseCallbackData; +typedef virAdmConnectCloseCallbackData *virAdmConnectCloseCallbackDataPtr; /** - * Internal structure holding data related to connection close callbacks. + * Internal structures holding data related to connection close callbacks. */ struct _virConnectCloseCallbackData { virObjectLockable parent; @@ -344,6 +346,15 @@ struct _virConnectCloseCallbackData { virFreeCallback freeCallback; }; +struct _virAdmConnectCloseCallbackData { + virObjectLockable parent; + + virAdmConnectPtr conn; + virAdmConnectCloseFunc callback; + void *opaque; + virFreeCallback freeCallback; +}; + /** * _virConnect: * @@ -401,6 +412,9 @@ struct _virAdmConnect { void *privateData; virFreeCallback privateDataFreeFunc; + + /* Per-connection close callback */ + virAdmConnectCloseCallbackDataPtr closeCallback; }; diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index d9a20fc..fdcf2c1 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -374,3 +374,115 @@ virAdmConnectGetURI(virAdmConnectPtr conn) return uri; } + +/** + * virAdmConnectRegisterCloseCallback: + * @conn: connection to admin server + * @cb: callback to be invoked upon connection close + * @opaque: user data to pass to @cb + * @freecb: callback to free @opaque + * + * Registers a callback to be invoked when the connection + * is closed. This callback is invoked when there is any + * condition that causes the socket connection to the + * hypervisor to be closed. + * + * The @freecb must not invoke any other libvirt public + * APIs, since it is not called from a re-entrant safe + * context. + * + * Returns 0 on success, -1 on error + */ +int virAdmConnectRegisterCloseCallback(virAdmConnectPtr conn, + virAdmConnectCloseFunc cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + virCheckAdmConnectReturn(conn, -1); + + virObjectRef(conn); + + virObjectLock(conn); + virObjectLock(conn->closeCallback); + + virCheckNonNullArgGoto(cb, error); + + if (conn->closeCallback->callback) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("A close callback is already registered")); + goto error; + } + + conn->closeCallback->conn = conn; + conn->closeCallback->callback = cb; + conn->closeCallback->opaque = opaque; + conn->closeCallback->freeCallback = freecb; + + virObjectUnlock(conn->closeCallback); + virObjectUnlock(conn); + + return 0; + + error: + virObjectUnlock(conn->closeCallback); + virObjectUnlock(conn); + virDispatchError(NULL); + virObjectUnref(conn); + return -1; + +} + +/** + * virAdmConnectUnregisterCloseCallback: + * @conn: pointer to connection object + * @cb: pointer to the current registered callback + * + * Unregisters the callback previously set with the + * virAdmConnectRegisterCloseCallback method. The callback + * will no longer receive notifications when the connection + * closes. If a virFreeCallback was provided at time of + * registration, it will be invoked. + * + * Returns 0 on success, -1 on error + */ +int virAdmConnectUnregisterCloseCallback(virAdmConnectPtr conn, + virAdmConnectCloseFunc cb) +{ + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + virCheckAdmConnectReturn(conn, -1); + + virObjectLock(conn); + virObjectLock(conn->closeCallback); + + virCheckNonNullArgGoto(cb, error); + + if (conn->closeCallback->callback != cb) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("A different callback was requested")); + goto error; + } + + conn->closeCallback->callback = NULL; + if (conn->closeCallback->freeCallback) + conn->closeCallback->freeCallback(conn->closeCallback->opaque); + conn->closeCallback->freeCallback = NULL; + + virObjectUnlock(conn->closeCallback); + virObjectUnlock(conn); + virObjectUnref(conn); + + return 0; + + error: + virObjectUnlock(conn->closeCallback); + virObjectUnlock(conn); + virDispatchError(NULL); + return -1; +} diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index 069508c..df01837 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -17,4 +17,6 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectRef; virAdmConnectIsAlive; virAdmConnectGetURI; + virAdmConnectRegisterCloseCallback; + virAdmConnectUnregisterCloseCallback; }; diff --git a/tools/virt-admin.c b/tools/virt-admin.c index ce36303..fc98964 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -52,6 +52,51 @@ static char *progname; static const vshCmdGrp cmdGroups[]; static const vshClientHooks hooks; +/* + * vshAdmCatchDisconnect: + * + * We get here when the connection was closed. Unlike virsh, we do not save + * the fact that the event was raised, sice there is virAdmConnectIsAlive to + * check if the communication channel has not been closed by remote party. + */ +static void +vshAdmCatchDisconnect(virAdmConnectPtr conn ATTRIBUTE_UNUSED, + int reason, + void *opaque) +{ + if (reason != VIR_CONNECT_CLOSE_REASON_CLIENT) { + vshControl *ctl = opaque; + const char *str = "unknown reason"; + virErrorPtr error; + char *uri = NULL; + + error = virSaveLastError(); + uri = virAdmConnectGetURI(conn); + + switch ((virConnectCloseReason) reason) { + case VIR_CONNECT_CLOSE_REASON_ERROR: + str = N_("Disconnected from %s due to I/O error"); + break; + case VIR_CONNECT_CLOSE_REASON_EOF: + str = N_("Disconnected from %s due to end of file"); + break; + case VIR_CONNECT_CLOSE_REASON_KEEPALIVE: + str = N_("Disconnected from %s due to keepalive timeout"); + break; + /* coverity[dead_error_condition] */ + case VIR_CONNECT_CLOSE_REASON_CLIENT: + case VIR_CONNECT_CLOSE_REASON_LAST: + break; + } + vshError(ctl, _(str), NULLSTR(uri)); + + if (error) { + virSetError(error); + virFreeError(error); + } + } +} + static int vshAdmConnect(vshControl *ctl, unsigned int flags) { @@ -66,6 +111,10 @@ vshAdmConnect(vshControl *ctl, unsigned int flags) vshError(ctl, "%s", _("Failed to connect to the admin server")); return -1; } else { + if (virAdmConnectRegisterCloseCallback(priv->conn, vshAdmCatchDisconnect, + NULL, NULL) < 0) + vshError(ctl, "%s", _("Unable to register disconnect callback")); + if (priv->wantReconnect) vshPrint(ctl, "%s\n", _("Reconnected to the admin server")); else @@ -84,6 +133,7 @@ vshAdmDisconnect(vshControl *ctl) if (!priv->conn) return ret; + virAdmConnectUnregisterCloseCallback(priv->conn, vshAdmCatchDisconnect); ret = virAdmConnectClose(priv->conn); if (ret < 0) vshError(ctl, "%s", _("Failed to disconnect from the admin server")); -- 2.4.3

On Fri, Nov 06, 2015 at 12:46:24PM +0100, Erik Skultety wrote:
As we need a client disconnect handler, we also need a mechanism to register such handlers for a client. This patch introduced both the close callbacks and also the client vshAdmCatchDisconnect handler to be registered with it. By registering the handler we still need to make sure the client can react to daemon's events like disconnect or keepalive, so asynchronous I/O event polling is necessary to be enabled too. --- cfg.mk | 2 +- include/libvirt/libvirt-admin.h | 21 ++++++++ src/admin/admin_remote.c | 35 +++++++++++++ src/datatypes.c | 24 +++++++++ src/datatypes.h | 16 +++++- src/libvirt-admin.c | 112 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 2 + tools/virt-admin.c | 50 ++++++++++++++++++ 8 files changed, 260 insertions(+), 2 deletions(-)
diff --git a/cfg.mk b/cfg.mk index a9bba38..43ee945 100644 --- a/cfg.mk +++ b/cfg.mk @@ -1192,7 +1192,7 @@ exclude_file_name_regexp--sc_prohibit_include_public_headers_quote = \ ^(src/internal\.h$$|tools/wireshark/src/packet-libvirt.h$$)
exclude_file_name_regexp--sc_prohibit_include_public_headers_brackets = \ - ^(tools/|examples/|include/libvirt/(virterror|libvirt-(qemu|lxc))\.h$$) + ^(tools/|examples/|include/libvirt/(virterror|libvirt-(admin|qemu|lxc))\.h$$)
exclude_file_name_regexp--sc_prohibit_int_ijk = \ ^(src/remote_protocol-structs|src/remote/remote_protocol.x|cfg.mk|include/)$ diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 145720d..5f9841c 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -26,6 +26,8 @@ #ifndef __VIR_ADMIN_H__ # define __VIR_ADMIN_H__
+# include <libvirt/libvirt.h> +
Oh no you didn't... This means that including libvirt-admin.h will allow seeing *everything* from libvirt. I see two things you need this for. Enum values and a function. Enum values could be defined somewhere else (e.g. datatypes.h) and that 'somewhere else' could be included instead. The function should either be re-implemented for both libraries or just moved outside and each library should have a wrapper for it. Do anything to remove this line, thanks.
# ifdef __cplusplus extern "C" { # endif @@ -56,6 +58,25 @@ int virAdmConnectIsAlive(virAdmConnectPtr conn);
char *virAdmConnectGetURI(virAdmConnectPtr conn);
+/** + * virAdmConnectCloseFunc: + * @conn: virAdmConnect connection + * @reason: reason why the connection was closed (see virConnectCloseReason) + * @opaque: opaque client data + * + * A callback to be registered, in case a connection was closed. + */ +typedef void (*virAdmConnectCloseFunc)(virAdmConnectPtr conn, + int reason, + void *opaque); + +int virAdmConnectRegisterCloseCallback(virAdmConnectPtr conn, + virAdmConnectCloseFunc cb, + void *opaque, + virFreeCallback freecb); +int virAdmConnectUnregisterCloseCallback(virAdmConnectPtr conn, + virAdmConnectCloseFunc cb); + # ifdef __cplusplus } # endif diff --git a/src/admin/admin_remote.c b/src/admin/admin_remote.c index 2c02ba6..7b40ea1 100644 --- a/src/admin/admin_remote.c +++ b/src/admin/admin_remote.c @@ -101,6 +101,28 @@ call(virAdmConnectPtr conn,
#include "admin_client.h"
+static void +remoteAdminClientCloseFunc(virNetClientPtr client ATTRIBUTE_UNUSED, + int reason, + void *opaque) +{ + virAdmConnectCloseCallbackDataPtr cbdata = opaque; + + virObjectLock(cbdata); + + if (cbdata->callback) { + VIR_DEBUG("Triggering connection close callback %p reason=%d, opaque=%p", + cbdata->callback, reason, cbdata->opaque); + cbdata->callback(cbdata->conn, reason, cbdata->opaque); + + if (cbdata->freeCallback) + cbdata->freeCallback(cbdata->opaque); + cbdata->callback = NULL; + cbdata->freeCallback = NULL; + } + virObjectUnlock(cbdata); +} + static int remoteAdminConnectOpen(virAdmConnectPtr conn, unsigned int flags) { @@ -112,6 +134,17 @@ remoteAdminConnectOpen(virAdmConnectPtr conn, unsigned int flags)
args.flags = flags;
+ if (virNetClientRegisterAsyncIO(priv->client) < 0) { + VIR_DEBUG("Failed to add event watch, disabling events and support for" + " keepalive messages");
This doesn't do anything with keepalive. But when I'm thinking about it I don't think it's worth removing just for the time until we add those few keepalive calls here.
+ virResetLastError(); + } + + virObjectRef(conn->closeCallback); + virNetClientSetCloseCallback(priv->client, remoteAdminClientCloseFunc, + conn->closeCallback, + virObjectFreeCallback); + if (call(conn, 0, ADMIN_PROC_CONNECT_OPEN, (xdrproc_t)xdr_admin_connect_open_args, (char *)&args, (xdrproc_t)xdr_void, (char *)NULL) == -1) { @@ -139,6 +172,8 @@ remoteAdminConnectClose(virAdmConnectPtr conn) goto done; }
+ virNetClientSetCloseCallback(priv->client, NULL, NULL, NULL); + rv = 0;
done: diff --git a/src/datatypes.c b/src/datatypes.c index aeac301..c832d80 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -60,8 +60,10 @@ static void virStorageVolDispose(void *obj); static void virStoragePoolDispose(void *obj);
virClassPtr virAdmConnectClass; +virClassPtr virAdmConnectCloseCallbackDataClass;
static void virAdmConnectDispose(void *obj); +static void virAdmConnectCloseCallbackDataDispose(void *obj);
static int virDataTypesOnceInit(void) @@ -91,6 +93,7 @@ virDataTypesOnceInit(void) DECLARE_CLASS(virStoragePool);
DECLARE_CLASS_LOCKABLE(virAdmConnect); + DECLARE_CLASS_LOCKABLE(virAdmConnectCloseCallbackData);
#undef DECLARE_CLASS_COMMON #undef DECLARE_CLASS_LOCKABLE @@ -822,7 +825,14 @@ virAdmConnectNew(void) if (!(ret = virObjectLockableNew(virAdmConnectClass))) return NULL;
+ if (!(ret->closeCallback = virObjectLockableNew(virAdmConnectCloseCallbackDataClass))) + goto error; + return ret; + + error: + virObjectUnref(ret); + return NULL; }
static void @@ -834,4 +844,18 @@ virAdmConnectDispose(void *obj) conn->privateDataFreeFunc(conn);
virURIFree(conn->uri); + virObjectUnref(conn->closeCallback); +} + +static void +virAdmConnectCloseCallbackDataDispose(void *obj) +{ + virAdmConnectCloseCallbackDataPtr cb_data = obj; + + virObjectLock(cb_data); + + if (cb_data->freeCallback) + cb_data->freeCallback(cb_data->opaque); + + virObjectUnlock(cb_data); } diff --git a/src/datatypes.h b/src/datatypes.h index b16bdb1..1b1777d 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -331,9 +331,11 @@ extern virClassPtr virAdmConnectClass;
typedef struct _virConnectCloseCallbackData virConnectCloseCallbackData; typedef virConnectCloseCallbackData *virConnectCloseCallbackDataPtr; +typedef struct _virAdmConnectCloseCallbackData virAdmConnectCloseCallbackData; +typedef virAdmConnectCloseCallbackData *virAdmConnectCloseCallbackDataPtr;
/** - * Internal structure holding data related to connection close callbacks. + * Internal structures holding data related to connection close callbacks. */ struct _virConnectCloseCallbackData { virObjectLockable parent; @@ -344,6 +346,15 @@ struct _virConnectCloseCallbackData { virFreeCallback freeCallback; };
+struct _virAdmConnectCloseCallbackData { + virObjectLockable parent; + + virAdmConnectPtr conn; + virAdmConnectCloseFunc callback; + void *opaque; + virFreeCallback freeCallback; +}; + /** * _virConnect: * @@ -401,6 +412,9 @@ struct _virAdmConnect {
void *privateData; virFreeCallback privateDataFreeFunc; + + /* Per-connection close callback */ + virAdmConnectCloseCallbackDataPtr closeCallback; };
diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index d9a20fc..fdcf2c1 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -374,3 +374,115 @@ virAdmConnectGetURI(virAdmConnectPtr conn)
return uri; } + +/** + * virAdmConnectRegisterCloseCallback: + * @conn: connection to admin server + * @cb: callback to be invoked upon connection close + * @opaque: user data to pass to @cb + * @freecb: callback to free @opaque + * + * Registers a callback to be invoked when the connection + * is closed. This callback is invoked when there is any + * condition that causes the socket connection to the + * hypervisor to be closed. + * + * The @freecb must not invoke any other libvirt public + * APIs, since it is not called from a re-entrant safe + * context. + * + * Returns 0 on success, -1 on error + */ +int virAdmConnectRegisterCloseCallback(virAdmConnectPtr conn, + virAdmConnectCloseFunc cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + virCheckAdmConnectReturn(conn, -1); + + virObjectRef(conn); + + virObjectLock(conn); + virObjectLock(conn->closeCallback); + + virCheckNonNullArgGoto(cb, error); + + if (conn->closeCallback->callback) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("A close callback is already registered")); + goto error; + } + + conn->closeCallback->conn = conn; + conn->closeCallback->callback = cb; + conn->closeCallback->opaque = opaque; + conn->closeCallback->freeCallback = freecb; + + virObjectUnlock(conn->closeCallback); + virObjectUnlock(conn); + + return 0; + + error: + virObjectUnlock(conn->closeCallback); + virObjectUnlock(conn); + virDispatchError(NULL); + virObjectUnref(conn); + return -1; + +} + +/** + * virAdmConnectUnregisterCloseCallback: + * @conn: pointer to connection object + * @cb: pointer to the current registered callback + * + * Unregisters the callback previously set with the + * virAdmConnectRegisterCloseCallback method. The callback + * will no longer receive notifications when the connection + * closes. If a virFreeCallback was provided at time of + * registration, it will be invoked. + * + * Returns 0 on success, -1 on error + */ +int virAdmConnectUnregisterCloseCallback(virAdmConnectPtr conn, + virAdmConnectCloseFunc cb) +{ + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + virCheckAdmConnectReturn(conn, -1); + + virObjectLock(conn); + virObjectLock(conn->closeCallback); + + virCheckNonNullArgGoto(cb, error); + + if (conn->closeCallback->callback != cb) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("A different callback was requested")); + goto error; + } + + conn->closeCallback->callback = NULL; + if (conn->closeCallback->freeCallback) + conn->closeCallback->freeCallback(conn->closeCallback->opaque); + conn->closeCallback->freeCallback = NULL; + + virObjectUnlock(conn->closeCallback); + virObjectUnlock(conn); + virObjectUnref(conn); + + return 0; + + error: + virObjectUnlock(conn->closeCallback); + virObjectUnlock(conn); + virDispatchError(NULL); + return -1; +} diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index 069508c..df01837 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -17,4 +17,6 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectRef; virAdmConnectIsAlive; virAdmConnectGetURI; + virAdmConnectRegisterCloseCallback; + virAdmConnectUnregisterCloseCallback; }; diff --git a/tools/virt-admin.c b/tools/virt-admin.c index ce36303..fc98964 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -52,6 +52,51 @@ static char *progname; static const vshCmdGrp cmdGroups[]; static const vshClientHooks hooks;
+/* + * vshAdmCatchDisconnect: + * + * We get here when the connection was closed. Unlike virsh, we do not save + * the fact that the event was raised, sice there is virAdmConnectIsAlive to + * check if the communication channel has not been closed by remote party. + */ +static void +vshAdmCatchDisconnect(virAdmConnectPtr conn ATTRIBUTE_UNUSED, + int reason, + void *opaque) +{ + if (reason != VIR_CONNECT_CLOSE_REASON_CLIENT) {
Please change this to if (reason == VIR_CONNECT_CLOSE_REASON_CLIENT) return; and then continue with unintended block. It's more readable. Feel free to fix it in virsh as well ;) Although I'm not sure we need that since we do unregister the close callback before disconnecting... But it's nice to be safe. Martin

On Mon, Nov 16, 2015 at 05:40:07PM +0100, Martin Kletzander wrote:
On Fri, Nov 06, 2015 at 12:46:24PM +0100, Erik Skultety wrote:
As we need a client disconnect handler, we also need a mechanism to register such handlers for a client. This patch introduced both the close callbacks and also the client vshAdmCatchDisconnect handler to be registered with it. By registering the handler we still need to make sure the client can react to daemon's events like disconnect or keepalive, so asynchronous I/O event polling is necessary to be enabled too. --- cfg.mk | 2 +- include/libvirt/libvirt-admin.h | 21 ++++++++ src/admin/admin_remote.c | 35 +++++++++++++ src/datatypes.c | 24 +++++++++ src/datatypes.h | 16 +++++- src/libvirt-admin.c | 112 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 2 + tools/virt-admin.c | 50 ++++++++++++++++++ 8 files changed, 260 insertions(+), 2 deletions(-)
diff --git a/cfg.mk b/cfg.mk index a9bba38..43ee945 100644 --- a/cfg.mk +++ b/cfg.mk @@ -1192,7 +1192,7 @@ exclude_file_name_regexp--sc_prohibit_include_public_headers_quote = \ ^(src/internal\.h$$|tools/wireshark/src/packet-libvirt.h$$)
exclude_file_name_regexp--sc_prohibit_include_public_headers_brackets = \ - ^(tools/|examples/|include/libvirt/(virterror|libvirt-(qemu|lxc))\.h$$) + ^(tools/|examples/|include/libvirt/(virterror|libvirt-(admin|qemu|lxc))\.h$$)
exclude_file_name_regexp--sc_prohibit_int_ijk = \ ^(src/remote_protocol-structs|src/remote/remote_protocol.x|cfg.mk|include/)$ diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 145720d..5f9841c 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -26,6 +26,8 @@ #ifndef __VIR_ADMIN_H__ # define __VIR_ADMIN_H__
+# include <libvirt/libvirt.h> +
Oh no you didn't... This means that including libvirt-admin.h will allow seeing *everything* from libvirt. I see two things you need this for. Enum values and a function. Enum values could be defined somewhere else (e.g. datatypes.h) and that 'somewhere else' could be included instead. The function should either be re-implemented for both libraries or just moved outside and each library should have a wrapper for it. Do anything to remove this line, thanks.
Yeah we could just have a libvirt/libvirt-common.h file for stuff that needs to be shared. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Introduce a new API to get libvirt version. It is worth noting, that libvirt-admin and libvirt share the same version number. Unfortunately, our existing API isn't generic enough to be used with virAdmConnectPtr as well. Also this patch wires up this API to the virt-admin client as a generic cmdVersion command. --- daemon/admin_server.c | 9 ++++++ include/libvirt/libvirt-admin.h | 3 ++ po/POTFILES.in | 1 + src/admin/admin_protocol.x | 11 ++++++- src/admin_protocol-structs | 4 +++ src/libvirt-admin.c | 31 +++++++++++++++++++ src/libvirt_admin_private.syms | 1 + src/libvirt_admin_public.syms | 1 + src/rpc/gendispatch.pl | 11 +++++-- tools/virt-admin.c | 67 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 135 insertions(+), 4 deletions(-) diff --git a/daemon/admin_server.c b/daemon/admin_server.c index 712a44b..189091e 100644 --- a/daemon/admin_server.c +++ b/daemon/admin_server.c @@ -114,4 +114,13 @@ adminDispatchConnectClose(virNetServerPtr server ATTRIBUTE_UNUSED, return 0; } +static int +adminConnectGetLibVersion(virNetDaemonPtr dmn ATTRIBUTE_UNUSED, + unsigned long long *libVer) +{ + if (libVer) + *libVer = LIBVIR_VERSION_NUMBER; + return 0; +} + #include "admin_dispatch.h" diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 5f9841c..e5f840c 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -58,6 +58,9 @@ int virAdmConnectIsAlive(virAdmConnectPtr conn); char *virAdmConnectGetURI(virAdmConnectPtr conn); +int virAdmConnectGetLibVersion(virAdmConnectPtr conn, + unsigned long long *libVer); + /** * virAdmConnectCloseFunc: * @conn: virAdmConnect connection diff --git a/po/POTFILES.in b/po/POTFILES.in index d0840f4..e165a08 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,3 +1,4 @@ +daemon/admin_dispatch.h daemon/admin_server.c daemon/libvirtd-config.c daemon/libvirtd.c diff --git a/src/admin/admin_protocol.x b/src/admin/admin_protocol.x index d0ca1a3..878983d 100644 --- a/src/admin/admin_protocol.x +++ b/src/admin/admin_protocol.x @@ -41,6 +41,10 @@ struct admin_connect_open_args { unsigned int flags; }; +struct admin_connect_get_lib_version_ret { + unsigned hyper libVer; +}; + /* Define the program number, protocol version and procedure numbers here. */ const ADMIN_PROGRAM = 0x06900690; const ADMIN_PROTOCOL_VERSION = 1; @@ -71,5 +75,10 @@ enum admin_procedure { /** * @generate: none */ - ADMIN_PROC_CONNECT_CLOSE = 2 + ADMIN_PROC_CONNECT_CLOSE = 2, + + /** + * @generate: both + */ + ADMIN_PROC_CONNECT_GET_LIB_VERSION = 3 }; diff --git a/src/admin_protocol-structs b/src/admin_protocol-structs index 3ac31fa..809379b 100644 --- a/src/admin_protocol-structs +++ b/src/admin_protocol-structs @@ -2,7 +2,11 @@ struct admin_connect_open_args { u_int flags; }; +struct admin_connect_get_lib_version_ret { + uint64_t libVer; +}; enum admin_procedure { ADMIN_PROC_CONNECT_OPEN = 1, ADMIN_PROC_CONNECT_CLOSE = 2, + ADMIN_PROC_CONNECT_GET_LIB_VERSION = 3, }; diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index fdcf2c1..00f2a54 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -486,3 +486,34 @@ int virAdmConnectUnregisterCloseCallback(virAdmConnectPtr conn, virDispatchError(NULL); return -1; } + +/** + * virAdmConnectGetLibVersion: + * @conn: pointer to an active admin connection + * @libVer: stores the current remote libvirt version number + * + * Retrieves the remote side libvirt version used by the daemon. Format + * returned in @libVer is of a following pattern: + * major * 1,000,000 + minor * 1,000 + release. + * + * Returns 0 on success, -1 on failure and @libVer follows this format: + */ +int virAdmConnectGetLibVersion(virAdmConnectPtr conn, + unsigned long long *libVer) +{ + VIR_DEBUG("conn=%p, libVir=%p", conn, libVer); + + virResetLastError(); + + virCheckAdmConnectReturn(conn, -1); + virCheckNonNullArgReturn(libVer, -1); + + if (remoteAdminConnectGetLibVersion(conn, libVer) < 0) + goto error; + + return 0; + + error: + virDispatchError(NULL); + return -1; +} diff --git a/src/libvirt_admin_private.syms b/src/libvirt_admin_private.syms index 401cd4e..85380dc 100644 --- a/src/libvirt_admin_private.syms +++ b/src/libvirt_admin_private.syms @@ -6,6 +6,7 @@ # # admin/admin_protocol.x +xdr_admin_connect_get_lib_version_ret; xdr_admin_connect_open_args; # Let emacs know we want case-insensitive sorting diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index df01837..303b110 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -17,6 +17,7 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectRef; virAdmConnectIsAlive; virAdmConnectGetURI; + virAdmConnectGetLibVersion; virAdmConnectRegisterCloseCallback; virAdmConnectUnregisterCloseCallback; }; diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index b6d50f3..5cfc512 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -50,7 +50,7 @@ my $protocol = shift or die "missing protocol argument"; my @autogen; my $connect_ptr = $structprefix eq "admin" ? "virAdmConnectPtr" : "virConnectPtr"; -my $prefix = ($structprefix eq "admin") ? "adm" : "vir"; +my $prefix = ($structprefix eq "admin") ? "admin" : "vir"; sub fixup_name { my $name = shift; @@ -1401,8 +1401,13 @@ elsif ($mode eq "client") { my $ret_name = $1; if ($call->{ProcName} =~ m/Get(Lib)?Version/) { - push(@args_list, "unsigned long *$ret_name"); - push(@ret_list, "if ($ret_name) HYPER_TO_ULONG(*$ret_name, ret.$ret_name);"); + if ($structprefix eq "admin") { + push(@args_list, "unsigned long long *$ret_name"); + push(@ret_list, "*$ret_name = ret.$ret_name;"); + } else { + push(@args_list, "unsigned long *$ret_name"); + push(@ret_list, "if ($ret_name) HYPER_TO_ULONG(*$ret_name, ret.$ret_name);"); + } push(@ret_list, "rv = 0;"); $single_ret_var = "int rv = -1"; $single_ret_type = "int"; diff --git a/tools/virt-admin.c b/tools/virt-admin.c index fc98964..dd7746f 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -195,6 +195,67 @@ cmdURI(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) return true; } +/* + * "version" command + */ + +static const vshCmdInfo info_version[] = { + {.name = "help", + .data = N_("show version") + }, + {.name = "desc", + .data = N_("Display the system and also the daemon version information.") + }, + {.name = NULL} +}; + +static bool +cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + unsigned long libVersion; + unsigned long long includeVersion; + unsigned long long daemonVersion; + int ret; + unsigned int major; + unsigned int minor; + unsigned int rel; + vshAdmControlPtr priv = ctl->privData; + + includeVersion = LIBVIR_VERSION_NUMBER; + major = includeVersion / 1000000; + includeVersion %= 1000000; + minor = includeVersion / 1000; + rel = includeVersion % 1000; + vshPrint(ctl, _("Compiled against library: libvirt %d.%d.%d\n"), + major, minor, rel); + + ret = virGetVersion(&libVersion, NULL, NULL); + if (ret < 0) { + vshError(ctl, "%s", _("failed to get the library version")); + return false; + } + major = libVersion / 1000000; + libVersion %= 1000000; + minor = libVersion / 1000; + rel = libVersion % 1000; + vshPrint(ctl, _("Using library: libvirt %d.%d.%d\n"), + major, minor, rel); + + ret = virAdmConnectGetLibVersion(priv->conn, &daemonVersion); + if (ret < 0) { + vshError(ctl, "%s", _("failed to get the daemon version")); + } else { + major = daemonVersion / 1000000; + daemonVersion %= 1000000; + minor = daemonVersion / 1000; + rel = daemonVersion % 1000; + vshPrint(ctl, _("Running against daemon: %d.%d.%d\n"), + major, minor, rel); + } + + return true; +} + /* --------------- * Command Connect @@ -523,6 +584,12 @@ static const vshCmdDef vshAdmCmds[] = { .info = info_uri, .flags = 0 }, + {.name = "version", + .handler = cmdVersion, + .opts = NULL, + .info = info_version, + .flags = 0 + }, {.name = "connect", .handler = cmdConnect, .opts = opts_connect, -- 2.4.3

On Fri, Nov 06, 2015 at 12:46:25PM +0100, Erik Skultety wrote:
Introduce a new API to get libvirt version. It is worth noting, that libvirt-admin and libvirt share the same version number. Unfortunately, our existing API isn't generic enough to be used with virAdmConnectPtr as well. Also this patch wires up this API to the virt-admin client as a generic cmdVersion command. --- daemon/admin_server.c | 9 ++++++ include/libvirt/libvirt-admin.h | 3 ++ po/POTFILES.in | 1 + src/admin/admin_protocol.x | 11 ++++++- src/admin_protocol-structs | 4 +++ src/libvirt-admin.c | 31 +++++++++++++++++++ src/libvirt_admin_private.syms | 1 + src/libvirt_admin_public.syms | 1 + src/rpc/gendispatch.pl | 11 +++++-- tools/virt-admin.c | 67 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 135 insertions(+), 4 deletions(-)
diff --git a/daemon/admin_server.c b/daemon/admin_server.c index 712a44b..189091e 100644 --- a/daemon/admin_server.c +++ b/daemon/admin_server.c @@ -114,4 +114,13 @@ adminDispatchConnectClose(virNetServerPtr server ATTRIBUTE_UNUSED, return 0; }
+static int +adminConnectGetLibVersion(virNetDaemonPtr dmn ATTRIBUTE_UNUSED, + unsigned long long *libVer) +{ + if (libVer) + *libVer = LIBVIR_VERSION_NUMBER; + return 0; +} + #include "admin_dispatch.h" diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 5f9841c..e5f840c 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -58,6 +58,9 @@ int virAdmConnectIsAlive(virAdmConnectPtr conn);
char *virAdmConnectGetURI(virAdmConnectPtr conn);
+int virAdmConnectGetLibVersion(virAdmConnectPtr conn, + unsigned long long *libVer); + /** * virAdmConnectCloseFunc: * @conn: virAdmConnect connection diff --git a/po/POTFILES.in b/po/POTFILES.in index d0840f4..e165a08 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,3 +1,4 @@ +daemon/admin_dispatch.h daemon/admin_server.c daemon/libvirtd-config.c daemon/libvirtd.c diff --git a/src/admin/admin_protocol.x b/src/admin/admin_protocol.x index d0ca1a3..878983d 100644 --- a/src/admin/admin_protocol.x +++ b/src/admin/admin_protocol.x @@ -41,6 +41,10 @@ struct admin_connect_open_args { unsigned int flags; };
+struct admin_connect_get_lib_version_ret { + unsigned hyper libVer; +}; + /* Define the program number, protocol version and procedure numbers here. */ const ADMIN_PROGRAM = 0x06900690; const ADMIN_PROTOCOL_VERSION = 1; @@ -71,5 +75,10 @@ enum admin_procedure { /** * @generate: none */ - ADMIN_PROC_CONNECT_CLOSE = 2 + ADMIN_PROC_CONNECT_CLOSE = 2, + + /** + * @generate: both + */ + ADMIN_PROC_CONNECT_GET_LIB_VERSION = 3
I was wondering why 'connect' is here, it does not necessarily relate to connection and makes the name long, we could start using 'daemon' instead as that is what we'll need to add anyway. Also 'lib' seems unnecessary here. Otherwise looks fine.

On 16/11/15 17:42, Martin Kletzander wrote:
const ADMIN_PROGRAM = 0x06900690; const ADMIN_PROTOCOL_VERSION = 1; @@ -71,5 +75,10 @@ enum admin_procedure { /** * @generate: none */ - ADMIN_PROC_CONNECT_CLOSE = 2 + ADMIN_PROC_CONNECT_CLOSE = 2, + + /** + * @generate: both + */ + ADMIN_PROC_CONNECT_GET_LIB_VERSION = 3
I was wondering why 'connect' is here, it does not necessarily relate to connection and makes the name long, we could start using 'daemon' instead as that is what we'll need to add anyway. Also 'lib' seems unnecessary here.
Well, this is a matter of consistency with libvirt library. You also mentioned the general API naming convention that should be met according to the arguments the API takes in one of your earlier reviews [1]. The 'connect' is there because virAdmConnectGetLibVersion indeed takes virAdmConnectPtr as its 1st argument and the remote version name of the API imho should not differ that much. Erik [1] https://www.redhat.com/archives/libvir-list/2015-September/msg00079.html

On Thu, Nov 19, 2015 at 10:52:35AM +0100, Erik Skultety wrote:
On 16/11/15 17:42, Martin Kletzander wrote:
const ADMIN_PROGRAM = 0x06900690; const ADMIN_PROTOCOL_VERSION = 1; @@ -71,5 +75,10 @@ enum admin_procedure { /** * @generate: none */ - ADMIN_PROC_CONNECT_CLOSE = 2 + ADMIN_PROC_CONNECT_CLOSE = 2, + + /** + * @generate: both + */ + ADMIN_PROC_CONNECT_GET_LIB_VERSION = 3
I was wondering why 'connect' is here, it does not necessarily relate to connection and makes the name long, we could start using 'daemon' instead as that is what we'll need to add anyway. Also 'lib' seems unnecessary here.
Well, this is a matter of consistency with libvirt library. You also mentioned the general API naming convention that should be met according to the arguments the API takes in one of your earlier reviews [1]. The 'connect' is there because virAdmConnectGetLibVersion indeed takes virAdmConnectPtr as its 1st argument and the remote version name of the API imho should not differ that much.
We want to stay consistent with libvirt, but not with all the wrong choices that were made along the way. I wish I named the initial pointer the user gets with virAdmConnect() something like virAdmDaemonPtr, everything you call with that would start with virAdmDaemon and would make tad more sense. But I didn't know there will be a "daemon" back then. Having said that, we *can* change that, but I already confused myself as to what is the best naming convention we should go with. Wider discussion about that would be fine and I think we would achieve a conclusion in less than 5 minutes if there's more than the two of us and it's not done through mail. So for now keep it as it is, and we'll add the discussion about naming to the TODO list of things needed to be done before enabling the admin api in the daemon.
Erik
[1] https://www.redhat.com/archives/libvir-list/2015-September/msg00079.html

--- tools/Makefile.am | 12 +-- tools/virt-admin.pod | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+), 8 deletions(-) create mode 100644 tools/virt-admin.pod diff --git a/tools/Makefile.am b/tools/Makefile.am index 62073f9..9180564 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -83,7 +83,8 @@ dist_man1_MANS = \ virt-host-validate.1 \ virt-pki-validate.1 \ virt-xml-validate.1 \ - virsh.1 + virsh.1 \ + virt-admin.1 if WITH_LXC dist_man1_MANS += virt-login-shell.1 else ! WITH_LXC @@ -280,14 +281,9 @@ virsh_win_icon.$(OBJEXT): virsh_win_icon.rc --output-format coff --output $@ endif WITH_WIN_ICON -virt-login-shell.1: virt-login-shell.pod $(top_srcdir)/configure.ac +%.1: %.pod $(top_srcdir)/configure.ac $(AM_V_GEN)$(POD2MAN) $< $(srcdir)/$@ \ - && if grep 'POD ERROR' $(srcdir)/$@ ; then \ - rm $(srcdir)/$@; exit 1; fi - -virsh.1: virsh.pod $(top_srcdir)/configure.ac - $(AM_V_GEN)$(POD2MAN) $< $(srcdir)/$@ \ - && if grep 'POD ERROR' $(srcdir)/$@ ; then \ + && if grep 'POD ERROR' $(srcdir)/$@ ; then \ rm $(srcdir)/$@; exit 1; fi install-data-local: install-init install-systemd diff --git a/tools/virt-admin.pod b/tools/virt-admin.pod new file mode 100644 index 0000000..dc8c083 --- /dev/null +++ b/tools/virt-admin.pod @@ -0,0 +1,255 @@ +=head1 NAME + +virt-admin - daemon administration interface + +=head1 SYNOPSIS + +B<virt-admin> [I<OPTION>]... [I<COMMAND_STRING>] + +B<virt-admin> [I<OPTION>]... I<COMMAND> [I<ARG>]... + +=head1 DESCRIPTION + +The B<virt-admin> program is the main administration interface for modifying +the libvirt daemon configuration in runtime, changing daemon behaviour as well +as for monitoring and managing all clients connected to the daemon. + +The basic structure of most virt-admin usage is: + + virt-admin [OPTION]... <command> [ARG]... + +Where I<command> is one of the commands listed below. + +The B<virt-admin> program can be used either to run one I<COMMAND> by giving the +command and its arguments on the shell command line, or a I<COMMAND_STRING> +which is a single shell argument consisting of multiple I<COMMAND> actions +and their arguments joined with whitespace, and separated by semicolons +between commands. Within I<COMMAND_STRING>, virt-admin understands the +same single, double, and backslash escapes as the shell, although you must +add another layer of shell escaping in creating the single shell argument. +If no command is given in the command line, B<virt-admin> will then start a minimal +interpreter waiting for your commands, and the B<quit> command will then exit +the program. + +The B<virt-admin> program understands the following I<OPTIONS>. + +=over 4 + +=item B<-c>, B<--connect> I<URI> + +Connect to the specified I<URI>, as if by the B<connect> command, +instead of the default connection. + +=item B<-d>, B<--debug> I<LEVEL> + +Enable debug messages at integer I<LEVEL> and above. I<LEVEL> can +range from 0 to 4 (default). See the documentation of B<VIRT_ADMIN_DEBUG> +environment variable below for the description of each I<LEVEL>. + +=item B<-h>, B<--help> + +Ignore all other arguments, and behave as if the B<help> command were +given instead. + +=item B<-l>, B<--log> I<FILE> + +Output logging details to I<FILE>. + +=item B<-q>, B<--quiet> + +Avoid extra informational messages. + +=item B<-v>, B<--version[=short]> + +Ignore all other arguments, and prints the version of the libvirt library +virt-admin is coming from + +=item B<-V>, B<--version=long> + +Ignore all other arguments, and prints the version of the libvirt library +virt-admin is coming from. + +=back + +=head1 NOTES + +Running B<virt-admin> requires root privileges due to the +communications channels used to talk to the daemon. Consider changing the +I<unix_sock_group> ownership setting to grant access to specific set of users +or modifying I<unix_sock_rw_perms> permissions. Daemon configuration file +provides more information about setting permissions. + +=head1 GENERIC COMMANDS + +The following commands are generic. + +=over 4 + +=item B<help> [I<command-or-group>] + +This lists each of the virt-admin commands. When used without options, all +commands are listed, one per line, grouped into related categories, +displaying the keyword for each group. + +To display detailed information for a specific command, use its name as the +option. + +=item B<quit>, B<exit> + +quit this interactive terminal + +=item B<version> + +Will print out the version info about which libvirt library was this client +built from. As opposed to I<virsh> client, the output already includes +the version of the daemon. + +B<Example> + + $ virt-admin version + Compiled against library: libvirt 1.2.21 + Using library: libvirt 1.2.21 + Running against daemon: 1.2.20 + +=item B<cd> [I<directory>] + +Will change current directory to I<directory>. The default directory +for the B<cd> command is the home directory or, if there is no I<HOME> +variable in the environment, the root directory. + +This command is only available in interactive mode. + +=item B<pwd> + +Will print the current directory. + +=item B<connect> [I<URI>] + +(Re)-Connect to a daemon's administrating server. The I<URI> parameter +specifies how to connect to the administrating server. +If I<LIBVIRT_ADMIN_DEFAULT_URI> or I<admin_uri_default> (see below) were set, +I<connect> is automatically issued every time a command that requires an +active connection is executed. Note that this only applies if there is no +connection at all or there is an inactive one. + +To find the currently used URI, check the I<uri> command documented below. + +For remote access see the documentation page at +L<http://libvirt.org/uri.html> on how to make URIs. + +=item B<uri> + +Prints the administrating server canonical URI, can be useful in shell mode. If +no I<uri> was specified, neither I<LIBVIRT_ADMIN_DEFAULT_URI> or +I<admin_uri_default> were set, libvirtd:///system is used. + +=back + +=head1 ENVIRONMENT + +The following environment variables can be set to alter the behaviour +of C<virt-admin> + +=over 4 + +=item VIRT_ADMIN_DEBUG=<0 to 4> + +Turn on verbose debugging of virt-admin commands. Valid levels are + +=over 4 + +=item * VIRT_ADMIN_DEBUG=0 + +DEBUG - Messages at ALL levels get logged + +=item * VIRT_ADMIN_DEBUG=1 + +INFO - Logs messages at levels INFO, NOTICE, WARNING and ERROR + +=item * VIRT_ADMIN_DEBUG=2 + +NOTICE - Logs messages at levels NOTICE, WARNING and ERROR + +=item * VIRT_ADMIN_DEBUG=3 + +WARNING - Logs messages at levels WARNING and ERROR + +=item * VIRT_ADMIN_DEBUG=4 + +ERROR - Messages at only ERROR level gets logged. + +=back + +=item VIRT_ADMIN_LOG_FILE=C<LOGFILE> + +The file to log virt-admin debug messages. + +=item LIBVIRT_ADMIN_DEFAULT_URI + +The daemon whose admin server to connect to by default. Set this to a URI, in +the same format as accepted by the B<connect> option. This overrides the +default URI set in any client config file. + +=item VIRT_ADMIN_HISTSIZE + +The number of commands to remember in the command history. The +default value is 500. + +=item LIBVIRT_DEBUG=LEVEL + +Turn on verbose debugging of all libvirt API calls. Valid levels are + +=over 4 + +=item * LIBVIRT_DEBUG=1 + +Messages at level DEBUG or above + +=item * LIBVIRT_DEBUG=2 + +Messages at level INFO or above + +=item * LIBVIRT_DEBUG=3 + +Messages at level WARNING or above + +=item * LIBVIRT_DEBUG=4 + +Messages at level ERROR or above + +=back + +For further information about debugging options consult C<http://libvirt.org/logging.html> + +=back + +=head1 BUGS + +Report any bugs discovered to the libvirt community via the mailing +list C<http://libvirt.org/contact.html> or bug tracker C<http://libvirt.org/bugs.html>. +Alternatively report bugs to your software distributor / vendor. + +=head1 AUTHORS + + Please refer to the AUTHORS file distributed with libvirt. + + Based on the virsh man page. + +=head1 COPYRIGHT + +Copyright (C) 2015 Red Hat, Inc., and the authors listed in the +libvirt AUTHORS file. + +=head1 LICENSE + +virt-admin is distributed under the terms of the GNU LGPL v2+. +This is free software; see the source for copying conditions. There +is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE + +=head1 SEE ALSO + +L<virsh(1)>, L<virt-xml-validate(1)>, L<virt-host-validate(1)>, +L<http://www.libvirt.org/> + +=cut -- 2.4.3

On 06.11.2015 12:46, Erik Skultety wrote:
--- tools/Makefile.am | 12 +-- tools/virt-admin.pod | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+), 8 deletions(-) create mode 100644 tools/virt-admin.pod
diff --git a/tools/Makefile.am b/tools/Makefile.am index 62073f9..9180564 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -83,7 +83,8 @@ dist_man1_MANS = \ virt-host-validate.1 \ virt-pki-validate.1 \ virt-xml-validate.1 \ - virsh.1 + virsh.1 \ + virt-admin.1 if WITH_LXC dist_man1_MANS += virt-login-shell.1 else ! WITH_LXC @@ -280,14 +281,9 @@ virsh_win_icon.$(OBJEXT): virsh_win_icon.rc --output-format coff --output $@ endif WITH_WIN_ICON
-virt-login-shell.1: virt-login-shell.pod $(top_srcdir)/configure.ac +%.1: %.pod $(top_srcdir)/configure.ac $(AM_V_GEN)$(POD2MAN) $< $(srcdir)/$@ \ - && if grep 'POD ERROR' $(srcdir)/$@ ; then \ - rm $(srcdir)/$@; exit 1; fi - -virsh.1: virsh.pod $(top_srcdir)/configure.ac - $(AM_V_GEN)$(POD2MAN) $< $(srcdir)/$@ \ - && if grep 'POD ERROR' $(srcdir)/$@ ; then \ + && if grep 'POD ERROR' $(srcdir)/$@ ; then \ rm $(srcdir)/$@; exit 1; fi
install-data-local: install-init install-systemd diff --git a/tools/virt-admin.pod b/tools/virt-admin.pod new file mode 100644 index 0000000..dc8c083 --- /dev/null +++ b/tools/virt-admin.pod @@ -0,0 +1,255 @@ +=head1 NAME + +virt-admin - daemon administration interface + +=head1 SYNOPSIS + +B<virt-admin> [I<OPTION>]... [I<COMMAND_STRING>] + +B<virt-admin> [I<OPTION>]... I<COMMAND> [I<ARG>]... + +=head1 DESCRIPTION + +The B<virt-admin> program is the main administration interface for modifying +the libvirt daemon configuration in runtime, changing daemon behaviour as well
s/in/at/
+as for monitoring and managing all clients connected to the daemon. + +The basic structure of most virt-admin usage is: + + virt-admin [OPTION]... <command> [ARG]... + +Where I<command> is one of the commands listed below.
Otherwise looking good. ACK. Michal

As it turned out, we need to share some enums and declarations between libvirt.h and libvirt-admin.h, but since our policy forbids direct includes of libvirt*.h, there has to be some header exempt from this rule. This patch moves the relevant part of code from libvirt.h.in to libvirt-common.h.in. Moreover, since there is no need to have libvirt.h generated anymore, introduce a new header libvirt.h which was previosly ignored from git and make the common header ignored and generated instead. --- I'd like add a couple more patches as a reaction to some reviews of this series, namely, adding a new admin configuration file, adding a new common public header file and also admin reimplementation of virGetVersion. .gitignore | 2 +- cfg.mk | 2 +- configure.ac | 2 +- docs/Makefile.am | 3 +- include/libvirt/Makefile.am | 2 +- include/libvirt/libvirt-admin.h | 3 + include/libvirt/libvirt-common.h.in | 127 ++++++++++++++++++++++++++++++++++++ include/libvirt/libvirt-host.h | 30 --------- include/libvirt/libvirt.h | 54 +++++++++++++++ include/libvirt/libvirt.h.in | 109 ------------------------------- 10 files changed, 190 insertions(+), 144 deletions(-) create mode 100644 include/libvirt/libvirt-common.h.in create mode 100644 include/libvirt/libvirt.h delete mode 100644 include/libvirt/libvirt.h.in diff --git a/.gitignore b/.gitignore index 2d52a8f..63f4edf 100644 --- a/.gitignore +++ b/.gitignore @@ -87,7 +87,7 @@ /gnulib/lib/* /gnulib/m4/* /gnulib/tests/* -/include/libvirt/libvirt.h +/include/libvirt/libvirt-common.h /libtool /libvirt-*.tar.gz /libvirt-[0-9]* diff --git a/cfg.mk b/cfg.mk index fd9fd75..85cbf95 100644 --- a/cfg.mk +++ b/cfg.mk @@ -1215,7 +1215,7 @@ exclude_file_name_regexp--sc_prohibit_include_public_headers_quote = \ ^(src/internal\.h$$|tools/wireshark/src/packet-libvirt.h$$) exclude_file_name_regexp--sc_prohibit_include_public_headers_brackets = \ - ^(tools/|examples/|include/libvirt/(virterror|libvirt-(qemu|lxc))\.h$$) + ^(tools/|examples/|include/libvirt/(virterror|libvirt(-(admin|qemu|lxc))?)\.h$$) exclude_file_name_regexp--sc_prohibit_int_ijk = \ ^(src/remote_protocol-structs|src/remote/remote_protocol.x|cfg.mk|include/)$ diff --git a/configure.ac b/configure.ac index 4b7c9ed..a94308e 100644 --- a/configure.ac +++ b/configure.ac @@ -2789,7 +2789,7 @@ AC_CONFIG_FILES([\ src/libvirt-lxc.pc \ libvirt.spec mingw-libvirt.spec \ po/Makefile.in \ - include/libvirt/Makefile include/libvirt/libvirt.h \ + include/libvirt/Makefile include/libvirt/libvirt-common.h \ daemon/Makefile \ tools/Makefile \ tests/Makefile \ diff --git a/docs/Makefile.am b/docs/Makefile.am index bfae35e..13d7a5f 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -301,7 +301,8 @@ EXTRA_DIST += $(APIBUILD_STAMP) $(python_generated_files): $(APIBUILD_STAMP) $(APIBUILD_STAMP): $(srcdir)/apibuild.py \ - $(top_srcdir)/include/libvirt/libvirt.h.in \ + $(top_srcdir)/include/libvirt/libvirt.h \ + $(top_srcdir)/include/libvirt/libvirt-common.h.in \ $(top_srcdir)/include/libvirt/libvirt-domain-snapshot.h \ $(top_srcdir)/include/libvirt/libvirt-domain.h \ $(top_srcdir)/include/libvirt/libvirt-event.h \ diff --git a/include/libvirt/Makefile.am b/include/libvirt/Makefile.am index 2d40621..8e5b1b8 100644 --- a/include/libvirt/Makefile.am +++ b/include/libvirt/Makefile.am @@ -37,7 +37,7 @@ virinc_HEADERS = libvirt.h \ install-exec-hook: $(mkinstalldirs) $(DESTDIR)$(virincdir) -EXTRA_DIST = libvirt.h.in +EXTRA_DIST = libvirt-common.h.in # Temporarily disabled, but we need it for building EXTRA_DIST += libvirt-admin.h diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 9997cc2..ae033d1 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -30,6 +30,9 @@ extern "C" { # endif +# define __VIR_ADMIN_H_INCLUDES__ +# include <libvirt/libvirt-common.h> +# undef __VIR_ADMIN_H_INCLUDES__ /** * virAdmConnect: diff --git a/include/libvirt/libvirt-common.h.in b/include/libvirt/libvirt-common.h.in new file mode 100644 index 0000000..efbb91b --- /dev/null +++ b/include/libvirt/libvirt-common.h.in @@ -0,0 +1,127 @@ +/* -*- c -*- + * libvirt-common.h + * Summary: common macros and enums for the libvirt and libvirt-admin library + * Description: Provides common macros and enums needed by both libvirt and + * libvirt-admin libraries + * + * Copyright (C) 2015 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 + * <http://www.gnu.org/licenses/>. + * + * Author: Erik Skultety <eskultet@redhat.com> + */ + +#if !defined __VIR_LIBVIRT_H_INCLUDES__ && !defined __VIR_ADMIN_H_INCLUDES__ +# error "Don't include this file directly" +#endif + +#ifndef __VIR_VIRCOMMON_H__ +# define __VIR_VIRCOMMON_H__ + +# include <sys/types.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# ifndef VIR_DEPRECATED + /* The feature is present in gcc-3.1 and newer. */ +# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define VIR_DEPRECATED __attribute__((__deprecated__)) +# else +# define VIR_DEPRECATED /* nothing */ +# endif +# endif /* VIR_DEPRECATED */ + +# ifdef WIN32 +# ifdef LIBVIRT_STATIC +# define VIR_EXPORT_VAR extern +# else +# ifdef IN_LIBVIRT +# define VIR_EXPORT_VAR __declspec(dllexport) +# else +# define VIR_EXPORT_VAR __declspec(dllimport) extern +# endif +# endif +# else +# define VIR_EXPORT_VAR extern +# endif + +/* General note - in the header files, any linear enumeration which + * might be expanded in the future has an optional *_LAST value that + * gives the size of the enum at the time of compilation, if the user + * defines VIR_ENUM_SENTINELS. Enumerations for bit values do not + * have a *_LAST value, but additional bits may be defined. */ + +/* library versioning */ + +/** + * LIBVIR_VERSION_NUMBER: + * + * Macro providing the version of the library as + * version * 1,000,000 + minor * 1000 + micro + */ + +# define LIBVIR_VERSION_NUMBER @LIBVIRT_VERSION_NUMBER@ + +/** + * LIBVIR_CHECK_VERSION: + * @major: major component of the version number + * @minor: minor component of the version number + * @micro: micro component of the version number + * + * Macro for developers to easily check what version of the library + * their code is compiling against. + * e.g. + * #if LIBVIR_CHECK_VERSION(1,1,3) + * // some code that only works in 1.1.3 and newer + * #endif + */ +# define LIBVIR_CHECK_VERSION(major, minor, micro) \ + ((major) * 1000000 + (minor) * 1000 + (micro) <= LIBVIR_VERSION_NUMBER) + +/* + * virFreeCallback: + * @opaque: opaque user data provided at registration + * + * Type for a callback cleanup function to be paired with a callback. This + * function will be called as a final chance to clean up the @opaque + * registered with the primary callback, at the time when the primary + * callback is deregistered. + * + * It is forbidden to call any other libvirt APIs from an + * implementation of this callback, since it can be invoked + * from a context which is not re-entrant safe. Failure to + * abide by this requirement may lead to application deadlocks + * or crashes. + */ +typedef void (*virFreeCallback)(void *opaque); + +typedef enum { + VIR_CONNECT_CLOSE_REASON_ERROR = 0, /* Misc I/O error */ + VIR_CONNECT_CLOSE_REASON_EOF = 1, /* End-of-file from server */ + VIR_CONNECT_CLOSE_REASON_KEEPALIVE = 2, /* Keepalive timer triggered */ + VIR_CONNECT_CLOSE_REASON_CLIENT = 3, /* Client requested it */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_CLOSE_REASON_LAST +# endif +} virConnectCloseReason; + +# ifdef __cplusplus +} +# endif + +#endif /* __VIR_VIRCOMMON_H__ */ diff --git a/include/libvirt/libvirt-host.h b/include/libvirt/libvirt-host.h index 3669711..8786fbb 100644 --- a/include/libvirt/libvirt-host.h +++ b/include/libvirt/libvirt-host.h @@ -29,24 +29,6 @@ # endif -/* - * virFreeCallback: - * @opaque: opaque user data provided at registration - * - * Type for a callback cleanup function to be paired with a callback. This - * function will be called as a final chance to clean up the @opaque - * registered with the primary callback, at the time when the primary - * callback is deregistered. - * - * It is forbidden to call any other libvirt APIs from an - * implementation of this callback, since it can be invoked - * from a context which is not re-entrant safe. Failure to - * abide by this requirement may lead to application deadlocks - * or crashes. - */ -typedef void (*virFreeCallback)(void *opaque); - - /** * virConnect: * @@ -755,18 +737,6 @@ char * virConnectGetSysinfo (virConnectPtr conn, int virConnectSetKeepAlive(virConnectPtr conn, int interval, unsigned int count); - -typedef enum { - VIR_CONNECT_CLOSE_REASON_ERROR = 0, /* Misc I/O error */ - VIR_CONNECT_CLOSE_REASON_EOF = 1, /* End-of-file from server */ - VIR_CONNECT_CLOSE_REASON_KEEPALIVE = 2, /* Keepalive timer triggered */ - VIR_CONNECT_CLOSE_REASON_CLIENT = 3, /* Client requested it */ - -# ifdef VIR_ENUM_SENTINELS - VIR_CONNECT_CLOSE_REASON_LAST -# endif -} virConnectCloseReason; - /** * virConnectCloseFunc: * @conn: virConnect connection diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h new file mode 100644 index 0000000..36f6d60 --- /dev/null +++ b/include/libvirt/libvirt.h @@ -0,0 +1,54 @@ +/* -*- c -*- + * libvirt.h: Core interfaces for the libvirt library + * Summary: core interfaces for the libvirt library + * Description: Provides the interfaces of the libvirt library to handle + * virtualized domains + * + * Copyright (C) 2005-2006, 2010-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 + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel Veillard <veillard@redhat.com> + */ + +#ifndef __VIR_VIRLIB_H__ +# define __VIR_VIRLIB_H__ + +# include <sys/types.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# define __VIR_LIBVIRT_H_INCLUDES__ +# include <libvirt/libvirt-common.h> +# include <libvirt/libvirt-host.h> +# include <libvirt/libvirt-domain.h> +# include <libvirt/libvirt-domain-snapshot.h> +# include <libvirt/libvirt-event.h> +# include <libvirt/libvirt-interface.h> +# include <libvirt/libvirt-network.h> +# include <libvirt/libvirt-nodedev.h> +# include <libvirt/libvirt-nwfilter.h> +# include <libvirt/libvirt-secret.h> +# include <libvirt/libvirt-storage.h> +# include <libvirt/libvirt-stream.h> +# undef __VIR_LIBVIRT_H_INCLUDES__ + +# ifdef __cplusplus +} +# endif + +#endif /* __VIR_VIRLIB_H__ */ diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in deleted file mode 100644 index 7706978..0000000 --- a/include/libvirt/libvirt.h.in +++ /dev/null @@ -1,109 +0,0 @@ -/* -*- c -*- - * libvirt.h: Core interfaces for the libvirt library - * Summary: core interfaces for the libvirt library - * Description: Provides the interfaces of the libvirt library to handle - * virtualized domains - * - * Copyright (C) 2005-2006, 2010-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 - * <http://www.gnu.org/licenses/>. - * - * Author: Daniel Veillard <veillard@redhat.com> - */ - -#ifndef __VIR_VIRLIB_H__ -# define __VIR_VIRLIB_H__ - -# include <sys/types.h> - -# ifdef __cplusplus -extern "C" { -# endif - -# ifndef VIR_DEPRECATED - /* The feature is present in gcc-3.1 and newer. */ -# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) -# define VIR_DEPRECATED __attribute__((__deprecated__)) -# else -# define VIR_DEPRECATED /* nothing */ -# endif -# endif /* VIR_DEPRECATED */ - -# ifdef WIN32 -# ifdef LIBVIRT_STATIC -# define VIR_EXPORT_VAR extern -# else -# ifdef IN_LIBVIRT -# define VIR_EXPORT_VAR __declspec(dllexport) -# else -# define VIR_EXPORT_VAR __declspec(dllimport) extern -# endif -# endif -# else -# define VIR_EXPORT_VAR extern -# endif - -/* General note - in the header files, any linear enumeration which - * might be expanded in the future has an optional *_LAST value that - * gives the size of the enum at the time of compilation, if the user - * defines VIR_ENUM_SENTINELS. Enumerations for bit values do not - * have a *_LAST value, but additional bits may be defined. */ - -/* library versioning */ - -/** - * LIBVIR_VERSION_NUMBER: - * - * Macro providing the version of the library as - * version * 1,000,000 + minor * 1000 + micro - */ - -# define LIBVIR_VERSION_NUMBER @LIBVIRT_VERSION_NUMBER@ - -/** - * LIBVIR_CHECK_VERSION: - * @major: major component of the version number - * @minor: minor component of the version number - * @micro: micro component of the version number - * - * Macro for developers to easily check what version of the library - * their code is compiling against. - * e.g. - * #if LIBVIR_CHECK_VERSION(1,1,3) - * // some code that only works in 1.1.3 and newer - * #endif - */ -# define LIBVIR_CHECK_VERSION(major, minor, micro) \ - ((major) * 1000000 + (minor) * 1000 + (micro) <= LIBVIR_VERSION_NUMBER) - -# define __VIR_LIBVIRT_H_INCLUDES__ -# include <libvirt/libvirt-host.h> -# include <libvirt/libvirt-domain.h> -# include <libvirt/libvirt-domain-snapshot.h> -# include <libvirt/libvirt-event.h> -# include <libvirt/libvirt-interface.h> -# include <libvirt/libvirt-network.h> -# include <libvirt/libvirt-nodedev.h> -# include <libvirt/libvirt-nwfilter.h> -# include <libvirt/libvirt-secret.h> -# include <libvirt/libvirt-storage.h> -# include <libvirt/libvirt-stream.h> -# undef __VIR_LIBVIRT_H_INCLUDES__ - -# ifdef __cplusplus -} -# endif - -#endif /* __VIR_VIRLIB_H__ */ -- 2.4.3

Since libvirt-admin is a separate library, it should also have a separate config file. Available settings are currently the same as for libvirt.conf. --- I'd like add a couple more patches as a reaction to some reviews of this series, namely, adding a new admin configuration file, adding a new common public header file and also admin reimplementation of virGetVersion. src/libvirt-admin.conf | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/libvirt-admin.conf diff --git a/src/libvirt-admin.conf b/src/libvirt-admin.conf new file mode 100644 index 0000000..d7cf12a --- /dev/null +++ b/src/libvirt-admin.conf @@ -0,0 +1,16 @@ +# +# 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 admin connection +# URI, including arbitrary parameters + +#uri_aliases = [ +# "admin=libvirtd:///system", +#] + +# This specifies the default location the client tries to connect to if no other +# URI is provided by the application + +#uri_default = "libvirtd:///system" -- 2.4.3

Unfortunately, client side version retrieval API virGetVersion uses one-time initialization (due to the fact we might not have initialized the library by calling connect prior to this) which is not completely compatible with admin initialization. This API is rather simplistic and reimplementing it for admin might be the preferred method of reusing it. Note that even though the method will be reimplemented, the version number is still the same for both the libvirt and libvirt-admin library. --- I'd like add a couple more patches as a reaction to some reviews of this series, namely, adding a new admin configuration file, adding a new common public header file and also admin reimplementation of virGetVersion. include/libvirt/libvirt-admin.h | 3 +++ src/libvirt-admin.c | 34 ++++++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 1 + 3 files changed, 38 insertions(+) diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index ae033d1..9f012c1 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -56,6 +56,9 @@ int virAdmConnectClose(virAdmConnectPtr conn); int virAdmConnectRef(virAdmConnectPtr conn); +int virAdmGetVersion(unsigned long long *libVer); + + # ifdef __cplusplus } # endif diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 5a4fc48..e14d2ef 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -386,3 +386,37 @@ virAdmConnectRef(virAdmConnectPtr conn) return 0; } + +/** + * virAdmGetVersion: + * @libVer: where to store the library version + * + * Provides version information. @libVer is the version of the library and will + * allways be set unless an error occurs in which case an error code and a + * generic message will be returned. @libVer format is as follows: + * major * 1,000,000 + minor * 1,000 + release. + * + * NOTE: To get the remote side library version use virAdmDaemonGetLibVersion + * instead. + * + * Returns 0 on success, -1 in case of an error. + */ +int +virAdmGetVersion(unsigned long long *libVer) +{ + if (virAdmInitialize() < 0) + goto error; + + VIR_DEBUG("libVer=%p", libVer); + + virResetLastError(); + if (!libVer) + goto error; + *libVer = LIBVIR_VERSION_NUMBER; + + return 0; + + error: + virDispatchError(NULL); + return -1; +} diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index d9e3c0b..5e774c2 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -15,4 +15,5 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectOpen; virAdmConnectClose; virAdmConnectRef; + virAdmGetVersion; }; -- 2.4.3

On Thu, Nov 26, 2015 at 04:43:37PM +0100, Erik Skultety wrote:
Unfortunately, client side version retrieval API virGetVersion uses one-time initialization (due to the fact we might not have initialized the library by calling connect prior to this) which is not completely compatible with admin initialization. This API is rather simplistic and reimplementing it for admin might be the preferred method of reusing it. Note that even though the method will be reimplemented, the version number is still the same for both the libvirt and libvirt-admin library. --- I'd like add a couple more patches as a reaction to some reviews of this series, namely, adding a new admin configuration file, adding a new common public header file and also admin reimplementation of virGetVersion.
include/libvirt/libvirt-admin.h | 3 +++ src/libvirt-admin.c | 34 ++++++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 1 + 3 files changed, 38 insertions(+)
diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index ae033d1..9f012c1 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -56,6 +56,9 @@ int virAdmConnectClose(virAdmConnectPtr conn);
int virAdmConnectRef(virAdmConnectPtr conn);
+int virAdmGetVersion(unsigned long long *libVer); + + # ifdef __cplusplus } # endif diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 5a4fc48..e14d2ef 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -386,3 +386,37 @@ virAdmConnectRef(virAdmConnectPtr conn)
return 0; } + +/** + * virAdmGetVersion: + * @libVer: where to store the library version + * + * Provides version information. @libVer is the version of the library and will + * allways be set unless an error occurs in which case an error code and a + * generic message will be returned. @libVer format is as follows: + * major * 1,000,000 + minor * 1,000 + release. + * + * NOTE: To get the remote side library version use virAdmDaemonGetLibVersion
Either you meant virAdmConnectGetLibVersion here or virAdmDaemonGetVersion but not the one you used. You should probably keep the *Connect* version for now as the future of my patch for renaming was not yet decided upon. If it is, I'll change it in the rename patch itself. ACK to [012].5
+ * instead. + * + * Returns 0 on success, -1 in case of an error. + */ +int +virAdmGetVersion(unsigned long long *libVer) +{ + if (virAdmInitialize() < 0) + goto error; + + VIR_DEBUG("libVer=%p", libVer); + + virResetLastError(); + if (!libVer) + goto error; + *libVer = LIBVIR_VERSION_NUMBER; + + return 0; + + error: + virDispatchError(NULL); + return -1; +} diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index d9e3c0b..5e774c2 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -15,4 +15,5 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectOpen; virAdmConnectClose; virAdmConnectRef; + virAdmGetVersion; }; -- 2.4.3
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

+/** + * virAdmGetVersion: + * @libVer: where to store the library version + * + * Provides version information. @libVer is the version of the library and will + * allways be set unless an error occurs in which case an error code and a + * generic message will be returned. @libVer format is as follows: + * major * 1,000,000 + minor * 1,000 + release. + * + * NOTE: To get the remote side library version use virAdmDaemonGetLibVersion
Either you meant virAdmConnectGetLibVersion here or virAdmDaemonGetVersion but not the one you used. You should probably keep the *Connect* version for now as the future of my patch for renaming was not yet decided upon. If it is, I'll change it in the rename patch itself.
ACK to [012].5
Fixed the minor issue and pushed the series, thank you for all reviews. Erik
participants (4)
-
Daniel P. Berrange
-
Erik Skultety
-
Martin Kletzander
-
Michal Privoznik