[libvirt] [PATCH v2 0/9] 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 - creation of the admin socket is prohibited (until 1.3.0) => testing requires the socket to be permitted and created! Erik Skultety (9): 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 admin: Add support for connection close callbacks admin: Introduce virAdmConnectGetLibVersion virt-admin: Provide a man page for virt-admin .gitignore | 1 + daemon/admin_server.c | 9 + daemon/libvirtd.c | 1 - include/libvirt/libvirt-admin.h | 25 ++ po/POTFILES.in | 2 + src/admin/admin_protocol.x | 15 +- src/admin/admin_remote.c | 217 ++++++++++++ src/admin_protocol-structs | 4 + src/datatypes.c | 22 ++ src/datatypes.h | 15 +- src/libvirt-admin.c | 421 +++++++++++++++-------- src/libvirt.c | 55 +-- src/libvirt_admin_private.syms | 1 + src/libvirt_admin_public.syms | 5 + src/libvirt_private.syms | 1 + src/rpc/gendispatch.pl | 11 +- src/util/virconf.c | 52 +++ src/util/virconf.h | 2 +- tools/Makefile.am | 36 +- tools/virt-admin.c | 737 ++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 47 +++ tools/virt-admin.pod | 278 +++++++++++++++ 22 files changed, 1747 insertions(+), 210 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 | 2 +- 4 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 2602dde..5ee41ef 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 (virGetLibvirtConfigFile(&conf) < 0) goto failed; if (name && name[0] == '\0') diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index be6ee19..b72a0ee 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1332,6 +1332,7 @@ virConfTypeToString; virConfWalk; virConfWriteFile; virConfWriteMem; +virGetLibvirtConfigFile; # util/vircrypto.h diff --git a/src/util/virconf.c b/src/util/virconf.c index 9f2d116..0b3d7da 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 * +virGetLibvirtConfigFilePath(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 +virGetLibvirtConfigFile(virConfPtr *conf) +{ + char *filename = NULL; + int ret = -1; + + *conf = NULL; + + if (!(filename = virGetLibvirtConfigFilePath())) + 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..dad7dba 100644 --- a/src/util/virconf.h +++ b/src/util/virconf.h @@ -96,5 +96,5 @@ int virConfWriteFile(const char *filename, int virConfWriteMem(char *memory, int *len, virConfPtr conf); - +int virGetLibvirtConfigFile(virConfPtr *conf); #endif /* __VIR_CONF_H__ */ -- 2.4.3

On Fri, Oct 16, 2015 at 08:12:18PM +0200, 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 | 2 +- 4 files changed, 55 insertions(+), 55 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index be6ee19..b72a0ee 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1332,6 +1332,7 @@ virConfTypeToString; virConfWalk; virConfWriteFile; virConfWriteMem; +virGetLibvirtConfigFile;
I would name it virConf... to go with the rest of the file. How about virConfLoad() or virConfLoadDefault(). We don't necessarily need over 20 characters long names for each function in our source =D
diff --git a/src/util/virconf.h b/src/util/virconf.h index 8037956..dad7dba 100644 --- a/src/util/virconf.h +++ b/src/util/virconf.h @@ -96,5 +96,5 @@ int virConfWriteFile(const char *filename, int virConfWriteMem(char *memory, int *len, virConfPtr conf); - +int virGetLibvirtConfigFile(virConfPtr *conf); #endif /* __VIR_CONF_H__ */
Keep the space between the declaration and the #endif. ACK with those things fixed. Martin

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 + daemon/libvirtd.c | 1 - po/POTFILES.in | 1 + tools/Makefile.am | 28 ++- tools/virt-admin.c | 581 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 47 +++++ 6 files changed, 656 insertions(+), 3 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/daemon/libvirtd.c b/daemon/libvirtd.c index 250094b..26ccf59 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -522,7 +522,6 @@ daemonSetupNetworking(virNetServerPtr srv, virNetServerAddService(srv, svcRO, NULL) < 0) goto cleanup; - /* Temporarily disabled */ if (sock_path_adm && false) { VIR_DEBUG("Registering unix socket %s", sock_path_adm); if (!(svcAdm = virNetServerServiceNewUNIX(sock_path_adm, 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..e68fe84 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,30 @@ 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) \ + $(NULL) +virt_admin_LDADD = \ + $(STATIC_BINARIES) \ + $(PIE_LDFLAGS) \ + ../src/libvirt.la \ + ../src/libvirt-admin.la \ + ../gnulib/lib/libgnu.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..cc33b7b --- /dev/null +++ b/tools/virt-admin.c @@ -0,0 +1,581 @@ +/* + * 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 "internal.h" +#include "virerror.h" +#include "viralloc.h" +#include "virfile.h" +#include "configmake.h" +#include "virthread.h" +#include "virstring.h" + +/* Gnulib doesn't guarantee SA_SIGINFO support. */ +#ifndef SA_SIGINFO +# define SA_SIGINFO 0 +#endif + +#define VIRT_ADMIN_PROMPT "virt-admin # " + +#define vshAdmDisconnect(ctl, conn) vshAdmDisconnectInternal(ctl, &conn); + +static char *progname; + +static const vshCmdGrp cmdGroups[]; +static const vshClientHooks hooks; + +/* + * Detection of disconnections and automatic reconnection support + */ +static bool disconnected; /* we may have been disconnected */ + +static virAdmConnectPtr +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 NULL; + } else { + if (priv->wantReconnect) + vshPrint(ctl, "%s\n", _("Reconnected to the admin server")); + else + vshPrint(ctl, "%s\n", _("Connected to the admin server")); + } + + return priv->conn; +} + +static int +vshAdmDisconnectInternal(vshControl *ctl, virAdmConnectPtr *conn) +{ + int ret = 0; + + if (!*conn) + return ret; + + ret = virAdmConnectClose(*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")); + *conn = NULL; + return ret; +} + +/* + * vshAdmReconnect: + * + * Reconnect to a daemon's admin server + * + */ +static void +vshAdmReconnect(vshControl *ctl) +{ + vshAdmControlPtr priv = ctl->privData; + if (priv->conn || disconnected) + priv->wantReconnect = true; + + vshAdmDisconnect(ctl, priv->conn); + priv->conn = vshAdmConnect(ctl, 0); + + priv->wantReconnect = false; + disconnected = 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_("(re)connect to daemon's admin server") + }, + {.name = "desc", + .data = N_("(Re)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); + if (!priv->conn) + return false; + + return true; +} + +static void * +vshAdmConnectionHandler(vshControl *ctl) +{ + vshAdmControlPtr priv = ctl->privData; + + if (!priv->conn || disconnected) + 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, priv->conn); + + 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:\n")); + vshPrint(ctl, "\n"); + vshPrint(ctl, "%s", _(" Miscellaneous:")); +#ifdef WITH_LIBVIRTD + vshPrint(ctl, " Daemon"); +#endif +#ifdef WITH_SECDRIVER_APPARMOR + vshPrint(ctl, " AppArmor"); +#endif +#ifdef WITH_SECDRIVER_SELINUX + vshPrint(ctl, " SELinux"); +#endif +#ifdef ENABLE_DEBUG + vshPrint(ctl, " Debug"); +#endif +#if WITH_READLINE + vshPrint(ctl, " Readline"); +#endif +#ifdef WITH_DRIVER_MODULES + vshPrint(ctl, " Modular"); +#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:e:h:l: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_CMD_GRP_SHELL, "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; + } + + if (virInitialize() < 0) { + vshError(ctl, "%s", _("Failed to initialize libvirt")); + 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..f7a8931 --- /dev/null +++ b/tools/virt-admin.h @@ -0,0 +1,47 @@ +/* + * 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 + */ +# define VIRT_ADMIN_CMD_GRP_SHELL "Virt-admin itself" + +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, Oct 16, 2015 at 08:12:19PM +0200, 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 + daemon/libvirtd.c | 1 - po/POTFILES.in | 1 + tools/Makefile.am | 28 ++- tools/virt-admin.c | 581 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 47 +++++ 6 files changed, 656 insertions(+), 3 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/daemon/libvirtd.c b/daemon/libvirtd.c index 250094b..26ccf59 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -522,7 +522,6 @@ daemonSetupNetworking(virNetServerPtr srv, virNetServerAddService(srv, svcRO, NULL) < 0) goto cleanup;
- /* Temporarily disabled */
Haha, still not cleaned up properly =)
if (sock_path_adm && false) { VIR_DEBUG("Registering unix socket %s", sock_path_adm); if (!(svcAdm = virNetServerServiceNewUNIX(sock_path_adm, 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..e68fe84 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,30 @@ virsh_CFLAGS = \ $(PIE_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(LIBXML_CFLAGS) + +virt_admin_SOURCES = \ + virt-admin.c virt-admin.h \ + $(NULL) + +virt_admin_LDFLAGS = \
misaligned backslashes
+ $(AM_LDFLAGS) \ + $(COVERAGE_LDFLAGS) \ + $(NULL) +virt_admin_LDADD = \ + $(STATIC_BINARIES) \ + $(PIE_LDFLAGS) \
I believe this should be in LDFLAGS, I see this is copy-paste from virsh that has it wrong as well. Well, not wrong as in "it won't work", but confusing.
+ ../src/libvirt.la \ + ../src/libvirt-admin.la \ + ../gnulib/lib/libgnu.la \
Another set of unaligned backslashes, plus there is no need for libvirt.la (as that is prerequisite of libvirt-admin.la) and neither for libgnu.la since that is BUILT_LIBADD into libvirt.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..cc33b7b --- /dev/null +++ b/tools/virt-admin.c @@ -0,0 +1,581 @@ +/* + * 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 "internal.h" +#include "virerror.h" +#include "viralloc.h" +#include "virfile.h" +#include "configmake.h"
If nitpicking, I'd sort these more so the vir* files are next to each other.
+#include "virthread.h" +#include "virstring.h" + +/* Gnulib doesn't guarantee SA_SIGINFO support. */ +#ifndef SA_SIGINFO +# define SA_SIGINFO 0 +#endif + +#define VIRT_ADMIN_PROMPT "virt-admin # " + +#define vshAdmDisconnect(ctl, conn) vshAdmDisconnectInternal(ctl, &conn); +
Don't do this. It looks like it won't modify the connect pointer and it's a macro even though it doesn't look like it, etc. Either call it directly with &priv->conn or just call it with priv if you don't like the dereference. You can then also remove the 'Internal' naming. ... Coming back after going through the file, why does it even have the second parameter? Are we going to connect to multiple admin servers like we do in virsh for migrations? That could clear few things up. The vshAdmConnect() function works like this already.
+static char *progname; + +static const vshCmdGrp cmdGroups[]; +static const vshClientHooks hooks; + +/* + * Detection of disconnections and automatic reconnection support + */ +static bool disconnected; /* we may have been disconnected */ +
Wow, have we. Maybe two more comments to make sure what this bool does? :-) Don't take it the wrong way, it just made me laugh. Anyway, it's a bit confusing to add variable here that you never set to anything until later when you introduce the disconnection event. I'm not sure why we have the 'disconnected' variable defined as int normally, maybe just to be sure that when doing disconnected++ it won't be stored into something that would overflow easily, but I think we can count on the fact that it will be stored in int anyway. And, mainly, Michal asked for it specifically in the first version, IIRC, so let's leave it like this while keeping in mind that in case this breaks something, it's his fault ;)
+static virAdmConnectPtr +vshAdmConnect(vshControl *ctl, unsigned int flags) +{ + vshAdmControlPtr priv = ctl->privData; + + priv->conn = virAdmConnectOpen(ctl->connname, flags); +
You're setting the pointer here, then returning it and then setting hte same place to the return value of this function. Is there a reason for that that I missed? Because otherwise it doesn't make sense.
+ 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 NULL; + } else { + if (priv->wantReconnect) + vshPrint(ctl, "%s\n", _("Reconnected to the admin server")); + else + vshPrint(ctl, "%s\n", _("Connected to the admin server")); + } + + return priv->conn; +} + +static int +vshAdmDisconnectInternal(vshControl *ctl, virAdmConnectPtr *conn) +{ + int ret = 0; + + if (!*conn) + return ret; + + ret = virAdmConnectClose(*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")); + *conn = NULL; + return ret; +} + +/* + * vshAdmReconnect: + * + * Reconnect to a daemon's admin server + * + */ +static void +vshAdmReconnect(vshControl *ctl) +{ + vshAdmControlPtr priv = ctl->privData; + if (priv->conn || disconnected) + priv->wantReconnect = true; + + vshAdmDisconnect(ctl, priv->conn); + priv->conn = vshAdmConnect(ctl, 0); +
This is what I was referencing from above with the dual assignment.
+ priv->wantReconnect = false; + disconnected = 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_("(re)connect to daemon's admin server") + }, + {.name = "desc", + .data = N_("(Re)connect to a daemon's administrating server.")
This reads weird, but after reading what we say in virsh.c, it's still way better =)
+ }, + {.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; +
OK, I see this is in virsh, too, but I wonder whether the problem I see here can cause some trouble. If vshCommandOptStringReq() fails, we don't connect anywhere, but the previous connname is cleared by the VIR_FREE(), so later on if reconnect needs to be done, it will connect somewhere else. Super-corner-case, but still...
+ ctl->connname = vshStrdup(ctl, name); + + vshAdmReconnect(ctl); + if (!priv->conn) + return false; + + return true;
Another part that's confusing. I though I knew why this is not 'return priv->conn', but you don't register the close callback here even in later patches, so I think it could directly return priv->conn as a bool (or !!priv->conn if you dislike the former).
+} + +static void * +vshAdmConnectionHandler(vshControl *ctl) +{ + vshAdmControlPtr priv = ctl->privData; + + if (!priv->conn || disconnected) + 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, priv->conn); + + 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:\n")); + vshPrint(ctl, "\n");
Well, this is bogus, ain't it?
+ vshPrint(ctl, "%s", _(" Miscellaneous:")); +#ifdef WITH_LIBVIRTD + vshPrint(ctl, " Daemon"); +#endif +#ifdef WITH_SECDRIVER_APPARMOR + vshPrint(ctl, " AppArmor"); +#endif +#ifdef WITH_SECDRIVER_SELINUX + vshPrint(ctl, " SELinux"); +#endif +#ifdef ENABLE_DEBUG + vshPrint(ctl, " Debug"); +#endif +#if WITH_READLINE + vshPrint(ctl, " Readline"); +#endif +#ifdef WITH_DRIVER_MODULES + vshPrint(ctl, " Modular");
Not needed either. Well, most of this is useless, I guess.
+#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:e:h:l: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_CMD_GRP_SHELL, "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; + } + + if (virInitialize() < 0) {
You want to call virAdmInitialize() here, I believe.
+ vshError(ctl, "%s", _("Failed to initialize libvirt")); + 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..f7a8931 --- /dev/null +++ b/tools/virt-admin.h @@ -0,0 +1,47 @@ +/* + * 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 + */ +# define VIRT_ADMIN_CMD_GRP_SHELL "Virt-admin itself" +
Does this need to be defined at all? I don't know what the origin of the _CMD_GRP_ macros is, but it sure doesn't make sense for me when it's not used anywhere.
+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
Most of what I commented on are no deal breakers, just nice-to-have-in things. I'll continue the review tomorrow, but that shouldn't keep anyone else from looking into it as well ;)
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

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 | 31 +++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 1 + tools/virt-admin.c | 2 +- 4 files changed, 34 insertions(+), 1 deletion(-) 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..f0824dd 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -386,3 +386,34 @@ 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. + */ +int +virAdmConnectIsAlive(virAdmConnectPtr conn) +{ + bool ret; + remoteAdminPrivPtr priv = conn->privateData; + + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + virObjectLock(priv); + ret = virNetClientIsOpen(priv->client); + virObjectUnlock(priv); + + if (ret) + return 1; + else + return 0; +} 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 cc33b7b..1bc10b0 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -171,7 +171,7 @@ vshAdmConnectionHandler(vshControl *ctl) if (!priv->conn || disconnected) vshAdmReconnect(ctl); - if (!priv->conn) { + if (!priv->conn || !virAdmConnectIsAlive(priv->conn)) { vshError(ctl, "%s", _("no valid connection")); return NULL; } -- 2.4.3

On Fri, Oct 16, 2015 at 08:12:20PM +0200, 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 | 31 +++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 1 + tools/virt-admin.c | 2 +- 4 files changed, 34 insertions(+), 1 deletion(-)
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..f0824dd 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -386,3 +386,34 @@ 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. + */ +int +virAdmConnectIsAlive(virAdmConnectPtr conn) +{ + bool ret; + remoteAdminPrivPtr priv = conn->privateData; + + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + virObjectLock(priv); + ret = virNetClientIsOpen(priv->client); + virObjectUnlock(priv); + + if (ret) + return 1; + else + return 0;
return ret; is fine here.
+} 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 cc33b7b..1bc10b0 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -171,7 +171,7 @@ vshAdmConnectionHandler(vshControl *ctl) if (!priv->conn || disconnected) vshAdmReconnect(ctl);
- if (!priv->conn) { + if (!priv->conn || !virAdmConnectIsAlive(priv->conn)) {
I know we don't do that in virsh, so it doesn't seem obvious here, but shouldn't we do reconnect also when the connection is not alive? I, personally, would add it into the previous condition as well. ACK.
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

+int +virAdmConnectIsAlive(virAdmConnectPtr conn) +{ + bool ret; + remoteAdminPrivPtr priv = conn->privateData; + + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + virObjectLock(priv); + ret = virNetClientIsOpen(priv->client); + virObjectUnlock(priv); + + if (ret) + return 1; + else + return 0;
return ret; is fine here.
Yep, thanks.
+} 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 cc33b7b..1bc10b0 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -171,7 +171,7 @@ vshAdmConnectionHandler(vshControl *ctl) if (!priv->conn || disconnected) vshAdmReconnect(ctl);
- if (!priv->conn) { + if (!priv->conn || !virAdmConnectIsAlive(priv->conn)) {
I know we don't do that in virsh, so it doesn't seem obvious here, but shouldn't we do reconnect also when the connection is not alive? I, personally, would add it into the previous condition as well.
Well, you're right. But things are more interesting than that. If you look at it closely (and I must have been blind or something until now), disconnect and virConnectIsAlive (standard libvirt) symbolize the very same thing, so the presence of disconnect variable in virsh is imho due to historical reasons (as virConnectIsAlive has been present since 0.9.X). In virt-admin however, virAdmConnectIsAlive API will be introduced at the same time as the client itself, so having that variable in the code will only contribute to more confusion. And I also need to have a look at vsh module which I added recently as 'disconnected' sneaked into that one as well and I'm not sure yet it is necessary to have it there at all.
ACK.
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

On Wed, Nov 04, 2015 at 01:56:40PM +0100, Erik Skultety wrote:
+int +virAdmConnectIsAlive(virAdmConnectPtr conn) +{ + bool ret; + remoteAdminPrivPtr priv = conn->privateData; + + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + virObjectLock(priv); + ret = virNetClientIsOpen(priv->client); + virObjectUnlock(priv); + + if (ret) + return 1; + else + return 0;
return ret; is fine here.
Yep, thanks.
+} 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 cc33b7b..1bc10b0 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -171,7 +171,7 @@ vshAdmConnectionHandler(vshControl *ctl) if (!priv->conn || disconnected) vshAdmReconnect(ctl);
- if (!priv->conn) { + if (!priv->conn || !virAdmConnectIsAlive(priv->conn)) {
I know we don't do that in virsh, so it doesn't seem obvious here, but shouldn't we do reconnect also when the connection is not alive? I, personally, would add it into the previous condition as well.
Well, you're right. But things are more interesting than that. If you look at it closely (and I must have been blind or something until now), disconnect and virConnectIsAlive (standard libvirt) symbolize the very same thing, so the presence of disconnect variable in virsh is imho due to historical reasons (as virConnectIsAlive has been present since 0.9.X). In virt-admin however, virAdmConnectIsAlive API will be introduced at the same time as the client itself, so having that variable in the code will only contribute to more confusion. And I also need to have a look at vsh module which I added recently as 'disconnected' sneaked into that one as well and I'm not sure yet it is necessary to have it there at all.
Oh, cool, I didn't go to such details. It would certainly be nice to have this cleaned up.
ACK.
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 f0824dd..cdc712c 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,121 +42,10 @@ 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" - 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; -} +#include "admin_remote.c" static void virAdmGlobalInit(void) -- 2.4.3

On Fri, Oct 16, 2015 at 08:12:21PM +0200, Erik Skultety wrote:
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 f0824dd..cdc712c 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,121 +42,10 @@
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" - static bool virAdmGlobalError; static virOnceControl virAdmGlobalOnce = VIR_ONCE_CONTROL_INITIALIZER;
[...] many lines [...]
+#include "admin_remote.c"
Simple code movement, ACK, although I would just move the include so that the virAdmGlobal* things are together. I have no idea why I had them so far apart... Martin
static void virAdmGlobalInit(void) -- 2.4.3
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

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

On Fri, Oct 16, 2015 at 08:12:22PM +0200, Erik Skultety wrote:
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(-)
ACK

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 | 132 +++++++++++++++++++++++++++++++--------- src/libvirt_admin_public.syms | 1 + tools/virt-admin.c | 39 ++++++++++++ 6 files changed, 147 insertions(+), 30 deletions(-) diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 72671c6..aae10b4 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..95aa49e 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 cdc712c..9fc8393 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,6 +151,40 @@ getSocketPath(const char *name) goto cleanup; } +static int +virAdmGetDefaultURI(virConfPtr conf, virURIPtr *uri) +{ + virConfValuePtr value = NULL; + const char *uristr = NULL; + + uristr = virGetEnvBlockSUID("LIBVIRT_ADMIN_DEFAULT_URI"); + if (uristr && *uristr) { + VIR_DEBUG("Using LIBVIRT_ADMIN_DEFAULT_URI '%s'", uristr); + } else if ((value = virConfGetValue(conf, "uri_default_admin"))) { + if (value->type != VIR_CONF_STRING) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected a string for 'uri_default_admin' config " + "parameter")); + return -1; + } + + 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"; + } + + if (!(*uri = virURIParse(uristr))) + return -1; + + return 0; +} + /** * virAdmConnectOpen: * @name: uri of the daemon to connect to, NULL for default @@ -170,6 +199,7 @@ virAdmConnectOpen(const char *name, unsigned int flags) { char *sock_path = NULL; virAdmConnectPtr conn = NULL; + virConfPtr conf = NULL; if (virAdmInitialize() < 0) goto error; @@ -180,7 +210,18 @@ virAdmConnectOpen(const char *name, unsigned int flags) if (!(conn = virAdmConnectNew())) goto error; - if (!(sock_path = getSocketPath(name))) + if (virGetLibvirtConfigFile(&conf) < 0) + goto error; + + if (!name) { + if (virAdmGetDefaultURI(conf, &conn->uri) < 0) + goto error; + } else { + if (!(conn->uri = virURIParse(name))) + goto error; + } + + if (!(sock_path = getSocketPath(conn->uri))) goto error; if (!(conn->privateData = remoteAdminPrivNew(sock_path))) @@ -304,3 +345,34 @@ virAdmConnectIsAlive(virAdmConnectPtr conn) else return 0; } + +/** + * 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))) + goto error; + + return uri; + + error: + virDispatchError(NULL); + return uri; +} 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 1bc10b0..c00a3b3 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -120,6 +120,39 @@ vshAdmReconnect(vshControl *ctl) disconnected = 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 * --------------- @@ -454,6 +487,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, Oct 16, 2015 at 08:12:23PM +0200, 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 | 132 +++++++++++++++++++++++++++++++--------- src/libvirt_admin_public.syms | 1 + tools/virt-admin.c | 39 ++++++++++++ 6 files changed, 147 insertions(+), 30 deletions(-)
diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 72671c6..aae10b4 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);
extra space after asterisk
+ # 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..95aa49e 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -397,6 +397,7 @@ struct _virConnect { */ struct _virAdmConnect { virObjectLockable object; + virURIPtr uri;
extra whitespace
void *privateData; virFreeCallback privateDataFreeFunc; diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index cdc712c..9fc8393 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;
This is the reason why I kept the conditions separated. So it's clear to read. You are changing the logic here, what you meant is to use || instead of &&. Either fix it to || or leave it unchanged.
} 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,6 +151,40 @@ getSocketPath(const char *name) goto cleanup; }
+static int +virAdmGetDefaultURI(virConfPtr conf, virURIPtr *uri) +{ + virConfValuePtr value = NULL; + const char *uristr = NULL; + + uristr = virGetEnvBlockSUID("LIBVIRT_ADMIN_DEFAULT_URI");
I don't see the point for the 'BlockSUID()' version, did I miss something?
+ if (uristr && *uristr) { + VIR_DEBUG("Using LIBVIRT_ADMIN_DEFAULT_URI '%s'", uristr); + } else if ((value = virConfGetValue(conf, "uri_default_admin"))) {
I would use 'admin' as a prefix, so we can differentiate any future config options. So either uri_admin_default or admin_uri_default.
+ if (value->type != VIR_CONF_STRING) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected a string for 'uri_default_admin' config " + "parameter")); + return -1; + } + + 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"; + } + + if (!(*uri = virURIParse(uristr))) + return -1; + + return 0; +} + /** * virAdmConnectOpen: * @name: uri of the daemon to connect to, NULL for default @@ -170,6 +199,7 @@ virAdmConnectOpen(const char *name, unsigned int flags) { char *sock_path = NULL; virAdmConnectPtr conn = NULL; + virConfPtr conf = NULL;
if (virAdmInitialize() < 0) goto error; @@ -180,7 +210,18 @@ virAdmConnectOpen(const char *name, unsigned int flags) if (!(conn = virAdmConnectNew())) goto error;
- if (!(sock_path = getSocketPath(name))) + if (virGetLibvirtConfigFile(&conf) < 0) + goto error; + + if (!name) { + if (virAdmGetDefaultURI(conf, &conn->uri) < 0)
Are we planning on using the @conf anywhere else? If not, I'd put the config getting inside this function.
+ goto error; + } else { + if (!(conn->uri = virURIParse(name)))
Change @name to @uri, so it's clear what it is.
+ goto error; + } + + if (!(sock_path = getSocketPath(conn->uri))) goto error;
if (!(conn->privateData = remoteAdminPrivNew(sock_path))) @@ -304,3 +345,34 @@ virAdmConnectIsAlive(virAdmConnectPtr conn) else return 0; } + +/** + * 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))) + goto error; + + return uri; + + error: + virDispatchError(NULL); + return uri;
You could save some lines with: if (!(uri = virURIFormat(conn->uri))) virDispatchError(NULL); return uri; But whatever flows your boat.
+} 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 1bc10b0..c00a3b3 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -120,6 +120,39 @@ vshAdmReconnect(vshControl *ctl) disconnected = 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 * --------------- @@ -454,6 +487,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
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

/** * virAdmConnectOpen: * @name: uri of the daemon to connect to, NULL for default @@ -170,6 +199,7 @@ virAdmConnectOpen(const char *name, unsigned int flags) { char *sock_path = NULL; virAdmConnectPtr conn = NULL; + virConfPtr conf = NULL;
if (virAdmInitialize() < 0) goto error; @@ -180,7 +210,18 @@ virAdmConnectOpen(const char *name, unsigned int flags) if (!(conn = virAdmConnectNew())) goto error;
- if (!(sock_path = getSocketPath(name))) + if (virGetLibvirtConfigFile(&conf) < 0) + goto error; + + if (!name) { + if (virAdmGetDefaultURI(conf, &conn->uri) < 0)
Are we planning on using the @conf anywhere else? If not, I'd put the config getting inside this function.
Actually, yes. A couple of days ago I realized that I forgot to introduce URI aliases support in this series, so I prepared a patch for that on my local branch, where I'm reusing the @conf reference as I need to read uri_aliases variable from the config.

On 10/16/2015 02:12 PM, 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 | 132 +++++++++++++++++++++++++++++++--------- src/libvirt_admin_public.syms | 1 + tools/virt-admin.c | 39 ++++++++++++ 6 files changed, 147 insertions(+), 30 deletions(-)
Ran the series through the Coverity checks... [...]
/** * virAdmConnectOpen: * @name: uri of the daemon to connect to, NULL for default @@ -170,6 +199,7 @@ virAdmConnectOpen(const char *name, unsigned int flags) { char *sock_path = NULL; virAdmConnectPtr conn = NULL; + virConfPtr conf = NULL;
if (virAdmInitialize() < 0) goto error; @@ -180,7 +210,18 @@ virAdmConnectOpen(const char *name, unsigned int flags) if (!(conn = virAdmConnectNew())) goto error;
- if (!(sock_path = getSocketPath(name))) + if (virGetLibvirtConfigFile(&conf) < 0) + goto error; +
conf is allocated now and will need to be free'd/cleaned appropriately via virConfFree() @ error: John
+ if (!name) { + if (virAdmGetDefaultURI(conf, &conn->uri) < 0) + goto error; + } else { + if (!(conn->uri = virURIParse(name))) + goto error; + } + + if (!(sock_path = getSocketPath(conn->uri))) goto error;
if (!(conn->privateData = remoteAdminPrivNew(sock_path))) @@ -304,3 +345,34 @@ virAdmConnectIsAlive(virAdmConnectPtr conn) else return 0; } + +/** + * 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))) + goto error; + + return uri; + + error: + virDispatchError(NULL); + return uri; +}
[...]

On 05/11/15 00:11, John Ferlan wrote:
On 10/16/2015 02:12 PM, 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 | 132 +++++++++++++++++++++++++++++++--------- src/libvirt_admin_public.syms | 1 + tools/virt-admin.c | 39 ++++++++++++ 6 files changed, 147 insertions(+), 30 deletions(-)
Ran the series through the Coverity checks...
[...]
/** * virAdmConnectOpen: * @name: uri of the daemon to connect to, NULL for default @@ -170,6 +199,7 @@ virAdmConnectOpen(const char *name, unsigned int flags) { char *sock_path = NULL; virAdmConnectPtr conn = NULL; + virConfPtr conf = NULL;
if (virAdmInitialize() < 0) goto error; @@ -180,7 +210,18 @@ virAdmConnectOpen(const char *name, unsigned int flags) if (!(conn = virAdmConnectNew())) goto error;
- if (!(sock_path = getSocketPath(name))) + if (virGetLibvirtConfigFile(&conf) < 0) + goto error; +
conf is allocated now and will need to be free'd/cleaned appropriately via virConfFree() @ error:
John
Luckily, I figured that out when I was playing with URI aliases support (coming in v3), but still, thank you very much for noticing :). 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. --- include/libvirt/libvirt-admin.h | 19 +++++++ src/admin/admin_remote.c | 36 +++++++++++++ src/datatypes.c | 20 +++++++ src/datatypes.h | 14 ++++- src/libvirt-admin.c | 112 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 2 + tools/virt-admin.c | 50 ++++++++++++++++++ 7 files changed, 252 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index aae10b4..1688201 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -56,6 +56,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..741b9a1 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,9 @@ remoteAdminConnectClose(virAdmConnectPtr conn) goto done; } + virNetClientSetCloseCallback(priv->client, NULL, conn->closeCallback, + virObjectFreeCallback); + rv = 0; done: diff --git a/src/datatypes.c b/src/datatypes.c index aeac301..603b168 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,6 +825,9 @@ virAdmConnectNew(void) if (!(ret = virObjectLockableNew(virAdmConnectClass))) return NULL; + if (!(ret->closeCallback = virObjectLockableNew(virAdmConnectCloseCallbackDataClass))) + return NULL; + return ret; } @@ -834,4 +840,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 95aa49e..aa160a8 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,7 @@ struct _virAdmConnect { void *privateData; virFreeCallback privateDataFreeFunc; + virAdmConnectCloseCallbackDataPtr closeCallback; }; diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 9fc8393..2a38e09 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -376,3 +376,115 @@ virAdmConnectGetURI(virAdmConnectPtr conn) virDispatchError(NULL); 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 c00a3b3..d352233 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -59,6 +59,51 @@ static const vshClientHooks hooks; */ static bool disconnected; /* we may have been disconnected */ +/* + * vshAdmCatchDisconnect: + * + * We get here when the connection was closed. Right now, we only save the fact + * the event was raised. + */ +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); + } + disconnected++; + } +} + static virAdmConnectPtr vshAdmConnect(vshControl *ctl, unsigned int flags) { @@ -73,6 +118,10 @@ vshAdmConnect(vshControl *ctl, unsigned int flags) vshError(ctl, "%s", _("Failed to connect to the admin server")); return NULL; } 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 @@ -90,6 +139,7 @@ vshAdmDisconnectInternal(vshControl *ctl, virAdmConnectPtr *conn) if (!*conn) return ret; + virAdmConnectUnregisterCloseCallback(*conn, vshAdmCatchDisconnect); ret = virAdmConnectClose(*conn); if (ret < 0) vshError(ctl, "%s", _("Failed to disconnect from the admin server")); -- 2.4.3

On Fri, Oct 16, 2015 at 08:12:24PM +0200, 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. --- include/libvirt/libvirt-admin.h | 19 +++++++ src/admin/admin_remote.c | 36 +++++++++++++ src/datatypes.c | 20 +++++++ src/datatypes.h | 14 ++++- src/libvirt-admin.c | 112 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 2 + tools/virt-admin.c | 50 ++++++++++++++++++ 7 files changed, 252 insertions(+), 1 deletion(-)
diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index aae10b4..1688201 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -56,6 +56,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..741b9a1 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,9 @@ remoteAdminConnectClose(virAdmConnectPtr conn) goto done; }
+ virNetClientSetCloseCallback(priv->client, NULL, conn->closeCallback, + virObjectFreeCallback);
Again, yes, we have this code in place already, in the remote driver, and I understand it actually means RemoveCloseCallback, but why aren't we passing NULL as all three parameters after the client?
+ rv = 0;
done: diff --git a/src/datatypes.c b/src/datatypes.c index aeac301..603b168 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,6 +825,9 @@ virAdmConnectNew(void) if (!(ret = virObjectLockableNew(virAdmConnectClass))) return NULL;
+ if (!(ret->closeCallback = virObjectLockableNew(virAdmConnectCloseCallbackDataClass))) + return NULL; + return ret; }
@@ -834,4 +840,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 95aa49e..aa160a8 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,7 @@ struct _virAdmConnect {
void *privateData; virFreeCallback privateDataFreeFunc;
I'd add one empty line here. Otherwise looks fine to me (with the adjustments that will be needed after previous patches are fixed, of course). Martin

On 10/16/2015 02:12 PM, 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. --- include/libvirt/libvirt-admin.h | 19 +++++++ src/admin/admin_remote.c | 36 +++++++++++++ src/datatypes.c | 20 +++++++ src/datatypes.h | 14 ++++- src/libvirt-admin.c | 112 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 2 + tools/virt-admin.c | 50 ++++++++++++++++++ 7 files changed, 252 insertions(+), 1 deletion(-)
Ran the series through the Coverity checks... [...]
diff --git a/src/datatypes.c b/src/datatypes.c index aeac301..603b168 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,6 +825,9 @@ virAdmConnectNew(void) if (!(ret = virObjectLockableNew(virAdmConnectClass))) return NULL;
+ if (!(ret->closeCallback = virObjectLockableNew(virAdmConnectCloseCallbackDataClass))) + return NULL; +
You'll need to free ret or have some sort of cleanup here. John
return ret; }
@@ -834,4 +840,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); }
[...]

#undef DECLARE_CLASS_COMMON #undef DECLARE_CLASS_LOCKABLE @@ -822,6 +825,9 @@ virAdmConnectNew(void) if (!(ret = virObjectLockableNew(virAdmConnectClass))) return NULL;
+ if (!(ret->closeCallback = virObjectLockableNew(virAdmConnectCloseCallbackDataClass))) + return NULL; +
You'll need to free ret or have some sort of cleanup here.
John
Unlike 6/7, I haven't noticed this one, so again, thanks :). Erik

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 1688201..d76b1bd 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -56,6 +56,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 2a38e09..666d39e 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -488,3 +488,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 d352233..882c48c 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -202,6 +202,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 @@ -543,6 +604,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, Oct 16, 2015 at 08:12:25PM +0200, 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 1688201..d76b1bd 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -56,6 +56,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 2a38e09..666d39e 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -488,3 +488,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";
I wanted to differentiate two different calls with admCall and adminCall, I wish I remembered which were those. Otherwise looks fine to me.

--- tools/Makefile.am | 8 +- tools/virt-admin.pod | 278 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 tools/virt-admin.pod diff --git a/tools/Makefile.am b/tools/Makefile.am index e68fe84..d8cc1e7 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 @@ -292,6 +293,11 @@ virsh.1: virsh.pod $(top_srcdir)/configure.ac && if grep 'POD ERROR' $(srcdir)/$@ ; then \ rm $(srcdir)/$@; exit 1; fi +virt-admin.1: virt-admin.pod $(top_srcdir)/configure.ac + $(AM_V_GEN)$(POD2MAN) $< $(srcdir)/$@ \ + && if grep 'POD ERROR' $(srcdir)/$@ ; then \ + rm $(srcdir)/$@; exit 1; fi + install-data-local: install-init install-systemd uninstall-local: uninstall-init uninstall-systemd diff --git a/tools/virt-admin.pod b/tools/virt-admin.pod new file mode 100644 index 0000000..f08dfc5 --- /dev/null +++ b/tools/virt-admin.pod @@ -0,0 +1,278 @@ +=head1 NAME + +virt-admin - virtualization daemon administrating user 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 administrating interface to modify/tweak +the libvirt daemon configuration as well as to monitor and manage 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<VIRSH_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 + +Due to still limited capabilities of libvirt-admin library is usage of +B<virt-admin> tool, which is under continuous development, currently meant as +a feature preview only and its capabilities are very constrained at the moment, +comprising from connection establishment and generic commands only. Also note +that with upcoming features/commands, no backwards compatibility can be +guaranteed yet, since B<virt-admin> relies on I<libvirt-admin> library where +it's also not guaranteed that the public APIs won't change over the first +several releases. + +Most B<virt-admin> operations rely upon the I<libvirt-admin> library being able +to connect to an already running libvirtd service. This can usually be done +using the command B<service libvirtd start>. + +Running B<virt-admin> requires root privileges due to the +communications channels used to talk to the daemon. An exempt to this rule would +be adding user to a group with write permission to the admin socket. + +=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 major 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 libvirt 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. Currently, only +connection to libvirtd admin server is supported. +If I<LIBVIRT_ADMIN_DEFAULT_URI> or I<default-admin-uri> (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<default-admin-uri> 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 VISUAL + +The editor to use by the B<edit> and related options. + +=item EDITOR + +The editor to use by the B<edit> and related options, if C<VISUAL> +is not set. + +=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 xm man page by: + Sean Dague <sean at dague dot net> + Daniel Stekloff <dsteklof at us dot ibm dot com> + +=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-install(1)>, L<virt-xml-validate(1)>, L<virt-top(1)>, +L<virt-df(1)>, L<http://www.libvirt.org/> + +=cut -- 2.4.3

On Fri, Oct 16, 2015 at 08:12:26PM +0200, Erik Skultety wrote:
--- tools/Makefile.am | 8 +- tools/virt-admin.pod | 278 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 tools/virt-admin.pod
diff --git a/tools/Makefile.am b/tools/Makefile.am index e68fe84..d8cc1e7 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 @@ -292,6 +293,11 @@ virsh.1: virsh.pod $(top_srcdir)/configure.ac && if grep 'POD ERROR' $(srcdir)/$@ ; then \ rm $(srcdir)/$@; exit 1; fi
+virt-admin.1: virt-admin.pod $(top_srcdir)/configure.ac + $(AM_V_GEN)$(POD2MAN) $< $(srcdir)/$@ \ + && if grep 'POD ERROR' $(srcdir)/$@ ; then \ + rm $(srcdir)/$@; exit 1; fi +
If you do s/virt-admin/%, then you can make it a general rule and clean up the makefile a little bit.
install-data-local: install-init install-systemd
uninstall-local: uninstall-init uninstall-systemd diff --git a/tools/virt-admin.pod b/tools/virt-admin.pod new file mode 100644 index 0000000..f08dfc5 --- /dev/null +++ b/tools/virt-admin.pod @@ -0,0 +1,278 @@ +=head1 NAME + +virt-admin - virtualization daemon administrating user interface +
This reads weirdly. I would say either "user interface for daemon administration" or "daemon administration interface" or something similar.
+=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 administrating interface to modify/tweak
Modify and tweak have the same meaning here.
+the libvirt daemon configuration as well as to monitor and manage all clients
Not only libvirt, it will be enabled for other daemons as well. And it will do more, how about abstracting it a bit, e.g.: The B<virt-admin> program is the main administrating interface for modifying daemon configuration in runtime as well as monitoring and changing daemon behaviour. or something along those lines.
+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<VIRSH_DEBUG>
s/VIRSH_DEBUG/VIRT_ADMIN_DEBUG/ and also add that info here (copy-paste with name changes from virsh.pod is fine)
+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. +
While on this, I've found out that if I run 'virt-admin -h' it errors out with: error: option '-h'/'--help' requires an argument but 'virt-admin --help' works fine.
+=item B<-l>, B<--log> I<FILE> + +Output logging details to I<FILE>. + +=item B<-q>, B<--quiet> + +Avoid extra informational messages. +
One of these "extra informational messages" is newline after any command. It can be seen clearly with 'echo asdf' run in virt-admin -q and without -q :) Unfortunately, virsh does that too.
+=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 + +Due to still limited capabilities of libvirt-admin library is usage of +B<virt-admin> tool, which is under continuous development, currently meant as +a feature preview only and its capabilities are very constrained at the moment, +comprising from connection establishment and generic commands only. Also note +that with upcoming features/commands, no backwards compatibility can be +guaranteed yet, since B<virt-admin> relies on I<libvirt-admin> library where +it's also not guaranteed that the public APIs won't change over the first +several releases. +
Although this is nice to say, I don't think we can do that. From the point when we enable it, it must be backward compatible. We can break compatibility only until we remove that ' && false'.
+Most B<virt-admin> operations rely upon the I<libvirt-admin> library being able +to connect to an already running libvirtd service. This can usually be done +using the command B<service libvirtd start>. +
I would either mention other services since it can be said to connect somewhere else. Or not mention this at all. If people run this command, they most probably have daemon running and want to do something to it.
+Running B<virt-admin> requires root privileges due to the +communications channels used to talk to the daemon. An exempt to this rule would +be adding user to a group with write permission to the admin socket. +
I wouldn't mention the group privilege, or add something similar to what's mentioned before the unix_sock_admin_perms config option.
+=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 major version info about which libvirt library was this
We also print out minor and micro version.
+client built from. As opposed to I<virsh> client, the output already includes +the version of the libvirt daemon. +
not necessarily libvirt, also I would not completely error out when connection to daemon cannot be established. Also we throw too many errors in that case, look at it: error: Failed to connect to the admin server error: no valid connection error: Failed to connect socket to '/var/run/libvirt/libvirt-admin-sock': No such file or directory That's pretty confusing for the user, well, mainly for users who don't read their error messages if they have more than one line =D We should clear up the vshError() calls. My reasoning for this is that we might ask someone for the version info and use that for debugging, so the more we can print the better, but even something small can help.
+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. Currently, only +connection to libvirtd admin server is supported.
These are all the things we will have to go through and clean up later after we turn it on for other daemons.
+If I<LIBVIRT_ADMIN_DEFAULT_URI> or I<default-admin-uri> (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<default-admin-uri> 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 VISUAL + +The editor to use by the B<edit> and related options. +
there is no edit option, and we don't know if there will be, just generelize it (like it will be used when running an editor will be needed or something
+=item EDITOR + +The editor to use by the B<edit> and related options, if C<VISUAL> +is not set. + +=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 xm man page by: + Sean Dague <sean at dague dot net> + Daniel Stekloff <dsteklof at us dot ibm dot com> +
This is more probably based on the virsh man page, but that's just my guess ;)
+=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-install(1)>, L<virt-xml-validate(1)>, L<virt-top(1)>, +L<virt-df(1)>, L<http://www.libvirt.org/> +
virt-top, virt-df and virt-install are not that relevant here also consider adding virt-host-validate (what if we add support for admin interface checking there).
+=cut -- 2.4.3
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
participants (3)
-
Erik Skultety
-
John Ferlan
-
Martin Kletzander