[libvirt] [PATCH 0/9] Introduce virt-admin client

Erik Skultety (9): libvirt: Export libvirt config getters by moving them to util virt-admin: Introduce first working skeleton admin: Do not generate remoteAdminConnect{Open,Close} admin: Add support for connection close callbacks admin: Introduce virAdmConnectGetLibVersion admin: Add URI support and introduce virAdmGetDefaultURI virt-admin: Introduce vshAdmCatchDisconnect handler virt-admin: Register async IO event polling virt-admin: Provide a man page for virt-admin .gitignore | 1 + daemon/admin_server.c | 9 + daemon/libvirtd.c | 3 +- include/libvirt/libvirt-admin.h | 25 ++ po/POTFILES.in | 2 + src/admin/admin_protocol.x | 15 +- src/datatypes.c | 24 ++ src/datatypes.h | 15 +- src/libvirt-admin.c | 387 ++++++++++++++++++-- src/libvirt.c | 55 +-- src/libvirt_admin_private.syms | 1 + src/libvirt_admin_public.syms | 5 + src/libvirt_private.syms | 2 +- src/rpc/gendispatch.pl | 11 +- src/util/virconf.c | 52 +++ src/util/virconf.h | 2 +- tools/Makefile.am | 36 +- tools/virt-admin.c | 759 ++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 71 ++++ tools/virt-admin.pod | 297 ++++++++++++++++ 20 files changed, 1675 insertions(+), 97 deletions(-) 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 | 2 +- src/util/virconf.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ src/util/virconf.h | 2 +- 4 files changed, 55 insertions(+), 56 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..c044287 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1332,7 +1332,7 @@ virConfTypeToString; virConfWalk; virConfWriteFile; virConfWriteMem; - +virGetLibvirtConfigFile; # util/vircrypto.h virCryptoHashString; 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 13.10.2015 15:38, 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 | 2 +- src/util/virconf.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ src/util/virconf.h | 2 +- 4 files changed, 55 insertions(+), 56 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..c044287 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1332,7 +1332,7 @@ virConfTypeToString; virConfWalk; virConfWriteFile; virConfWriteMem; - +virGetLibvirtConfigFile;
No, we like the double empty lines here.
# util/vircrypto.h virCryptoHashString; 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__ */
Otherwise looking good. ACK. Michal

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 | 3 +- include/libvirt/libvirt-admin.h | 1 + po/POTFILES.in | 1 + src/libvirt-admin.c | 31 +++ src/libvirt_admin_public.syms | 1 + tools/Makefile.am | 28 +- tools/virt-admin.c | 604 ++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 71 +++++ 9 files changed, 737 insertions(+), 4 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..8734f93 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -522,8 +522,7 @@ daemonSetupNetworking(virNetServerPtr srv, virNetServerAddService(srv, svcRO, NULL) < 0) goto cleanup; - /* Temporarily disabled */ - if (sock_path_adm && false) { + if (sock_path_adm) { VIR_DEBUG("Registering unix socket %s", sock_path_adm); if (!(svcAdm = virNetServerServiceNewUNIX(sock_path_adm, unix_sock_adm_mask, 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/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/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/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..4964e3d --- /dev/null +++ b/tools/virt-admin.c @@ -0,0 +1,604 @@ +/* + * 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/>. + * + * Daniel Veillard <veillard@redhat.com> + * Karel Zak <kzak@redhat.com> + * Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> +#include "virt-admin.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> +#include <errno.h> +#include <getopt.h> +#include <sys/time.h> +#include <fcntl.h> +#include <locale.h> +#include <time.h> +#include <limits.h> +#include <sys/stat.h> +#include <inttypes.h> +#include <signal.h> + +#if WITH_READLINE +# include <readline/readline.h> +# include <readline/history.h> +#endif + +#include "internal.h" +#include "virerror.h" +#include "virbuffer.h" +#include "viralloc.h" +#include <libvirt/libvirt-qemu.h> +#include <libvirt/libvirt-lxc.h> +#include "virfile.h" +#include "configmake.h" +#include "virthread.h" +#include "virtypedparam.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 int 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 = 0; +} + +/* --------------- + * 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_("Connect to daemon's administrating server. Note that having " + "an invalid or no connection causes the 'connect' command to be " + "automatically invoked using the default URI when a command " + "which requires an active connection is executed.") + }, + {.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 || !virAdmConnectIsAlive(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_MANAGEMENT, "domain", domManagementCmds}, + {VIRT_ADMIN_CMD_GRP_MONITORING, "monitor", domMonitoringCmds},*/ + {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..962d5bc --- /dev/null +++ b/tools/virt-admin.h @@ -0,0 +1,71 @@ +/* + * 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/>. + * + * Daniel Veillard <veillard@redhat.com> + * Karel Zak <kzak@redhat.com> + * Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef VIRT_ADMIN_H +# define VIRT_ADMIN_H + +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +# include <stdarg.h> +# include <unistd.h> +# include <sys/stat.h> +# include <termios.h> + +# include "internal.h" +# include "virerror.h" +# include "virthread.h" +# include "vsh.h" + +# define VIR_FROM_THIS VIR_FROM_NONE + +/* + * Command group types + */ +# define VIRT_ADMIN_CMD_GRP_MANAGEMENT "Daemon/server/client Management" +# define VIRT_ADMIN_CMD_GRP_MONITORING "Daemon/server/client Monitoring" +# 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; + /*bool useGetInfo; [> must use virDomainGetInfo, since + virDomainGetState is not supported <]*/ +}; + +/* Filter flags for various vshCommandOpt*By() functions */ +typedef enum { + VIRSH_BYID = (1 << 1), + VIRSH_BYUUID = (1 << 2), + VIRSH_BYNAME = (1 << 3), + VIRSH_BYMAC = (1 << 4), +} vshAdmLookupByFlags; + +#endif /* VIRT_ADMIN_H */ -- 2.4.3

On 13.10.2015 15:38, 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 | 3 +- include/libvirt/libvirt-admin.h | 1 + po/POTFILES.in | 1 + src/libvirt-admin.c | 31 +++ src/libvirt_admin_public.syms | 1 + tools/Makefile.am | 28 +- tools/virt-admin.c | 604 ++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 71 +++++ 9 files changed, 737 insertions(+), 4 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..8734f93 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -522,8 +522,7 @@ daemonSetupNetworking(virNetServerPtr srv, virNetServerAddService(srv, svcRO, NULL) < 0) goto cleanup;
- /* Temporarily disabled */ - if (sock_path_adm && false) { + if (sock_path_adm) { VIR_DEBUG("Registering unix socket %s", sock_path_adm); if (!(svcAdm = virNetServerServiceNewUNIX(sock_path_adm, unix_sock_adm_mask, 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);
I'd split new API introduction into a separate commit. But that's not a show stopper.
# ifdef __cplusplus } 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/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/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..4964e3d --- /dev/null +++ b/tools/virt-admin.c @@ -0,0 +1,604 @@ +/* + * 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/>. + * + * Daniel Veillard <veillard@redhat.com> + * Karel Zak <kzak@redhat.com> + * Daniel P. Berrange <berrange@redhat.com>
I did not know that Karel or Daniel contributed to the new file ...
+ */ + +#include <config.h> +#include "virt-admin.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> +#include <errno.h> +#include <getopt.h> +#include <sys/time.h> +#include <fcntl.h> +#include <locale.h> +#include <time.h> +#include <limits.h> +#include <sys/stat.h> +#include <inttypes.h> +#include <signal.h>
This seems like a lot of includes ...
+ +#if WITH_READLINE +# include <readline/readline.h> +# include <readline/history.h> +#endif + +#include "internal.h" +#include "virerror.h" +#include "virbuffer.h" +#include "viralloc.h" +#include <libvirt/libvirt-qemu.h> +#include <libvirt/libvirt-lxc.h>
We are certainly not going to need libvirt-qemu nor libvirt-lxc here.
+#include "virfile.h" +#include "configmake.h" +#include "virthread.h" +#include "virtypedparam.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 int disconnected; /* we may have been disconnected */
This is used as boolean. s/int/bool/.
+ +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 = 0; +} + +/* --------------- + * 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_("Connect to daemon's administrating server. Note that having " + "an invalid or no connection causes the 'connect' command to be " + "automatically invoked using the default URI when a command " + "which requires an active connection is executed.") + }, + {.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 || !virAdmConnectIsAlive(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_MANAGEMENT, "domain", domManagementCmds}, + {VIRT_ADMIN_CMD_GRP_MONITORING, "monitor", domMonitoringCmds},*/
Just drop these and introduce when needed.
+ {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..962d5bc --- /dev/null +++ b/tools/virt-admin.h @@ -0,0 +1,71 @@ +/* + * 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/>. + * + * Daniel Veillard <veillard@redhat.com> + * Karel Zak <kzak@redhat.com> + * Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef VIRT_ADMIN_H +# define VIRT_ADMIN_H + +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +# include <stdarg.h> +# include <unistd.h> +# include <sys/stat.h> +# include <termios.h>
Again, plenty of includes. Some of them are duplicated in .c some of them are not needed (e.g. termios.h).
+ +# include "internal.h" +# include "virerror.h" +# include "virthread.h" +# include "vsh.h" + +# define VIR_FROM_THIS VIR_FROM_NONE + +/* + * Command group types + */ +# define VIRT_ADMIN_CMD_GRP_MANAGEMENT "Daemon/server/client Management" +# define VIRT_ADMIN_CMD_GRP_MONITORING "Daemon/server/client Monitoring"
These ^^ are not used yet.
+# 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; + /*bool useGetInfo; [> must use virDomainGetInfo, since + virDomainGetState is not supported <]*/
Er, what? virDomainGetInfo? I believe this is a leftover from copy and paste. Drop it.
+}; + +/* Filter flags for various vshCommandOpt*By() functions */ +typedef enum { + VIRSH_BYID = (1 << 1), + VIRSH_BYUUID = (1 << 2), + VIRSH_BYNAME = (1 << 3), + VIRSH_BYMAC = (1 << 4), +} vshAdmLookupByFlags; +
And again here.
+#endif /* VIRT_ADMIN_H */
Otherwise looking good. ACK. Michal

On Wed, Oct 14, 2015 at 01:25:13PM +0200, Michal Privoznik wrote:
On 13.10.2015 15:38, 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 | 3 +- include/libvirt/libvirt-admin.h | 1 + po/POTFILES.in | 1 + src/libvirt-admin.c | 31 +++ src/libvirt_admin_public.syms | 1 + tools/Makefile.am | 28 +- tools/virt-admin.c | 604 ++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.h | 71 +++++ 9 files changed, 737 insertions(+), 4 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..8734f93 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -522,8 +522,7 @@ daemonSetupNetworking(virNetServerPtr srv, virNetServerAddService(srv, svcRO, NULL) < 0) goto cleanup;
- /* Temporarily disabled */ - if (sock_path_adm && false) { + if (sock_path_adm) {
Also (as said already, I'm sure you did this by mistake) this should go in with version change to 1.3 and no earlier then when we decide we have first set of APIs. I know you'r enot going to push this, but I'm just making sure that someone will not push or ACK it by mistake. Thanks. Martin

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/libvirt-admin.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/libvirt-admin.c b/src/libvirt-admin.c index f0824dd..e12e2b8 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -126,6 +126,51 @@ call(virAdmConnectPtr conn, static bool virAdmGlobalError; static virOnceControl virAdmGlobalOnce = VIR_ONCE_CONTROL_INITIALIZER; +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 13.10.2015 15:38, 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/libvirt-admin.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/libvirt-admin.c b/src/libvirt-admin.c index f0824dd..e12e2b8 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -126,6 +126,51 @@ call(virAdmConnectPtr conn, static bool virAdmGlobalError; static virOnceControl virAdmGlobalOnce = VIR_ONCE_CONTROL_INITIALIZER;
+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) {
I don't want to be picky, but these should really go into a separate file. Perhaps somewhere under src/admin/? Michal

As we need a client disconnect handler, we need a mechanism to register such handlers for a client. --- include/libvirt/libvirt-admin.h | 19 ++++++ src/datatypes.c | 22 +++++++ src/datatypes.h | 14 +++- src/libvirt-admin.c | 142 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 2 + tools/virt-admin.c | 5 ++ 6 files changed, 203 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 72671c6..4539ac6 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -54,6 +54,25 @@ int virAdmConnectClose(virAdmConnectPtr conn); int virAdmConnectRef(virAdmConnectPtr conn); int virAdmConnectIsAlive(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/datatypes.c b/src/datatypes.c index 12bcfc1..9e374e4 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; } @@ -832,4 +838,20 @@ virAdmConnectDispose(void *obj) if (conn->privateDataFreeFunc) conn->privateDataFreeFunc(conn); + + virObjectUnref(conn->closeCallback); +} + +static void +virAdmConnectCloseCallbackDataDispose(void *obj) +{ + virAdmConnectCloseCallbackDataPtr cb_data = obj; + + virObjectLock(cb_data); + + cb_data->callback = NULL; + if (cb_data->freeCallback) + cb_data->freeCallback(cb_data->opaque); + + virObjectUnlock(cb_data); } diff --git a/src/datatypes.h b/src/datatypes.h index be108fe..33df476 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: * @@ -400,6 +411,7 @@ struct _virAdmConnect { void *privateData; virFreeCallback privateDataFreeFunc; + virAdmConnectCloseCallbackDataPtr closeCallback; }; diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index e12e2b8..9ad124e 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -64,9 +64,31 @@ remoteAdminPrivDispose(void *opaque) remoteAdminPrivPtr priv = opaque; virObjectUnref(priv->program); + virNetClientClose(priv->client); virObjectUnref(priv->client); } +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 callFull(virAdmConnectPtr conn ATTRIBUTE_UNUSED, @@ -137,6 +159,11 @@ remoteAdminConnectOpen(virAdmConnectPtr conn, unsigned int flags) args.flags = flags; + 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) { @@ -164,6 +191,9 @@ remoteAdminConnectClose(virAdmConnectPtr conn) goto done; } + virNetClientSetCloseCallback(priv->client, NULL, conn->closeCallback, + virObjectFreeCallback); + rv = 0; done: @@ -462,3 +492,115 @@ virAdmConnectIsAlive(virAdmConnectPtr conn) else return 0; } + +/** + * 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 16cfd42..5b9ba51 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -16,4 +16,6 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectClose; virAdmConnectRef; virAdmConnectIsAlive; + virAdmConnectRegisterCloseCallback; + virAdmConnectUnregisterCloseCallback; }; diff --git a/tools/virt-admin.c b/tools/virt-admin.c index 4964e3d..679ae99 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -91,6 +91,10 @@ vshAdmConnect(vshControl *ctl, unsigned int flags) vshError(ctl, "%s", _("Failed to connect to the admin server")); return NULL; } else { + if (virAdmConnectRegisterCloseCallback(priv->conn, NULL, NULL, + NULL) < 0) + vshError(ctl, "%s", _("Unable to register disconnect callback")); + if (priv->wantReconnect) vshPrint(ctl, "%s\n", _("Reconnected to the admin server")); else @@ -108,6 +112,7 @@ vshAdmDisconnectInternal(vshControl *ctl, virAdmConnectPtr *conn) if (!*conn) return ret; + virAdmConnectUnregisterCloseCallback(*conn, NULL); ret = virAdmConnectClose(*conn); if (ret < 0) vshError(ctl, "%s", _("Failed to disconnect from the admin server")); -- 2.4.3

On 13.10.2015 15:38, Erik Skultety wrote:
As we need a client disconnect handler, we need a mechanism to register such handlers for a client. --- include/libvirt/libvirt-admin.h | 19 ++++++ src/datatypes.c | 22 +++++++ src/datatypes.h | 14 +++- src/libvirt-admin.c | 142 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_admin_public.syms | 2 + tools/virt-admin.c | 5 ++ 6 files changed, 203 insertions(+), 1 deletion(-)
diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 72671c6..4539ac6 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -54,6 +54,25 @@ int virAdmConnectClose(virAdmConnectPtr conn); int virAdmConnectRef(virAdmConnectPtr conn); int virAdmConnectIsAlive(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/datatypes.c b/src/datatypes.c index 12bcfc1..9e374e4 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; }
@@ -832,4 +838,20 @@ virAdmConnectDispose(void *obj)
if (conn->privateDataFreeFunc) conn->privateDataFreeFunc(conn); + + virObjectUnref(conn->closeCallback); +} + +static void +virAdmConnectCloseCallbackDataDispose(void *obj) +{ + virAdmConnectCloseCallbackDataPtr cb_data = obj; + + virObjectLock(cb_data); + + cb_data->callback = NULL;
This ^^ shouldn't be needed. We are the last ones to see cb_data alive.
+ if (cb_data->freeCallback) + cb_data->freeCallback(cb_data->opaque); + + virObjectUnlock(cb_data); } diff --git a/src/datatypes.h b/src/datatypes.h index be108fe..33df476 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: * @@ -400,6 +411,7 @@ struct _virAdmConnect {
void *privateData; virFreeCallback privateDataFreeFunc; + virAdmConnectCloseCallbackDataPtr closeCallback; };
diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index e12e2b8..9ad124e 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -64,9 +64,31 @@ remoteAdminPrivDispose(void *opaque) remoteAdminPrivPtr priv = opaque;
virObjectUnref(priv->program); + virNetClientClose(priv->client); virObjectUnref(priv->client); }
+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); +}
Again, remoteAdmin** funcs should go into a separate file.
static int callFull(virAdmConnectPtr conn ATTRIBUTE_UNUSED, @@ -137,6 +159,11 @@ remoteAdminConnectOpen(virAdmConnectPtr conn, unsigned int flags)
args.flags = flags;
+ 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) { @@ -164,6 +191,9 @@ remoteAdminConnectClose(virAdmConnectPtr conn) goto done; }
+ virNetClientSetCloseCallback(priv->client, NULL, conn->closeCallback, + virObjectFreeCallback); + rv = 0;
done: @@ -462,3 +492,115 @@ virAdmConnectIsAlive(virAdmConnectPtr conn) else return 0; } + +/** + * 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 16cfd42..5b9ba51 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -16,4 +16,6 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectClose; virAdmConnectRef; virAdmConnectIsAlive; + virAdmConnectRegisterCloseCallback; + virAdmConnectUnregisterCloseCallback; }; diff --git a/tools/virt-admin.c b/tools/virt-admin.c index 4964e3d..679ae99 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -91,6 +91,10 @@ vshAdmConnect(vshControl *ctl, unsigned int flags) vshError(ctl, "%s", _("Failed to connect to the admin server")); return NULL; } else { + if (virAdmConnectRegisterCloseCallback(priv->conn, NULL, NULL, + NULL) < 0) + vshError(ctl, "%s", _("Unable to register disconnect callback")); +
This can never succeed. virADmConnectRegisterCloseCallback checks for NULL cb. So after this you will always see the error.
if (priv->wantReconnect) vshPrint(ctl, "%s\n", _("Reconnected to the admin server")); else @@ -108,6 +112,7 @@ vshAdmDisconnectInternal(vshControl *ctl, virAdmConnectPtr *conn) if (!*conn) return ret;
+ virAdmConnectUnregisterCloseCallback(*conn, NULL); ret = virAdmConnectClose(*conn); if (ret < 0) vshError(ctl, "%s", _("Failed to disconnect from the admin server"));
Otherwise looking good. Michal

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/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 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 132 insertions(+), 3 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 4539ac6..2358173 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -73,6 +73,9 @@ int virAdmConnectRegisterCloseCallback(virAdmConnectPtr conn, int virAdmConnectUnregisterCloseCallback(virAdmConnectPtr conn, virAdmConnectCloseFunc cb); +int virAdmConnectGetLibVersion(virAdmConnectPtr conn, + unsigned long long *libVer); + # ifdef __cplusplus } # endif 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/libvirt-admin.c b/src/libvirt-admin.c index 9ad124e..2da3d43 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -604,3 +604,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 5b9ba51..273af4d 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -18,4 +18,5 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectIsAlive; virAdmConnectRegisterCloseCallback; virAdmConnectUnregisterCloseCallback; + virAdmConnectGetLibVersion; }; diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index b6d50f3..d5be6e8 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; @@ -125,7 +125,9 @@ sub push_privconn { # Read the input file (usually remote_protocol.x) and form an # opinion about the name, args and return type of each RPC. my ($name, $ProcName, $id, $flags, %calls, @calls, %opts); - +#print "procprefix $procprefix\n"; +#print "structprefix $structprefix\n"; +#print "protocol $protocol\n"; my $collect_args_members = 0; my $collect_ret_members = 0; my $collect_opts = 0; @@ -1401,8 +1403,13 @@ elsif ($mode eq "client") { my $ret_name = $1; if ($call->{ProcName} =~ m/Get(Lib)?Version/) { + 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 679ae99..0c807f2 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -143,6 +143,67 @@ vshAdmReconnect(vshControl *ctl) disconnected = 0; } +/* + * "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 * --------------- @@ -480,6 +541,12 @@ static const vshCmdDef vshAdmCmds[] = { VSH_CMD_HELP, VSH_CMD_PWD, VSH_CMD_QUIT, + {.name = "version", + .handler = cmdVersion, + .opts = NULL, + .info = info_version, + .flags = 0 + }, {.name = "connect", .handler = cmdConnect, .opts = opts_connect, -- 2.4.3

On 13.10.2015 15:38, 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/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 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 132 insertions(+), 3 deletions(-)
You forgot to update src/admin_protocol-structs.
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 4539ac6..2358173 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -73,6 +73,9 @@ int virAdmConnectRegisterCloseCallback(virAdmConnectPtr conn, int virAdmConnectUnregisterCloseCallback(virAdmConnectPtr conn, virAdmConnectCloseFunc cb);
+int virAdmConnectGetLibVersion(virAdmConnectPtr conn, + unsigned long long *libVer); + # ifdef __cplusplus } # endif 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/libvirt-admin.c b/src/libvirt-admin.c index 9ad124e..2da3d43 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -604,3 +604,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 5b9ba51..273af4d 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -18,4 +18,5 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectIsAlive; virAdmConnectRegisterCloseCallback; virAdmConnectUnregisterCloseCallback; + virAdmConnectGetLibVersion; }; diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index b6d50f3..d5be6e8 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; @@ -125,7 +125,9 @@ sub push_privconn { # Read the input file (usually remote_protocol.x) and form an # opinion about the name, args and return type of each RPC. my ($name, $ProcName, $id, $flags, %calls, @calls, %opts); - +#print "procprefix $procprefix\n"; +#print "structprefix $structprefix\n"; +#print "protocol $protocol\n";
Looks like a leftover from some debugging?
my $collect_args_members = 0; my $collect_ret_members = 0; my $collect_opts = 0; @@ -1401,8 +1403,13 @@ elsif ($mode eq "client") { my $ret_name = $1;
if ($call->{ProcName} =~ m/Get(Lib)?Version/) { + 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);"); + }
Do you plan to indent these ^^ lines?
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 679ae99..0c807f2 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -143,6 +143,67 @@ vshAdmReconnect(vshControl *ctl) disconnected = 0; }
+/* + * "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 * --------------- @@ -480,6 +541,12 @@ static const vshCmdDef vshAdmCmds[] = { VSH_CMD_HELP, VSH_CMD_PWD, VSH_CMD_QUIT, + {.name = "version", + .handler = cmdVersion, + .opts = NULL, + .info = info_version, + .flags = 0 + }, {.name = "connect", .handler = cmdConnect, .opts = opts_connect,
Otherwise looking good. Michal

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 | 38 ++++++++++++ 6 files changed, 146 insertions(+), 30 deletions(-) diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 2358173..4b6b719 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -76,6 +76,8 @@ int virAdmConnectUnregisterCloseCallback(virAdmConnectPtr conn, int virAdmConnectGetLibVersion(virAdmConnectPtr conn, unsigned long long *libVer); +char * virAdmConnectGetURI(virAdmConnectPtr conn); + # ifdef __cplusplus } # endif diff --git a/src/datatypes.c b/src/datatypes.c index 9e374e4..1d2329c 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -839,6 +839,8 @@ virAdmConnectDispose(void *obj) if (conn->privateDataFreeFunc) conn->privateDataFreeFunc(conn); + virURIFree(conn->uri); + virObjectUnref(conn->closeCallback); } diff --git a/src/datatypes.h b/src/datatypes.h index 33df476..aa160a8 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -408,6 +408,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 2da3d43..252fdbe 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -29,6 +29,7 @@ #include "configmake.h" #include "viralloc.h" +#include "virconf.h" #include "virlog.h" #include "virnetclient.h" #include "virobject.h" @@ -283,60 +284,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: @@ -344,6 +339,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 @@ -358,6 +387,7 @@ virAdmConnectOpen(const char *name, unsigned int flags) { char *sock_path = NULL; virAdmConnectPtr conn = NULL; + virConfPtr conf = NULL; if (virAdmInitialize() < 0) goto error; @@ -368,7 +398,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))) @@ -635,3 +676,34 @@ int virAdmConnectGetLibVersion(virAdmConnectPtr conn, virDispatchError(NULL); return -1; } + +/** + * 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 273af4d..4467c88 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -19,4 +19,5 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectRegisterCloseCallback; virAdmConnectUnregisterCloseCallback; virAdmConnectGetLibVersion; + virAdmConnectGetURI; }; diff --git a/tools/virt-admin.c b/tools/virt-admin.c index 0c807f2..a202ce2 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -203,6 +203,38 @@ cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) return true; } +/* + * '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 @@ -547,6 +579,12 @@ static const vshCmdDef vshAdmCmds[] = { .info = info_version, .flags = 0 }, + {.name = "uri", + .handler = cmdURI, + .opts = NULL, + .info = info_uri, + .flags = 0 + }, {.name = "connect", .handler = cmdConnect, .opts = opts_connect, -- 2.4.3

On 13.10.2015 15:38, 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 | 38 ++++++++++++ 6 files changed, 146 insertions(+), 30 deletions(-)
ACK Michal

Since close callback and URI support was introduced in the earlier patches, it is now the right time to implement the disconnect handler itself and switch the dummy NULL reference in virAdmConnectRegisterCloseCallback for this new handler. The handler itself is the same as the one used in virsh, it only remembers that the event occurs, lets the user know, but no other useful logic is present. --- tools/virt-admin.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/tools/virt-admin.c b/tools/virt-admin.c index a202ce2..3069d48 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -77,6 +77,51 @@ static const vshClientHooks hooks; */ static int 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) { @@ -91,8 +136,8 @@ vshAdmConnect(vshControl *ctl, unsigned int flags) vshError(ctl, "%s", _("Failed to connect to the admin server")); return NULL; } else { - if (virAdmConnectRegisterCloseCallback(priv->conn, NULL, NULL, - NULL) < 0) + if (virAdmConnectRegisterCloseCallback(priv->conn, vshAdmCatchDisconnect, + NULL, NULL) < 0) vshError(ctl, "%s", _("Unable to register disconnect callback")); if (priv->wantReconnect) @@ -112,7 +157,7 @@ vshAdmDisconnectInternal(vshControl *ctl, virAdmConnectPtr *conn) if (!*conn) return ret; - virAdmConnectUnregisterCloseCallback(*conn, NULL); + virAdmConnectUnregisterCloseCallback(*conn, vshAdmCatchDisconnect); ret = virAdmConnectClose(*conn); if (ret < 0) vshError(ctl, "%s", _("Failed to disconnect from the admin server")); -- 2.4.3

On 13.10.2015 15:38, Erik Skultety wrote:
Since close callback and URI support was introduced in the earlier patches, it is now the right time to implement the disconnect handler itself and switch the dummy NULL reference in virAdmConnectRegisterCloseCallback for this new handler. The handler itself is the same as the one used in virsh, it only remembers that the event occurs, lets the user know, but no other useful logic is present. --- tools/virt-admin.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-)
diff --git a/tools/virt-admin.c b/tools/virt-admin.c index a202ce2..3069d48 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -77,6 +77,51 @@ static const vshClientHooks hooks; */ static int 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) { @@ -91,8 +136,8 @@ vshAdmConnect(vshControl *ctl, unsigned int flags) vshError(ctl, "%s", _("Failed to connect to the admin server")); return NULL; } else { - if (virAdmConnectRegisterCloseCallback(priv->conn, NULL, NULL, - NULL) < 0) + if (virAdmConnectRegisterCloseCallback(priv->conn, vshAdmCatchDisconnect, + NULL, NULL) < 0) vshError(ctl, "%s", _("Unable to register disconnect callback"));
if (priv->wantReconnect) @@ -112,7 +157,7 @@ vshAdmDisconnectInternal(vshControl *ctl, virAdmConnectPtr *conn) if (!*conn) return ret;
- virAdmConnectUnregisterCloseCallback(*conn, NULL); + virAdmConnectUnregisterCloseCallback(*conn, vshAdmCatchDisconnect); ret = virAdmConnectClose(*conn); if (ret < 0) vshError(ctl, "%s", _("Failed to disconnect from the admin server"));
For some reason, this is not working yet... Maybe you need to enable it somewhere? Michal

In order to be able to react to upcoming daemon events (disconnect, keepalive, ...), we need to register asynchronous polling so that our event loop can detect events on a file descriptor. --- src/libvirt-admin.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 252fdbe..3d74d5f 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -160,6 +160,12 @@ 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, -- 2.4.3

On 13.10.2015 15:38, Erik Skultety wrote:
In order to be able to react to upcoming daemon events (disconnect, keepalive, ...), we need to register asynchronous polling so that our event loop can detect events on a file descriptor. --- src/libvirt-admin.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 252fdbe..3d74d5f 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -160,6 +160,12 @@ 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,
Aha! This is the commit that enables the feature. I think you want to squash it into 4/9. ACK Michal

--- tools/Makefile.am | 8 +- tools/virt-admin.pod | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 304 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..348353d --- /dev/null +++ b/tools/virt-admin.pod @@ -0,0 +1,297 @@ +=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> <domain> [ARG]... + +Where I<command> is one of the commands listed below; I<domain> is the +numeric domain id, or the domain name, or the domain UUID; and I<ARGS> +are command specific options. There are a few exceptions to this rule +in the cases where the command in question acts on all domains, the +entire machine, or directly on the xen hypervisor. Those exceptions +will be clear for each of those commands. Note: it is permissible to +give numeric names to domains, however, doing so will result in a +domain that can only be identified by domain id. In other words, if a +numeric value is supplied it will be interpreted as a domain id, not +as a name. + +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<-e>, B<--escape> I<string> + +Set alternative escape sequence for I<console> command. By default, +telnet's B<^]> is used. Allowed characters when using hat notation are: +alphabetic character, @, [, ], \, ^, _. + +=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<-r>, B<--readonly> + +Make the initial connection read-only, as if by the I<--readonly> +option of the B<connect> command. + +=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 and which options and driver are compiled in. + +=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 what this 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>] [I<--readonly>] + +(Re)-Connect to a daemon's administrating server. When the shell is first started, this +is automatically run with the I<URI> parameter requested by the C<-c> +option on the command line. The I<URI> parameter specifies how to +connect to the hypervisor. The documentation page at +L<http://libvirt.org/uri.html> list the values supported, but the most +common are: + +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. +The I<--readonly> option allows for read-only connection + +=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) 2005, 2007-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 13.10.2015 15:38, Erik Skultety wrote:
--- tools/Makefile.am | 8 +- tools/virt-admin.pod | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 304 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..348353d --- /dev/null +++ b/tools/virt-admin.pod @@ -0,0 +1,297 @@ +=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> <domain> [ARG]...
s/domain// here and everywhere.
+ +Where I<command> is one of the commands listed below; I<domain> is the +numeric domain id, or the domain name, or the domain UUID; and I<ARGS> +are command specific options. There are a few exceptions to this rule +in the cases where the command in question acts on all domains, the +entire machine, or directly on the xen hypervisor. Those exceptions +will be clear for each of those commands. Note: it is permissible to +give numeric names to domains, however, doing so will result in a +domain that can only be identified by domain id. In other words, if a +numeric value is supplied it will be interpreted as a domain id, not +as a name.
This paragraph ^^ except for the first sentence can be dropped. We are not going to support domains here ..
+ +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<-e>, B<--escape> I<string> + +Set alternative escape sequence for I<console> command. By default, +telnet's B<^]> is used. Allowed characters when using hat notation are: +alphabetic character, @, [, ], \, ^, _. +
Not really. I went back to 2/9 and you've dropped --escape there (correctly). You need to drop it here too.
+=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<-r>, B<--readonly> + +Make the initial connection read-only, as if by the I<--readonly> +option of the B<connect> command. +
Again, we don't support --readonly.
+=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 and which options and driver are compiled in. + +=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 what this 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>] [I<--readonly>] + +(Re)-Connect to a daemon's administrating server. When the shell is first started, this +is automatically run with the I<URI> parameter requested by the C<-c> +option on the command line. The I<URI> parameter specifies how to +connect to the hypervisor. The documentation page at +L<http://libvirt.org/uri.html> list the values supported, but the most +common are: + +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. +The I<--readonly> option allows for read-only connection + +=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) 2005, 2007-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
You don't describe connect command. Otherwise looking good. Michal
participants (3)
-
Erik Skultety
-
Martin Kletzander
-
Michal Privoznik