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@xxxxxxxxxx> > + * Karel Zak <kzak@xxxxxxxxxx> > + * Daniel P. Berrange <berrange@xxxxxxxxxx> 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@xxxxxxxxxx> > + * Karel Zak <kzak@xxxxxxxxxx> > + * Daniel P. Berrange <berrange@xxxxxxxxxx> > + */ > + > +#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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list