[libvirt] [PATCH v3 0/3] virt-shell: v3 diff series

v3: - renamed virshCommandOptTimeoutToMs - resolved conflicts caused by virsh block job handling refactor - generic commands implementation moved to vsh.c As usual, for testing purposes, everything is available on my remote branch https://github.com/eskultety/libvirt/tree/virt-shell Erik Skultety (3): virt-shell: Resolve conflicts and some forgotten substitution from v2 virt-shell: Support command history for individual clients virt-shell: Move generic commands implementation to vsh.c src/libvirt_private.syms | 1 + src/util/virstring.c | 32 ++++ src/util/virstring.h | 1 + tools/virsh-domain.c | 150 +++++++++--------- tools/virsh-network.c | 2 +- tools/virsh.c | 390 ++++++++++++----------------------------------- tools/virsh.h | 1 - tools/vsh.c | 242 ++++++++++++++++++++++++++--- tools/vsh.h | 13 +- 9 files changed, 441 insertions(+), 391 deletions(-) -- 2.4.3

Recent refactor series (commits below) created a conflict with this virsh split series. This patch also renames virshCommandOptTimeoutToMs to vshCommandOptTimeoutToMs and moves it to vsh.c Commits causing conflict: 6da3b694 - faa14391 --- tools/virsh-domain.c | 150 ++++++++++++++++++++++++++------------------------ tools/virsh-network.c | 2 +- tools/virsh.c | 33 ----------- tools/virsh.h | 1 - tools/vsh.c | 35 ++++++++++++ tools/vsh.h | 1 + 6 files changed, 115 insertions(+), 107 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 1788ac8..283d475 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -1718,9 +1718,9 @@ static void virshCatchInt(int sig ATTRIBUTE_UNUSED, } -typedef struct _vshBlockJobWaitData vshBlockJobWaitData; -typedef vshBlockJobWaitData *vshBlockJobWaitDataPtr; -struct _vshBlockJobWaitData { +typedef struct _virshBlockJobWaitData virshBlockJobWaitData; +typedef virshBlockJobWaitData *virshBlockJobWaitDataPtr; +struct _virshBlockJobWaitData { vshControl *ctl; virDomainPtr dom; const char *dev; @@ -1737,14 +1737,14 @@ struct _vshBlockJobWaitData { static void -vshBlockJobStatusHandler(virConnectPtr conn ATTRIBUTE_UNUSED, - virDomainPtr dom ATTRIBUTE_UNUSED, - const char *disk, - int type ATTRIBUTE_UNUSED, - int status, - void *opaque) +virshBlockJobStatusHandler(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom ATTRIBUTE_UNUSED, + const char *disk, + int type ATTRIBUTE_UNUSED, + int status, + void *opaque) { - vshBlockJobWaitDataPtr data = opaque; + virshBlockJobWaitDataPtr data = opaque; if (STREQ_NULLABLE(disk, data->dev)) data->status = status; @@ -1752,7 +1752,7 @@ vshBlockJobStatusHandler(virConnectPtr conn ATTRIBUTE_UNUSED, /** - * vshBlockJobWaitInit: + * virshBlockJobWaitInit: * @ctl: vsh control structure * @dom: domain object * @dev: block device name to wait for @@ -1763,23 +1763,24 @@ vshBlockJobStatusHandler(virConnectPtr conn ATTRIBUTE_UNUSED, * * Prepares virsh for waiting for completion of a block job. This function * registers event handlers for block job events and prepares the data structures - * for them. A call to vshBlockJobWait then waits for completion of the given + * for them. A call to virshBlockJobWait then waits for completion of the given * block job. This function should be tolerant to different versions of daemon * and the reporting capabilities of those. * * Returns the data structure that holds data needed for block job waiting or * NULL in case of error. */ -static vshBlockJobWaitDataPtr -vshBlockJobWaitInit(vshControl *ctl, - virDomainPtr dom, - const char *dev, - const char *job_name, - bool verbose, - unsigned int timeout, - bool async_abort) +static virshBlockJobWaitDataPtr +virshBlockJobWaitInit(vshControl *ctl, + virDomainPtr dom, + const char *dev, + const char *job_name, + bool verbose, + unsigned int timeout, + bool async_abort) { - vshBlockJobWaitDataPtr ret; + virshBlockJobWaitDataPtr ret; + virshControlPtr priv = ctl->privData; if (VIR_ALLOC(ret) < 0) return NULL; @@ -1796,14 +1797,14 @@ vshBlockJobWaitInit(vshControl *ctl, ret->status = -1; virConnectDomainEventGenericCallback cb = - VIR_DOMAIN_EVENT_CALLBACK(vshBlockJobStatusHandler); + VIR_DOMAIN_EVENT_CALLBACK(virshBlockJobStatusHandler); - if ((ret->cb_id = virConnectDomainEventRegisterAny(ctl->conn, dom, + if ((ret->cb_id = virConnectDomainEventRegisterAny(priv->conn, dom, VIR_DOMAIN_EVENT_ID_BLOCK_JOB, cb, ret, NULL)) < 0) vshResetLibvirtError(); - if ((ret->cb_id2 = virConnectDomainEventRegisterAny(ctl->conn, dom, + if ((ret->cb_id2 = virConnectDomainEventRegisterAny(priv->conn, dom, VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2, cb, ret, NULL)) < 0) vshResetLibvirtError(); @@ -1813,23 +1814,26 @@ vshBlockJobWaitInit(vshControl *ctl, static void -vshBlockJobWaitFree(vshBlockJobWaitDataPtr data) +virshBlockJobWaitFree(virshBlockJobWaitDataPtr data) { + virshControlPtr priv = NULL; + if (!data) return; + priv = data->ctl->privData; if (data->cb_id >= 0) - virConnectDomainEventDeregisterAny(data->ctl->conn, data->cb_id); + virConnectDomainEventDeregisterAny(priv->conn, data->cb_id); if (data->cb_id2 >= 0) - virConnectDomainEventDeregisterAny(data->ctl->conn, data->cb_id2); + virConnectDomainEventDeregisterAny(priv->conn, data->cb_id2); VIR_FREE(data); } /** - * vshBlockJobWait: - * @data: private data initialized by vshBlockJobWaitInit + * virshBlockJobWait: + * @data: private data initialized by virshBlockJobWaitInit * * Waits for the block job to complete. This function prefers to get an event * from libvirt but still has fallback means if the device name can't be matched @@ -1841,7 +1845,7 @@ vshBlockJobWaitFree(vshBlockJobWaitDataPtr data) * VIR_DOMAIN_BLOCK_JOB_READY. */ static int -vshBlockJobWait(vshBlockJobWaitDataPtr data) +virshBlockJobWait(virshBlockJobWaitDataPtr data) { /* For two phase jobs like active commit or block copy, the marker reaches * 100% and an event fires. In case where virsh would not be able to match @@ -1871,7 +1875,7 @@ vshBlockJobWait(vshBlockJobWaitDataPtr data) sigaddset(&sigmask, SIGINT); intCaught = 0; - sig_action.sa_sigaction = vshCatchInt; + sig_action.sa_sigaction = virshCatchInt; sig_action.sa_flags = SA_SIGINFO; sigemptyset(&sig_action.sa_mask); sigaction(SIGINT, &sig_action, &old_sig_action); @@ -1913,7 +1917,8 @@ vshBlockJobWait(vshBlockJobWaitDataPtr data) } if (data->verbose) - vshPrintJobProgress(data->job_name, info.end - info.cur, info.end); + virshPrintJobProgress(data->job_name, info.end - info.cur, + info.end); if (data->timeout && virTimeMillisNow(&curr) < 0) { vshSaveLibvirtError(); @@ -1941,7 +1946,7 @@ vshBlockJobWait(vshBlockJobWaitDataPtr data) if (data->verbose && (ret == VIR_DOMAIN_BLOCK_JOB_COMPLETED || ret == VIR_DOMAIN_BLOCK_JOB_READY)) - vshPrintJobProgress(data->job_name, 0, 1); + virshPrintJobProgress(data->job_name, 0, 1); sigaction(SIGINT, &old_sig_action, NULL); return ret; @@ -2046,7 +2051,7 @@ cmdBlockCommit(vshControl *ctl, const vshCmd *cmd) int abort_flags = 0; unsigned int flags = 0; unsigned long bandwidth = 0; - vshBlockJobWaitDataPtr bjWait = NULL; + virshBlockJobWaitDataPtr bjWait = NULL; VSH_EXCLUSIVE_OPTIONS("pivot", "keep-overlay"); @@ -2097,12 +2102,12 @@ cmdBlockCommit(vshControl *ctl, const vshCmd *cmd) if (async) abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC; - if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) return false; if (blocking && - !(bjWait = vshBlockJobWaitInit(ctl, dom, path, _("Block commit"), - verbose, timeout, async))) + !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block commit"), + verbose, timeout, async))) goto cleanup; if (virDomainBlockCommit(dom, path, base, top, bandwidth, flags) < 0) @@ -2119,7 +2124,7 @@ cmdBlockCommit(vshControl *ctl, const vshCmd *cmd) } /* Execution continues here only if --wait or friends were specified */ - switch (vshBlockJobWait(bjWait)) { + switch (virshBlockJobWait(bjWait)) { case -1: goto cleanup; @@ -2164,7 +2169,7 @@ cmdBlockCommit(vshControl *ctl, const vshCmd *cmd) ret = true; cleanup: virDomainFree(dom); - vshBlockJobWaitFree(bjWait); + virshBlockJobWaitFree(bjWait); return ret; } @@ -2282,7 +2287,7 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd) const char *xml = NULL; char *xmlstr = NULL; virTypedParameterPtr params = NULL; - vshBlockJobWaitDataPtr bjWait = NULL; + virshBlockJobWaitDataPtr bjWait = NULL; int nparams = 0; if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0) @@ -2346,8 +2351,8 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd) goto cleanup; if (blocking && - !(bjWait = vshBlockJobWaitInit(ctl, dom, path, _("Block Copy"), verbose, - timeout, async))) + !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block Copy"), + verbose, timeout, async))) goto cleanup; if (xml) { @@ -2425,7 +2430,7 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd) } /* Execution continues here only if --wait or friends were specified */ - switch (vshBlockJobWait(bjWait)) { + switch (virshBlockJobWait(bjWait)) { case -1: goto cleanup; @@ -2469,7 +2474,7 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd) VIR_FREE(xmlstr); virTypedParamsFree(params, nparams); virDomainFree(dom); - vshBlockJobWaitFree(bjWait); + virshBlockJobWaitFree(bjWait); return ret; } @@ -2546,13 +2551,14 @@ virshDomainBlockJobToString(int type) static bool -vshBlockJobInfo(vshControl *ctl, - virDomainPtr dom, - const char *path, - bool raw, - bool bytes) +virshBlockJobInfo(vshControl *ctl, + virDomainPtr dom, + const char *path, + bool raw, + bool bytes) { virDomainBlockJobInfo info; + virshControlPtr priv = ctl->privData; unsigned long long speed; unsigned int flags = 0; bool ret = false; @@ -2630,10 +2636,10 @@ vshBlockJobInfo(vshControl *ctl, static bool -vshBlockJobSetSpeed(vshControl *ctl, - const vshCmd *cmd, - virDomainPtr dom, - const char *path) +virshBlockJobSetSpeed(vshControl *ctl, + const vshCmd *cmd, + virDomainPtr dom, + const char *path) { unsigned long bandwidth; @@ -2648,10 +2654,10 @@ vshBlockJobSetSpeed(vshControl *ctl, static bool -vshBlockJobAbort(virDomainPtr dom, - const char *path, - bool pivot, - bool async) +virshBlockJobAbort(virDomainPtr dom, + const char *path, + bool pivot, + bool async) { unsigned int flags = 0; @@ -2697,7 +2703,7 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd) /* XXX also support --bytes with bandwidth mode */ VSH_EXCLUSIVE_OPTIONS_VAR(bytes, bandwidth); - if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) goto cleanup; /* XXX Allow path to be optional to list info on all devices at once */ @@ -2705,11 +2711,11 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd) goto cleanup; if (bandwidth) - ret = vshBlockJobSetSpeed(ctl, cmd, dom, path); + ret = virshBlockJobSetSpeed(ctl, cmd, dom, path); else if (abortMode || pivot || async) - ret = vshBlockJobAbort(dom, path, pivot, async); + ret = virshBlockJobAbort(dom, path, pivot, async); else - ret = vshBlockJobInfo(ctl, dom, path, raw, bytes); + ret = virshBlockJobInfo(ctl, dom, path, raw, bytes); cleanup: if (dom) @@ -2785,7 +2791,7 @@ cmdBlockPull(vshControl *ctl, const vshCmd *cmd) const char *base = NULL; unsigned long bandwidth = 0; unsigned int flags = 0; - vshBlockJobWaitDataPtr bjWait = NULL; + virshBlockJobWaitDataPtr bjWait = NULL; VSH_REQUIRE_OPTION("verbose", "wait"); VSH_REQUIRE_OPTION("async", "wait"); @@ -2805,12 +2811,12 @@ cmdBlockPull(vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool(cmd, "keep-relative")) flags |= VIR_DOMAIN_BLOCK_REBASE_RELATIVE; - if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) return false; if (blocking && - !(bjWait = vshBlockJobWaitInit(ctl, dom, path, _("Block Pull"), verbose, - timeout, async))) + !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block Pull"), + verbose, timeout, async))) goto cleanup; if (base || flags) { @@ -2828,7 +2834,7 @@ cmdBlockPull(vshControl *ctl, const vshCmd *cmd) } /* Execution continues here only if --wait or friends were specified */ - switch (vshBlockJobWait(bjWait)) { + switch (virshBlockJobWait(bjWait)) { case -1: goto cleanup; @@ -2852,7 +2858,7 @@ cmdBlockPull(vshControl *ctl, const vshCmd *cmd) cleanup: virDomainFree(dom); - vshBlockJobWaitFree(bjWait); + virshBlockJobWaitFree(bjWait); return ret; } @@ -5383,7 +5389,7 @@ static const vshCmdOptDef opts_screenshot[] = { * Generate string: '<domain name>-<timestamp>[<extension>]' */ static char * -vshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime) +virshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime) { char timestr[100]; time_t cur_time; @@ -5450,7 +5456,7 @@ cmdScreenshot(vshControl *ctl, const vshCmd *cmd) } if (!file) { - if (!(file = vshGenFileName(ctl, dom, mime))) + if (!(file = virshGenFileName(ctl, dom, mime))) goto cleanup; generated = true; } @@ -9188,7 +9194,7 @@ cmdQemuMonitorEvent(vshControl *ctl, const vshCmd *cmd) data.loop = vshCommandOptBool(cmd, "loop"); data.pretty = vshCommandOptBool(cmd, "pretty"); data.count = 0; - if (virshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) return false; if (vshCommandOptString(ctl, cmd, "event", &event) < 0) return false; @@ -10174,7 +10180,7 @@ cmdMigrate(vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool(cmd, "live")) live_flag = true; - if (virshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) { + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) { goto cleanup; } else if (timeout > 0 && !live_flag) { vshError(ctl, "%s", @@ -12453,7 +12459,7 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) data[0].cb = &vshEventCallbacks[event]; data[0].id = -1; } - if (virshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) goto cleanup; if (vshCommandOptBool(cmd, "domain")) diff --git a/tools/virsh-network.c b/tools/virsh-network.c index 00a50ef..a0f7707 100644 --- a/tools/virsh-network.c +++ b/tools/virsh-network.c @@ -1276,7 +1276,7 @@ cmdNetworkEvent(vshControl *ctl, const vshCmd *cmd) data.ctl = ctl; data.loop = vshCommandOptBool(cmd, "loop"); data.count = 0; - if (virshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) return false; if (vshCommandOptBool(cmd, "network")) diff --git a/tools/virsh.c b/tools/virsh.c index 97294c5..9f9e1d3 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -536,39 +536,6 @@ cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) * --------------- */ -/* - * virshCommandOptTimeoutToMs: - * @ctl virsh control structure - * @cmd command reference - * @timeout result - * - * Parse an optional --timeout parameter in seconds, but store the - * value of the timeout in milliseconds. - * See vshCommandOptInt() - */ -int -virshCommandOptTimeoutToMs(vshControl *ctl, const vshCmd *cmd, int *timeout) -{ - int ret; - unsigned int utimeout; - - if ((ret = vshCommandOptUInt(ctl, cmd, "timeout", &utimeout)) <= 0) - return ret; - - /* Ensure that the timeout is not zero and that we can convert - * it from seconds to milliseconds without overflowing. */ - if (utimeout == 0 || utimeout > INT_MAX / 1000) { - vshError(ctl, - _("Numeric value '%u' for <%s> option is malformed or out of range"), - utimeout, - "timeout"); - ret = -1; - } else { - *timeout = ((int) utimeout) * 1000; - } - - return ret; -} static bool virshConnectionUsability(vshControl *ctl, virConnectPtr conn) diff --git a/tools/virsh.h b/tools/virsh.h index 6c4159a..3402408 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -102,7 +102,6 @@ typedef enum { } virshLookupByFlags; virConnectPtr virshConnect(vshControl *ctl, const char *uri, bool readonly); -int virshCommandOptTimeoutToMs(vshControl *ctl, const vshCmd *cmd, int *timeout); int virshDomainState(vshControl *ctl, virDomainPtr dom, int *reason); int virshStreamSink(virStreamPtr st, const char *bytes, size_t nbytes, diff --git a/tools/vsh.c b/tools/vsh.c index f4c342b..62f57ca 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -1574,6 +1574,41 @@ vshCommandStringParse(vshControl *ctl, char *cmdstr) return vshCommandParse(ctl, &parser); } +/** + * virshCommandOptTimeoutToMs: + * @ctl virsh control structure + * @cmd command reference + * @timeout result + * + * Parse an optional --timeout parameter in seconds, but store the + * value of the timeout in milliseconds. + * See vshCommandOptInt() + */ +int +vshCommandOptTimeoutToMs(vshControl *ctl, const vshCmd *cmd, int *timeout) +{ + int ret; + unsigned int utimeout; + + if ((ret = vshCommandOptUInt(ctl, cmd, "timeout", &utimeout)) <= 0) + return ret; + + /* Ensure that the timeout is not zero and that we can convert + * it from seconds to milliseconds without overflowing. */ + if (utimeout == 0 || utimeout > INT_MAX / 1000) { + vshError(ctl, + _("Numeric value '%u' for <%s> option is malformed or out of range"), + utimeout, + "timeout"); + ret = -1; + } else { + *timeout = ((int) utimeout) * 1000; + } + + return ret; +} + + /* --------------- * Misc utils * --------------- diff --git a/tools/vsh.h b/tools/vsh.h index 66c9c3b..ecf52e9 100644 --- a/tools/vsh.h +++ b/tools/vsh.h @@ -295,6 +295,7 @@ bool vshCommandStringParse(vshControl *ctl, char *cmdstr); const vshCmdOpt *vshCommandOptArgv(vshControl *ctl, const vshCmd *cmd, const vshCmdOpt *opt); bool vshCommandArgvParse(vshControl *ctl, int nargs, char **argv); +int vshCommandOptTimeoutToMs(vshControl *ctl, const vshCmd *cmd, int *timeout); void vshPrintExtra(vshControl *ctl, const char *format, ...) ATTRIBUTE_FMT_PRINTF(2, 3); -- 2.4.3

By splitting generic parts from virsh we will need to preserve each client's history, yet we use hardcoded names and paths. This patch fixes this problem. --- src/libvirt_private.syms | 1 + src/util/virstring.c | 32 +++++++++++++++++++++++++++++ src/util/virstring.h | 1 + tools/virsh.c | 14 ++++++------- tools/vsh.c | 52 +++++++++++++++++++++++++++++------------------- tools/vsh.h | 5 ++++- 6 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 45f42f5..e2140e4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2187,6 +2187,7 @@ virStringSplit; virStringSplitCount; virStringStripControlChars; virStringStripIPv6Brackets; +virStringToUpper; virStrncpy; virStrndup; virStrToDouble; diff --git a/src/util/virstring.c b/src/util/virstring.c index 5794f96..4f0afe9 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -1020,3 +1020,35 @@ virStringStripControlChars(char *str) } str[j] = '\0'; } + +/** + * virStringToUpper: + * @str: string to capitalize + * @dst: where to store the new capitalized string + * + * Capitalize the string with replacement of all '-' characters for '_' + * characters. Caller frees the result. + * + * Returns 0 if src is NULL, 1 if capitalization was successfull, -1 on failure. + */ +int +virStringToUpper(char **dst, const char *src) +{ + char *cap = NULL; + size_t i; + + if (!src) + return 0; + + if (VIR_ALLOC_N(cap, strlen(src) + 1) < 0) + return -1; + + for (i = 0; src[i]; i++) { + cap[i] = c_toupper(src[i]); + if (cap[i] == '-') + cap[i] = '_'; + } + + *dst = cap; + return 1; +} diff --git a/src/util/virstring.h b/src/util/virstring.h index e6dcb32..9848fb6 100644 --- a/src/util/virstring.h +++ b/src/util/virstring.h @@ -258,6 +258,7 @@ size_t virStringListLength(char **strings); int virStringSortCompare(const void *a, const void *b); int virStringSortRevCompare(const void *a, const void *b); +int virStringToUpper(char **dst, const char *src); ssize_t virStringSearch(const char *str, const char *regexp, diff --git a/tools/virsh.c b/tools/virsh.c index 9f9e1d3..2d198cd 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -33,7 +33,6 @@ #include <errno.h> #include <getopt.h> #include <sys/time.h> -#include "c-ctype.h" #include <fcntl.h> #include <locale.h> #include <time.h> @@ -169,7 +168,7 @@ virshReconnect(vshControl *ctl) "disconnect from the hypervisor")); } - priv->conn = virshConnect(ctl, ctl->name, priv->readonly); + priv->conn = virshConnect(ctl, ctl->connname, priv->readonly); if (!priv->conn) { if (disconnected) @@ -629,7 +628,7 @@ virshInit(vshControl *ctl) NULL)) < 0) return false; - if (ctl->name) { + if (ctl->connname) { virshReconnect(ctl); /* Connecting to a named connection must succeed, but we delay * connecting to the default connection until we need it @@ -661,7 +660,7 @@ virshDeinit(vshControl *ctl) virshControlPtr priv = ctl->privData; vshDeinit(ctl); - VIR_FREE(ctl->name); + VIR_FREE(ctl->connname); if (priv->conn) { int ret; virConnectUnregisterCloseCallback(priv->conn, virshCatchDisconnect); @@ -937,8 +936,8 @@ virshParseArgv(vshControl *ctl, int argc, char **argv) while ((arg = getopt_long(argc, argv, "+:c:d:e:hk:K:l:qrtvV", opt, &longindex)) != -1) { switch (arg) { case 'c': - VIR_FREE(ctl->name); - ctl->name = vshStrdup(ctl, optarg); + VIR_FREE(ctl->connname); + ctl->connname = vshStrdup(ctl, optarg); break; case 'd': if (virStrToLong_i(optarg, NULL, 10, &debug) < 0) { @@ -1137,6 +1136,7 @@ main(int argc, char **argv) memset(ctl, 0, sizeof(vshControl)); memset(&virshCtl, 0, sizeof(virshControl)); + ctl->name = "virsh"; /* hardcoded name of the binary */ ctl->imode = true; /* default is interactive mode */ ctl->log_fd = -1; /* Initialize log file descriptor */ ctl->debug = VSH_DEBUG_DEFAULT; @@ -1193,7 +1193,7 @@ main(int argc, char **argv) virFileActivateDirOverride(argv[0]); if ((defaultConn = virGetEnvBlockSUID("VIRSH_DEFAULT_CONNECT_URI"))) - ctl->name = vshStrdup(ctl, defaultConn); + ctl->connname = vshStrdup(ctl, defaultConn); if (vshInit(ctl, cmdGroups, NULL) < 0) exit(EXIT_FAILURE); diff --git a/tools/vsh.c b/tools/vsh.c index 62f57ca..043d0fb 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -2543,36 +2543,46 @@ vshReadlineCompletion(const char *text, int start, return matches; } -# define VIRTSHELL_HISTSIZE_MAX 500000 +# define HISTSIZE_MAX 500000 static int vshReadlineInit(vshControl *ctl) { char *userdir = NULL; + char *name_capitalized = NULL; int max_history = 500; - const char *histsize_str; + int ret = -1; + const char *histsize_str = NULL; + const char *histsize_env = NULL; + const char *strings[] = { + name_capitalized, + "HISTSIZE", + NULL + }; /* Allow conditional parsing of the ~/.inputrc file. * Work around ancient readline 4.1 (hello Mac OS X), * which declared it as 'char *' instead of 'const char *'. */ - rl_readline_name = (char *) "virtshell"; + rl_readline_name = ctl->name; /* Tell the completer that we want a crack first. */ rl_attempted_completion_function = vshReadlineCompletion; + if (virStringToUpper(&name_capitalized, ctl->name) < 0 || + !(histsize_env = virStringJoin(strings, "_"))) + goto cleanup; + /* Limit the total size of the history buffer */ - if ((histsize_str = virGetEnvBlockSUID("VIRTSHELL_HISTSIZE"))) { + if ((histsize_str = virGetEnvBlockSUID(histsize_env))) { if (virStrToLong_i(histsize_str, NULL, 10, &max_history) < 0) { - vshError(ctl, "%s", _("Bad $VIRTSHELL_HISTSIZE value.")); - VIR_FREE(userdir); - return -1; - } else if (max_history > VIRTSHELL_HISTSIZE_MAX || max_history < 0) { - vshError(ctl, _("$VIRTSHELL_HISTSIZE value should be between 0 " + vshError(ctl, _("Bad $%s value."), histsize_env); + goto cleanup; + } else if (max_history > HISTSIZE_MAX || max_history < 0) { + vshError(ctl, _("$%s value should be between 0 " "and %d"), - VIRTSHELL_HISTSIZE_MAX); - VIR_FREE(userdir); - return -1; + histsize_env, HISTSIZE_MAX); + goto cleanup; } } stifle_history(max_history); @@ -2584,26 +2594,26 @@ vshReadlineInit(vshControl *ctl) if (userdir == NULL) { vshError(ctl, "%s", _("Could not determine home directory")); - return -1; + goto cleanup; } - if (virAsprintf(&ctl->historydir, "%s/virtshell", userdir) < 0) { + if (virAsprintf(&ctl->historydir, "%s/%s", userdir, ctl->name) < 0) { vshError(ctl, "%s", _("Out of memory")); - VIR_FREE(userdir); - return -1; + goto cleanup; } if (virAsprintf(&ctl->historyfile, "%s/history", ctl->historydir) < 0) { vshError(ctl, "%s", _("Out of memory")); - VIR_FREE(userdir); - return -1; + goto cleanup; } - VIR_FREE(userdir); - read_history(ctl->historyfile); + ret = 0; - return 0; + cleanup: + VIR_FREE(userdir); + VIR_FREE(name_capitalized); + return ret; } static void diff --git a/tools/vsh.h b/tools/vsh.h index ecf52e9..19862d0 100644 --- a/tools/vsh.h +++ b/tools/vsh.h @@ -194,7 +194,10 @@ struct _vshCmd { * vshControl */ struct _vshControl { - char *name; /* connection name */ + const char *name; /* hardcoded name of the binary that cannot + * be changed without recompilation compared + * to program name */ + char *connname; /* connection name */ char *progname; /* program name */ vshCmd *cmd; /* the current command */ char *cmdstr; /* string with command */ -- 2.4.3

Generic commands like 'help', 'cd', 'pwd',etc. can be reused by any client, so the clients should profit from this implementation rather than providing their own similar implementation (if it's not intensional and there's a reason for this) --- tools/virsh.c | 343 +++++++++++++++------------------------------------------- tools/vsh.c | 155 ++++++++++++++++++++++++++ tools/vsh.h | 7 ++ 3 files changed, 250 insertions(+), 255 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 2d198cd..e123ea2 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -188,79 +188,6 @@ virshReconnect(vshControl *ctl) priv->blockJobNoBytes = false; } - -/* - * "connect" command - */ -static const vshCmdInfo info_connect[] = { - {.name = "help", - .data = N_("(re)connect to hypervisor") - }, - {.name = "desc", - .data = N_("Connect to local hypervisor. This is built-in " - "command after shell start up.") - }, - {.name = NULL} -}; - -static const vshCmdOptDef opts_connect[] = { - {.name = "name", - .type = VSH_OT_STRING, - .flags = VSH_OFLAG_EMPTY_OK, - .help = N_("hypervisor connection URI") - }, - {.name = "readonly", - .type = VSH_OT_BOOL, - .help = N_("read-only connection") - }, - {.name = NULL} -}; - -static bool -cmdConnect(vshControl *ctl, const vshCmd *cmd) -{ - bool ro = vshCommandOptBool(cmd, "readonly"); - const char *name = NULL; - virshControlPtr priv = ctl->privData; - - if (priv->conn) { - int ret; - - virConnectUnregisterCloseCallback(priv->conn, virshCatchDisconnect); - ret = virConnectClose(priv->conn); - if (ret < 0) - vshError(ctl, "%s", _("Failed to disconnect from the hypervisor")); - else if (ret > 0) - vshError(ctl, "%s", _("One or more references were leaked after " - "disconnect from the hypervisor")); - priv->conn = NULL; - } - - VIR_FREE(ctl->name); - if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) - return false; - - ctl->name = vshStrdup(ctl, name); - - priv->useGetInfo = false; - priv->useSnapshotOld = false; - priv->blockJobNoBytes = false; - priv->readonly = ro; - - priv->conn = virshConnect(ctl, ctl->name, priv->readonly); - - if (!priv->conn) { - vshError(ctl, "%s", _("Failed to connect to the hypervisor")); - return false; - } - - if (virConnectRegisterCloseCallback(priv->conn, virshCatchDisconnect, - NULL, NULL) < 0) - vshError(ctl, "%s", _("Unable to register disconnect callback")); - - return true; -} - int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED, const char *bytes, size_t nbytes, void *opaque) { @@ -270,13 +197,9 @@ int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED, } /* --------------- - * Commands + * Command Info * --------------- */ - -/* - * "help" command - */ static const vshCmdInfo info_help[] = { {.name = "help", .data = N_("print help") @@ -288,55 +211,6 @@ static const vshCmdInfo info_help[] = { {.name = NULL} }; -static const vshCmdOptDef opts_help[] = { - {.name = "command", - .type = VSH_OT_STRING, - .help = N_("Prints global help, command specific help, or help for a group of related commands") - }, - {.name = NULL} -}; - -static bool -cmdHelp(vshControl *ctl, const vshCmd *cmd) - { - const char *name = NULL; - - if (vshCommandOptString(ctl, cmd, "command", &name) <= 0) { - const vshCmdGrp *grp; - const vshCmdDef *def; - - vshPrint(ctl, "%s", _("Grouped commands:\n\n")); - - for (grp = cmdGroups; grp->name; grp++) { - vshPrint(ctl, _(" %s (help keyword '%s'):\n"), grp->name, - grp->keyword); - - for (def = grp->commands; def->name; def++) { - if (def->flags & VSH_CMD_FLAG_ALIAS) - continue; - vshPrint(ctl, " %-30s %s\n", def->name, - _(vshCmddefGetInfo(def, "help"))); - } - - vshPrint(ctl, "\n"); - } - - return true; - } - - if (vshCmddefSearch(name)) { - return vshCmddefHelp(ctl, name); - } else if (vshCmdGrpSearch(name)) { - return vshCmdGrpHelp(ctl, name); - } else { - vshError(ctl, _("command or command group '%s' doesn't exist"), name); - return false; - } -} - -/* - * "cd" command - */ static const vshCmdInfo info_cd[] = { {.name = "help", .data = N_("change the current directory") @@ -347,45 +221,6 @@ static const vshCmdInfo info_cd[] = { {.name = NULL} }; -static const vshCmdOptDef opts_cd[] = { - {.name = "dir", - .type = VSH_OT_STRING, - .help = N_("directory to switch to (default: home or else root)") - }, - {.name = NULL} -}; - -static bool -cmdCd(vshControl *ctl, const vshCmd *cmd) -{ - const char *dir = NULL; - char *dir_malloced = NULL; - bool ret = true; - char ebuf[1024]; - - if (!ctl->imode) { - vshError(ctl, "%s", _("cd: command valid only in interactive mode")); - return false; - } - - if (vshCommandOptString(ctl, cmd, "dir", &dir) <= 0) - dir = dir_malloced = virGetUserDirectory(); - if (!dir) - dir = "/"; - - if (chdir(dir) == -1) { - vshError(ctl, _("cd: %s: %s"), - virStrerror(errno, ebuf, sizeof(ebuf)), dir); - ret = false; - } - - VIR_FREE(dir_malloced); - return ret; -} - -/* - * "pwd" command - */ static const vshCmdInfo info_pwd[] = { {.name = "help", .data = N_("print the current directory") @@ -396,29 +231,6 @@ static const vshCmdInfo info_pwd[] = { {.name = NULL} }; -static bool -cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) -{ - char *cwd; - bool ret = true; - char ebuf[1024]; - - cwd = getcwd(NULL, 0); - if (!cwd) { - vshError(ctl, _("pwd: cannot get current directory: %s"), - virStrerror(errno, ebuf, sizeof(ebuf))); - ret = false; - } else { - vshPrint(ctl, _("%s\n"), cwd); - VIR_FREE(cwd); - } - - return ret; -} - -/* - * "echo" command - */ static const vshCmdInfo info_echo[] = { {.name = "help", .data = N_("echo arguments") @@ -429,6 +241,47 @@ static const vshCmdInfo info_echo[] = { {.name = NULL} }; +static const vshCmdInfo info_quit[] = { + {.name = "help", + .data = N_("quit this interactive terminal") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdInfo info_connect[] = { + {.name = "help", + .data = N_("(re)connect to hypervisor") + }, + {.name = "desc", + .data = N_("Connect to local hypervisor. This is built-in " + "command after shell start up.") + }, + {.name = NULL} +}; + +/* --------------- + * Command Opts + * --------------- + */ +static const vshCmdOptDef opts_help[] = { + {.name = "command", + .type = VSH_OT_STRING, + .help = N_("Prints global help, command specific help, or help for a group of related commands") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_cd[] = { + {.name = "dir", + .type = VSH_OT_STRING, + .help = N_("directory to switch to (default: home or else root)") + }, + {.name = NULL} +}; + static const vshCmdOptDef opts_echo[] = { {.name = "shell", .type = VSH_OT_BOOL, @@ -453,80 +306,61 @@ static const vshCmdOptDef opts_echo[] = { {.name = NULL} }; -/* Exists mainly for debugging virsh, but also handy for adding back - * quotes for later evaluation. - */ +static const vshCmdOptDef opts_connect[] = { + {.name = "name", + .type = VSH_OT_STRING, + .flags = VSH_OFLAG_EMPTY_OK, + .help = N_("hypervisor connection URI") + }, + {.name = "readonly", + .type = VSH_OT_BOOL, + .help = N_("read-only connection") + }, + {.name = NULL} +}; + static bool -cmdEcho(vshControl *ctl, const vshCmd *cmd) +cmdConnect(vshControl *ctl, const vshCmd *cmd) { - bool shell = false; - bool xml = false; - int count = 0; - const vshCmdOpt *opt = NULL; - char *arg; - virBuffer buf = VIR_BUFFER_INITIALIZER; - - if (vshCommandOptBool(cmd, "shell")) - shell = true; - if (vshCommandOptBool(cmd, "xml")) - xml = true; - - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - char *str; - virBuffer xmlbuf = VIR_BUFFER_INITIALIZER; - - arg = opt->data; - - if (count) - virBufferAddChar(&buf, ' '); - - if (xml) { - virBufferEscapeString(&xmlbuf, "%s", arg); - if (virBufferError(&xmlbuf)) { - vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); - return false; - } - str = virBufferContentAndReset(&xmlbuf); - } else { - str = vshStrdup(ctl, arg); - } + bool ro = vshCommandOptBool(cmd, "readonly"); + const char *name = NULL; + virshControlPtr priv = ctl->privData; - if (shell) - virBufferEscapeShell(&buf, str); - else - virBufferAdd(&buf, str, -1); - count++; - VIR_FREE(str); + if (priv->conn) { + int ret; + + virConnectUnregisterCloseCallback(priv->conn, virshCatchDisconnect); + ret = virConnectClose(priv->conn); + if (ret < 0) + vshError(ctl, "%s", _("Failed to disconnect from the hypervisor")); + else if (ret > 0) + vshError(ctl, "%s", _("One or more references were leaked after " + "disconnect from the hypervisor")); + priv->conn = NULL; } - if (virBufferError(&buf)) { - vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + VIR_FREE(ctl->connname); + if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) + return false; + + ctl->connname = vshStrdup(ctl, name); + + priv->useGetInfo = false; + priv->useSnapshotOld = false; + priv->blockJobNoBytes = false; + priv->readonly = ro; + + priv->conn = virshConnect(ctl, ctl->connname, priv->readonly); + + if (!priv->conn) { + vshError(ctl, "%s", _("Failed to connect to the hypervisor")); return false; } - arg = virBufferContentAndReset(&buf); - if (arg) - vshPrint(ctl, "%s", arg); - VIR_FREE(arg); - return true; -} -/* - * "quit" command - */ -static const vshCmdInfo info_quit[] = { - {.name = "help", - .data = N_("quit this interactive terminal") - }, - {.name = "desc", - .data = "" - }, - {.name = NULL} -}; + if (virConnectRegisterCloseCallback(priv->conn, virshCatchDisconnect, + NULL, NULL) < 0) + vshError(ctl, "%s", _("Unable to register disconnect callback")); -static bool -cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) -{ - ctl->imode = false; return true; } @@ -535,7 +369,6 @@ cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) * --------------- */ - static bool virshConnectionUsability(vshControl *ctl, virConnectPtr conn) { diff --git a/tools/vsh.c b/tools/vsh.c index 043d0fb..084cac5 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -2748,3 +2748,158 @@ vshDeinit(vshControl *ctl) vshReadlineDeinit(ctl); vshCloseLogFile(ctl); } + +/* ----------------------------------------------- + * Generic commands available to use by any client + * ----------------------------------------------- + */ + +bool +cmdHelp(vshControl *ctl, const vshCmd *cmd) + { + const char *name = NULL; + + if (vshCommandOptString(ctl, cmd, "command", &name) <= 0) { + const vshCmdGrp *grp; + const vshCmdDef *def; + + vshPrint(ctl, "%s", _("Grouped commands:\n\n")); + + for (grp = cmdGroups; grp->name; grp++) { + vshPrint(ctl, _(" %s (help keyword '%s'):\n"), grp->name, + grp->keyword); + + for (def = grp->commands; def->name; def++) { + if (def->flags & VSH_CMD_FLAG_ALIAS) + continue; + vshPrint(ctl, " %-30s %s\n", def->name, + _(vshCmddefGetInfo(def, "help"))); + } + + vshPrint(ctl, "\n"); + } + + return true; + } + + if (vshCmddefSearch(name)) { + return vshCmddefHelp(ctl, name); + } else if (vshCmdGrpSearch(name)) { + return vshCmdGrpHelp(ctl, name); + } else { + vshError(ctl, _("command or command group '%s' doesn't exist"), name); + return false; + } +} + +bool +cmdCd(vshControl *ctl, const vshCmd *cmd) +{ + const char *dir = NULL; + char *dir_malloced = NULL; + bool ret = true; + char ebuf[1024]; + + if (!ctl->imode) { + vshError(ctl, "%s", _("cd: command valid only in interactive mode")); + return false; + } + + if (vshCommandOptString(ctl, cmd, "dir", &dir) <= 0) + dir = dir_malloced = virGetUserDirectory(); + if (!dir) + dir = "/"; + + if (chdir(dir) == -1) { + vshError(ctl, _("cd: %s: %s"), + virStrerror(errno, ebuf, sizeof(ebuf)), dir); + ret = false; + } + + VIR_FREE(dir_malloced); + return ret; +} + +/* Exists mainly for debugging virsh, but also handy for adding back + * quotes for later evaluation. + */ +bool +cmdEcho(vshControl *ctl, const vshCmd *cmd) +{ + bool shell = false; + bool xml = false; + int count = 0; + const vshCmdOpt *opt = NULL; + char *arg; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (vshCommandOptBool(cmd, "shell")) + shell = true; + if (vshCommandOptBool(cmd, "xml")) + xml = true; + + while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { + char *str; + virBuffer xmlbuf = VIR_BUFFER_INITIALIZER; + + arg = opt->data; + + if (count) + virBufferAddChar(&buf, ' '); + + if (xml) { + virBufferEscapeString(&xmlbuf, "%s", arg); + if (virBufferError(&xmlbuf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + return false; + } + str = virBufferContentAndReset(&xmlbuf); + } else { + str = vshStrdup(ctl, arg); + } + + if (shell) + virBufferEscapeShell(&buf, str); + else + virBufferAdd(&buf, str, -1); + count++; + VIR_FREE(str); + } + + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + return false; + } + arg = virBufferContentAndReset(&buf); + if (arg) + vshPrint(ctl, "%s", arg); + VIR_FREE(arg); + return true; +} + +bool +cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + char *cwd; + bool ret = true; + char ebuf[1024]; + + cwd = getcwd(NULL, 0); + if (!cwd) { + vshError(ctl, _("pwd: cannot get current directory: %s"), + virStrerror(errno, ebuf, sizeof(ebuf))); + ret = false; + } else { + vshPrint(ctl, _("%s\n"), cwd); + VIR_FREE(cwd); + } + + return ret; +} + +bool +cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + ctl->imode = false; + return true; +} diff --git a/tools/vsh.h b/tools/vsh.h index 19862d0..cbc3455 100644 --- a/tools/vsh.h +++ b/tools/vsh.h @@ -363,6 +363,13 @@ int vshEventStart(vshControl *ctl, int timeout_ms); void vshEventTimeout(int timer, void *opaque); int vshEventWait(vshControl *ctl); +/* generic commands */ +bool cmdHelp(vshControl *ctl, const vshCmd *cmd); +bool cmdCd(vshControl *ctl, const vshCmd *cmd); +bool cmdEcho(vshControl *ctl, const vshCmd *cmd); +bool cmdPwd(vshControl *ctl, const vshCmd *cmd); +bool cmdQuit(vshControl *ctl, const vshCmd *cmd); + /* readline */ char * vshReadline(vshControl *ctl, const char *prompt); -- 2.4.3

I talked to Martin and Michal about this and I agree with them that moving the command structures as well as the commands is better idea than leaving the structures in virsh.c so I'll send a v4 inline patch. Erik On 13/08/15 13:39, Erik Skultety wrote:
Generic commands like 'help', 'cd', 'pwd',etc. can be reused by any client, so the clients should profit from this implementation rather than providing their own similar implementation (if it's not intensional and there's a reason for this) --- tools/virsh.c | 343 +++++++++++++++------------------------------------------- tools/vsh.c | 155 ++++++++++++++++++++++++++ tools/vsh.h | 7 ++ 3 files changed, 250 insertions(+), 255 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index 2d198cd..e123ea2 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -188,79 +188,6 @@ virshReconnect(vshControl *ctl) priv->blockJobNoBytes = false; }
- -/* - * "connect" command - */ -static const vshCmdInfo info_connect[] = { - {.name = "help", - .data = N_("(re)connect to hypervisor") - }, - {.name = "desc", - .data = N_("Connect to local hypervisor. This is built-in " - "command after shell start up.") - }, - {.name = NULL} -}; - -static const vshCmdOptDef opts_connect[] = { - {.name = "name", - .type = VSH_OT_STRING, - .flags = VSH_OFLAG_EMPTY_OK, - .help = N_("hypervisor connection URI") - }, - {.name = "readonly", - .type = VSH_OT_BOOL, - .help = N_("read-only connection") - }, - {.name = NULL} -}; - -static bool -cmdConnect(vshControl *ctl, const vshCmd *cmd) -{ - bool ro = vshCommandOptBool(cmd, "readonly"); - const char *name = NULL; - virshControlPtr priv = ctl->privData; - - if (priv->conn) { - int ret; - - virConnectUnregisterCloseCallback(priv->conn, virshCatchDisconnect); - ret = virConnectClose(priv->conn); - if (ret < 0) - vshError(ctl, "%s", _("Failed to disconnect from the hypervisor")); - else if (ret > 0) - vshError(ctl, "%s", _("One or more references were leaked after " - "disconnect from the hypervisor")); - priv->conn = NULL; - } - - VIR_FREE(ctl->name); - if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) - return false; - - ctl->name = vshStrdup(ctl, name); - - priv->useGetInfo = false; - priv->useSnapshotOld = false; - priv->blockJobNoBytes = false; - priv->readonly = ro; - - priv->conn = virshConnect(ctl, ctl->name, priv->readonly); - - if (!priv->conn) { - vshError(ctl, "%s", _("Failed to connect to the hypervisor")); - return false; - } - - if (virConnectRegisterCloseCallback(priv->conn, virshCatchDisconnect, - NULL, NULL) < 0) - vshError(ctl, "%s", _("Unable to register disconnect callback")); - - return true; -} - int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED, const char *bytes, size_t nbytes, void *opaque) { @@ -270,13 +197,9 @@ int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED, }
/* --------------- - * Commands + * Command Info * --------------- */ - -/* - * "help" command - */ static const vshCmdInfo info_help[] = { {.name = "help", .data = N_("print help") @@ -288,55 +211,6 @@ static const vshCmdInfo info_help[] = { {.name = NULL} };
-static const vshCmdOptDef opts_help[] = { - {.name = "command", - .type = VSH_OT_STRING, - .help = N_("Prints global help, command specific help, or help for a group of related commands") - }, - {.name = NULL} -}; - -static bool -cmdHelp(vshControl *ctl, const vshCmd *cmd) - { - const char *name = NULL; - - if (vshCommandOptString(ctl, cmd, "command", &name) <= 0) { - const vshCmdGrp *grp; - const vshCmdDef *def; - - vshPrint(ctl, "%s", _("Grouped commands:\n\n")); - - for (grp = cmdGroups; grp->name; grp++) { - vshPrint(ctl, _(" %s (help keyword '%s'):\n"), grp->name, - grp->keyword); - - for (def = grp->commands; def->name; def++) { - if (def->flags & VSH_CMD_FLAG_ALIAS) - continue; - vshPrint(ctl, " %-30s %s\n", def->name, - _(vshCmddefGetInfo(def, "help"))); - } - - vshPrint(ctl, "\n"); - } - - return true; - } - - if (vshCmddefSearch(name)) { - return vshCmddefHelp(ctl, name); - } else if (vshCmdGrpSearch(name)) { - return vshCmdGrpHelp(ctl, name); - } else { - vshError(ctl, _("command or command group '%s' doesn't exist"), name); - return false; - } -} - -/* - * "cd" command - */ static const vshCmdInfo info_cd[] = { {.name = "help", .data = N_("change the current directory") @@ -347,45 +221,6 @@ static const vshCmdInfo info_cd[] = { {.name = NULL} };
-static const vshCmdOptDef opts_cd[] = { - {.name = "dir", - .type = VSH_OT_STRING, - .help = N_("directory to switch to (default: home or else root)") - }, - {.name = NULL} -}; - -static bool -cmdCd(vshControl *ctl, const vshCmd *cmd) -{ - const char *dir = NULL; - char *dir_malloced = NULL; - bool ret = true; - char ebuf[1024]; - - if (!ctl->imode) { - vshError(ctl, "%s", _("cd: command valid only in interactive mode")); - return false; - } - - if (vshCommandOptString(ctl, cmd, "dir", &dir) <= 0) - dir = dir_malloced = virGetUserDirectory(); - if (!dir) - dir = "/"; - - if (chdir(dir) == -1) { - vshError(ctl, _("cd: %s: %s"), - virStrerror(errno, ebuf, sizeof(ebuf)), dir); - ret = false; - } - - VIR_FREE(dir_malloced); - return ret; -} - -/* - * "pwd" command - */ static const vshCmdInfo info_pwd[] = { {.name = "help", .data = N_("print the current directory") @@ -396,29 +231,6 @@ static const vshCmdInfo info_pwd[] = { {.name = NULL} };
-static bool -cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) -{ - char *cwd; - bool ret = true; - char ebuf[1024]; - - cwd = getcwd(NULL, 0); - if (!cwd) { - vshError(ctl, _("pwd: cannot get current directory: %s"), - virStrerror(errno, ebuf, sizeof(ebuf))); - ret = false; - } else { - vshPrint(ctl, _("%s\n"), cwd); - VIR_FREE(cwd); - } - - return ret; -} - -/* - * "echo" command - */ static const vshCmdInfo info_echo[] = { {.name = "help", .data = N_("echo arguments") @@ -429,6 +241,47 @@ static const vshCmdInfo info_echo[] = { {.name = NULL} };
+static const vshCmdInfo info_quit[] = { + {.name = "help", + .data = N_("quit this interactive terminal") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdInfo info_connect[] = { + {.name = "help", + .data = N_("(re)connect to hypervisor") + }, + {.name = "desc", + .data = N_("Connect to local hypervisor. This is built-in " + "command after shell start up.") + }, + {.name = NULL} +}; + +/* --------------- + * Command Opts + * --------------- + */ +static const vshCmdOptDef opts_help[] = { + {.name = "command", + .type = VSH_OT_STRING, + .help = N_("Prints global help, command specific help, or help for a group of related commands") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_cd[] = { + {.name = "dir", + .type = VSH_OT_STRING, + .help = N_("directory to switch to (default: home or else root)") + }, + {.name = NULL} +}; + static const vshCmdOptDef opts_echo[] = { {.name = "shell", .type = VSH_OT_BOOL, @@ -453,80 +306,61 @@ static const vshCmdOptDef opts_echo[] = { {.name = NULL} };
-/* Exists mainly for debugging virsh, but also handy for adding back - * quotes for later evaluation. - */ +static const vshCmdOptDef opts_connect[] = { + {.name = "name", + .type = VSH_OT_STRING, + .flags = VSH_OFLAG_EMPTY_OK, + .help = N_("hypervisor connection URI") + }, + {.name = "readonly", + .type = VSH_OT_BOOL, + .help = N_("read-only connection") + }, + {.name = NULL} +}; + static bool -cmdEcho(vshControl *ctl, const vshCmd *cmd) +cmdConnect(vshControl *ctl, const vshCmd *cmd) { - bool shell = false; - bool xml = false; - int count = 0; - const vshCmdOpt *opt = NULL; - char *arg; - virBuffer buf = VIR_BUFFER_INITIALIZER; - - if (vshCommandOptBool(cmd, "shell")) - shell = true; - if (vshCommandOptBool(cmd, "xml")) - xml = true; - - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - char *str; - virBuffer xmlbuf = VIR_BUFFER_INITIALIZER; - - arg = opt->data; - - if (count) - virBufferAddChar(&buf, ' '); - - if (xml) { - virBufferEscapeString(&xmlbuf, "%s", arg); - if (virBufferError(&xmlbuf)) { - vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); - return false; - } - str = virBufferContentAndReset(&xmlbuf); - } else { - str = vshStrdup(ctl, arg); - } + bool ro = vshCommandOptBool(cmd, "readonly"); + const char *name = NULL; + virshControlPtr priv = ctl->privData;
- if (shell) - virBufferEscapeShell(&buf, str); - else - virBufferAdd(&buf, str, -1); - count++; - VIR_FREE(str); + if (priv->conn) { + int ret; + + virConnectUnregisterCloseCallback(priv->conn, virshCatchDisconnect); + ret = virConnectClose(priv->conn); + if (ret < 0) + vshError(ctl, "%s", _("Failed to disconnect from the hypervisor")); + else if (ret > 0) + vshError(ctl, "%s", _("One or more references were leaked after " + "disconnect from the hypervisor")); + priv->conn = NULL; }
- if (virBufferError(&buf)) { - vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + VIR_FREE(ctl->connname); + if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) + return false; + + ctl->connname = vshStrdup(ctl, name); + + priv->useGetInfo = false; + priv->useSnapshotOld = false; + priv->blockJobNoBytes = false; + priv->readonly = ro; + + priv->conn = virshConnect(ctl, ctl->connname, priv->readonly); + + if (!priv->conn) { + vshError(ctl, "%s", _("Failed to connect to the hypervisor")); return false; } - arg = virBufferContentAndReset(&buf); - if (arg) - vshPrint(ctl, "%s", arg); - VIR_FREE(arg); - return true; -}
-/* - * "quit" command - */ -static const vshCmdInfo info_quit[] = { - {.name = "help", - .data = N_("quit this interactive terminal") - }, - {.name = "desc", - .data = "" - }, - {.name = NULL} -}; + if (virConnectRegisterCloseCallback(priv->conn, virshCatchDisconnect, + NULL, NULL) < 0) + vshError(ctl, "%s", _("Unable to register disconnect callback"));
-static bool -cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) -{ - ctl->imode = false; return true; }
@@ -535,7 +369,6 @@ cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) * --------------- */
- static bool virshConnectionUsability(vshControl *ctl, virConnectPtr conn) { diff --git a/tools/vsh.c b/tools/vsh.c index 043d0fb..084cac5 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -2748,3 +2748,158 @@ vshDeinit(vshControl *ctl) vshReadlineDeinit(ctl); vshCloseLogFile(ctl); } + +/* ----------------------------------------------- + * Generic commands available to use by any client + * ----------------------------------------------- + */ + +bool +cmdHelp(vshControl *ctl, const vshCmd *cmd) + { + const char *name = NULL; + + if (vshCommandOptString(ctl, cmd, "command", &name) <= 0) { + const vshCmdGrp *grp; + const vshCmdDef *def; + + vshPrint(ctl, "%s", _("Grouped commands:\n\n")); + + for (grp = cmdGroups; grp->name; grp++) { + vshPrint(ctl, _(" %s (help keyword '%s'):\n"), grp->name, + grp->keyword); + + for (def = grp->commands; def->name; def++) { + if (def->flags & VSH_CMD_FLAG_ALIAS) + continue; + vshPrint(ctl, " %-30s %s\n", def->name, + _(vshCmddefGetInfo(def, "help"))); + } + + vshPrint(ctl, "\n"); + } + + return true; + } + + if (vshCmddefSearch(name)) { + return vshCmddefHelp(ctl, name); + } else if (vshCmdGrpSearch(name)) { + return vshCmdGrpHelp(ctl, name); + } else { + vshError(ctl, _("command or command group '%s' doesn't exist"), name); + return false; + } +} + +bool +cmdCd(vshControl *ctl, const vshCmd *cmd) +{ + const char *dir = NULL; + char *dir_malloced = NULL; + bool ret = true; + char ebuf[1024]; + + if (!ctl->imode) { + vshError(ctl, "%s", _("cd: command valid only in interactive mode")); + return false; + } + + if (vshCommandOptString(ctl, cmd, "dir", &dir) <= 0) + dir = dir_malloced = virGetUserDirectory(); + if (!dir) + dir = "/"; + + if (chdir(dir) == -1) { + vshError(ctl, _("cd: %s: %s"), + virStrerror(errno, ebuf, sizeof(ebuf)), dir); + ret = false; + } + + VIR_FREE(dir_malloced); + return ret; +} + +/* Exists mainly for debugging virsh, but also handy for adding back + * quotes for later evaluation. + */ +bool +cmdEcho(vshControl *ctl, const vshCmd *cmd) +{ + bool shell = false; + bool xml = false; + int count = 0; + const vshCmdOpt *opt = NULL; + char *arg; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (vshCommandOptBool(cmd, "shell")) + shell = true; + if (vshCommandOptBool(cmd, "xml")) + xml = true; + + while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { + char *str; + virBuffer xmlbuf = VIR_BUFFER_INITIALIZER; + + arg = opt->data; + + if (count) + virBufferAddChar(&buf, ' '); + + if (xml) { + virBufferEscapeString(&xmlbuf, "%s", arg); + if (virBufferError(&xmlbuf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + return false; + } + str = virBufferContentAndReset(&xmlbuf); + } else { + str = vshStrdup(ctl, arg); + } + + if (shell) + virBufferEscapeShell(&buf, str); + else + virBufferAdd(&buf, str, -1); + count++; + VIR_FREE(str); + } + + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + return false; + } + arg = virBufferContentAndReset(&buf); + if (arg) + vshPrint(ctl, "%s", arg); + VIR_FREE(arg); + return true; +} + +bool +cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + char *cwd; + bool ret = true; + char ebuf[1024]; + + cwd = getcwd(NULL, 0); + if (!cwd) { + vshError(ctl, _("pwd: cannot get current directory: %s"), + virStrerror(errno, ebuf, sizeof(ebuf))); + ret = false; + } else { + vshPrint(ctl, _("%s\n"), cwd); + VIR_FREE(cwd); + } + + return ret; +} + +bool +cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + ctl->imode = false; + return true; +} diff --git a/tools/vsh.h b/tools/vsh.h index 19862d0..cbc3455 100644 --- a/tools/vsh.h +++ b/tools/vsh.h @@ -363,6 +363,13 @@ int vshEventStart(vshControl *ctl, int timeout_ms); void vshEventTimeout(int timer, void *opaque); int vshEventWait(vshControl *ctl);
+/* generic commands */ +bool cmdHelp(vshControl *ctl, const vshCmd *cmd); +bool cmdCd(vshControl *ctl, const vshCmd *cmd); +bool cmdEcho(vshControl *ctl, const vshCmd *cmd); +bool cmdPwd(vshControl *ctl, const vshCmd *cmd); +bool cmdQuit(vshControl *ctl, const vshCmd *cmd); + /* readline */ char * vshReadline(vshControl *ctl, const char *prompt);

Generic commands like 'help', 'cd', 'pwd',etc. can be reused by any client, so the clients should profit from this implementation rather than providing their own similar implementation (if it's not intensional and there's a reason for this) --- tools/virsh.c | 351 +++++----------------------------------------------------- tools/vsh.c | 245 ++++++++++++++++++++++++++++++++++++++++ tools/vsh.h | 71 ++++++++++++ 3 files changed, 346 insertions(+), 321 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 2d198cd..391c155 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -188,20 +188,18 @@ virshReconnect(vshControl *ctl) priv->blockJobNoBytes = false; } +int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED, + const char *bytes, size_t nbytes, void *opaque) +{ + int *fd = opaque; -/* - * "connect" command + return safewrite(*fd, bytes, nbytes); +} + +/* --------------- + * Command Connect + * --------------- */ -static const vshCmdInfo info_connect[] = { - {.name = "help", - .data = N_("(re)connect to hypervisor") - }, - {.name = "desc", - .data = N_("Connect to local hypervisor. This is built-in " - "command after shell start up.") - }, - {.name = NULL} -}; static const vshCmdOptDef opts_connect[] = { {.name = "name", @@ -216,6 +214,17 @@ static const vshCmdOptDef opts_connect[] = { {.name = NULL} }; +static const vshCmdInfo info_connect[] = { + {.name = "help", + .data = N_("(re)connect to hypervisor") + }, + {.name = "desc", + .data = N_("Connect to local hypervisor. This is built-in " + "command after shell start up.") + }, + {.name = NULL} +}; + static bool cmdConnect(vshControl *ctl, const vshCmd *cmd) { @@ -236,18 +245,18 @@ cmdConnect(vshControl *ctl, const vshCmd *cmd) priv->conn = NULL; } - VIR_FREE(ctl->name); + VIR_FREE(ctl->connname); if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) return false; - ctl->name = vshStrdup(ctl, name); + ctl->connname = vshStrdup(ctl, name); priv->useGetInfo = false; priv->useSnapshotOld = false; priv->blockJobNoBytes = false; priv->readonly = ro; - priv->conn = virshConnect(ctl, ctl->name, priv->readonly); + priv->conn = virshConnect(ctl, ctl->connname, priv->readonly); if (!priv->conn) { vshError(ctl, "%s", _("Failed to connect to the hypervisor")); @@ -261,281 +270,11 @@ cmdConnect(vshControl *ctl, const vshCmd *cmd) return true; } -int virshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED, - const char *bytes, size_t nbytes, void *opaque) -{ - int *fd = opaque; - - return safewrite(*fd, bytes, nbytes); -} - -/* --------------- - * Commands - * --------------- - */ - -/* - * "help" command - */ -static const vshCmdInfo info_help[] = { - {.name = "help", - .data = N_("print help") - }, - {.name = "desc", - .data = N_("Prints global help, command specific help, or help for a\n" - " group of related commands") - }, - {.name = NULL} -}; - -static const vshCmdOptDef opts_help[] = { - {.name = "command", - .type = VSH_OT_STRING, - .help = N_("Prints global help, command specific help, or help for a group of related commands") - }, - {.name = NULL} -}; - -static bool -cmdHelp(vshControl *ctl, const vshCmd *cmd) - { - const char *name = NULL; - - if (vshCommandOptString(ctl, cmd, "command", &name) <= 0) { - const vshCmdGrp *grp; - const vshCmdDef *def; - - vshPrint(ctl, "%s", _("Grouped commands:\n\n")); - - for (grp = cmdGroups; grp->name; grp++) { - vshPrint(ctl, _(" %s (help keyword '%s'):\n"), grp->name, - grp->keyword); - - for (def = grp->commands; def->name; def++) { - if (def->flags & VSH_CMD_FLAG_ALIAS) - continue; - vshPrint(ctl, " %-30s %s\n", def->name, - _(vshCmddefGetInfo(def, "help"))); - } - - vshPrint(ctl, "\n"); - } - - return true; - } - - if (vshCmddefSearch(name)) { - return vshCmddefHelp(ctl, name); - } else if (vshCmdGrpSearch(name)) { - return vshCmdGrpHelp(ctl, name); - } else { - vshError(ctl, _("command or command group '%s' doesn't exist"), name); - return false; - } -} - -/* - * "cd" command - */ -static const vshCmdInfo info_cd[] = { - {.name = "help", - .data = N_("change the current directory") - }, - {.name = "desc", - .data = N_("Change the current directory.") - }, - {.name = NULL} -}; - -static const vshCmdOptDef opts_cd[] = { - {.name = "dir", - .type = VSH_OT_STRING, - .help = N_("directory to switch to (default: home or else root)") - }, - {.name = NULL} -}; - -static bool -cmdCd(vshControl *ctl, const vshCmd *cmd) -{ - const char *dir = NULL; - char *dir_malloced = NULL; - bool ret = true; - char ebuf[1024]; - - if (!ctl->imode) { - vshError(ctl, "%s", _("cd: command valid only in interactive mode")); - return false; - } - - if (vshCommandOptString(ctl, cmd, "dir", &dir) <= 0) - dir = dir_malloced = virGetUserDirectory(); - if (!dir) - dir = "/"; - - if (chdir(dir) == -1) { - vshError(ctl, _("cd: %s: %s"), - virStrerror(errno, ebuf, sizeof(ebuf)), dir); - ret = false; - } - - VIR_FREE(dir_malloced); - return ret; -} - -/* - * "pwd" command - */ -static const vshCmdInfo info_pwd[] = { - {.name = "help", - .data = N_("print the current directory") - }, - {.name = "desc", - .data = N_("Print the current directory.") - }, - {.name = NULL} -}; - -static bool -cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) -{ - char *cwd; - bool ret = true; - char ebuf[1024]; - - cwd = getcwd(NULL, 0); - if (!cwd) { - vshError(ctl, _("pwd: cannot get current directory: %s"), - virStrerror(errno, ebuf, sizeof(ebuf))); - ret = false; - } else { - vshPrint(ctl, _("%s\n"), cwd); - VIR_FREE(cwd); - } - - return ret; -} - -/* - * "echo" command - */ -static const vshCmdInfo info_echo[] = { - {.name = "help", - .data = N_("echo arguments") - }, - {.name = "desc", - .data = N_("Echo back arguments, possibly with quoting.") - }, - {.name = NULL} -}; - -static const vshCmdOptDef opts_echo[] = { - {.name = "shell", - .type = VSH_OT_BOOL, - .help = N_("escape for shell use") - }, - {.name = "xml", - .type = VSH_OT_BOOL, - .help = N_("escape for XML use") - }, - {.name = "str", - .type = VSH_OT_ALIAS, - .help = "string" - }, - {.name = "hi", - .type = VSH_OT_ALIAS, - .help = "string=hello" - }, - {.name = "string", - .type = VSH_OT_ARGV, - .help = N_("arguments to echo") - }, - {.name = NULL} -}; - -/* Exists mainly for debugging virsh, but also handy for adding back - * quotes for later evaluation. - */ -static bool -cmdEcho(vshControl *ctl, const vshCmd *cmd) -{ - bool shell = false; - bool xml = false; - int count = 0; - const vshCmdOpt *opt = NULL; - char *arg; - virBuffer buf = VIR_BUFFER_INITIALIZER; - - if (vshCommandOptBool(cmd, "shell")) - shell = true; - if (vshCommandOptBool(cmd, "xml")) - xml = true; - - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - char *str; - virBuffer xmlbuf = VIR_BUFFER_INITIALIZER; - - arg = opt->data; - - if (count) - virBufferAddChar(&buf, ' '); - - if (xml) { - virBufferEscapeString(&xmlbuf, "%s", arg); - if (virBufferError(&xmlbuf)) { - vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); - return false; - } - str = virBufferContentAndReset(&xmlbuf); - } else { - str = vshStrdup(ctl, arg); - } - - if (shell) - virBufferEscapeShell(&buf, str); - else - virBufferAdd(&buf, str, -1); - count++; - VIR_FREE(str); - } - - if (virBufferError(&buf)) { - vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); - return false; - } - arg = virBufferContentAndReset(&buf); - if (arg) - vshPrint(ctl, "%s", arg); - VIR_FREE(arg); - return true; -} - -/* - * "quit" command - */ -static const vshCmdInfo info_quit[] = { - {.name = "help", - .data = N_("quit this interactive terminal") - }, - {.name = "desc", - .data = "" - }, - {.name = NULL} -}; - -static bool -cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) -{ - ctl->imode = false; - return true; -} - /* --------------- * Utils for work with runtime commands data * --------------- */ - static bool virshConnectionUsability(vshControl *ctl, virConnectPtr conn) { @@ -1061,48 +800,18 @@ virshParseArgv(vshControl *ctl, int argc, char **argv) } static const vshCmdDef virshCmds[] = { - {.name = "cd", - .handler = cmdCd, - .opts = opts_cd, - .info = info_cd, - .flags = VSH_CMD_FLAG_NOCONNECT - }, + 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 = "echo", - .handler = cmdEcho, - .opts = opts_echo, - .info = info_echo, - .flags = VSH_CMD_FLAG_NOCONNECT - }, - {.name = "exit", - .handler = cmdQuit, - .opts = NULL, - .info = info_quit, - .flags = VSH_CMD_FLAG_NOCONNECT - }, - {.name = "help", - .handler = cmdHelp, - .opts = opts_help, - .info = info_help, - .flags = VSH_CMD_FLAG_NOCONNECT - }, - {.name = "pwd", - .handler = cmdPwd, - .opts = NULL, - .info = info_pwd, - .flags = VSH_CMD_FLAG_NOCONNECT - }, - {.name = "quit", - .handler = cmdQuit, - .opts = NULL, - .info = info_quit, - .flags = VSH_CMD_FLAG_NOCONNECT - }, {.name = NULL} }; diff --git a/tools/vsh.c b/tools/vsh.c index 043d0fb..03ff859 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -2748,3 +2748,248 @@ vshDeinit(vshControl *ctl) vshReadlineDeinit(ctl); vshCloseLogFile(ctl); } + +/* ----------------------------------------------- + * Generic commands available to use by any client + * ----------------------------------------------- + */ +const vshCmdOptDef opts_help[] = { + {.name = "command", + .type = VSH_OT_STRING, + .help = N_("Prints global help, command specific help, or help for a group of related commands") + }, + {.name = NULL} +}; + +const vshCmdInfo info_help[] = { + {.name = "help", + .data = N_("print help") + }, + {.name = "desc", + .data = N_("Prints global help, command specific help, or help for a\n" + " group of related commands") + }, + {.name = NULL} +}; + +bool +cmdHelp(vshControl *ctl, const vshCmd *cmd) + { + const char *name = NULL; + + if (vshCommandOptString(ctl, cmd, "command", &name) <= 0) { + const vshCmdGrp *grp; + const vshCmdDef *def; + + vshPrint(ctl, "%s", _("Grouped commands:\n\n")); + + for (grp = cmdGroups; grp->name; grp++) { + vshPrint(ctl, _(" %s (help keyword '%s'):\n"), grp->name, + grp->keyword); + + for (def = grp->commands; def->name; def++) { + if (def->flags & VSH_CMD_FLAG_ALIAS) + continue; + vshPrint(ctl, " %-30s %s\n", def->name, + _(vshCmddefGetInfo(def, "help"))); + } + + vshPrint(ctl, "\n"); + } + + return true; + } + + if (vshCmddefSearch(name)) { + return vshCmddefHelp(ctl, name); + } else if (vshCmdGrpSearch(name)) { + return vshCmdGrpHelp(ctl, name); + } else { + vshError(ctl, _("command or command group '%s' doesn't exist"), name); + return false; + } +} + +const vshCmdOptDef opts_cd[] = { + {.name = "dir", + .type = VSH_OT_STRING, + .help = N_("directory to switch to (default: home or else root)") + }, + {.name = NULL} +}; + +const vshCmdInfo info_cd[] = { + {.name = "help", + .data = N_("change the current directory") + }, + {.name = "desc", + .data = N_("Change the current directory.") + }, + {.name = NULL} +}; + +bool +cmdCd(vshControl *ctl, const vshCmd *cmd) +{ + const char *dir = NULL; + char *dir_malloced = NULL; + bool ret = true; + char ebuf[1024]; + + if (!ctl->imode) { + vshError(ctl, "%s", _("cd: command valid only in interactive mode")); + return false; + } + + if (vshCommandOptString(ctl, cmd, "dir", &dir) <= 0) + dir = dir_malloced = virGetUserDirectory(); + if (!dir) + dir = "/"; + + if (chdir(dir) == -1) { + vshError(ctl, _("cd: %s: %s"), + virStrerror(errno, ebuf, sizeof(ebuf)), dir); + ret = false; + } + + VIR_FREE(dir_malloced); + return ret; +} + +const vshCmdOptDef opts_echo[] = { + {.name = "shell", + .type = VSH_OT_BOOL, + .help = N_("escape for shell use") + }, + {.name = "xml", + .type = VSH_OT_BOOL, + .help = N_("escape for XML use") + }, + {.name = "str", + .type = VSH_OT_ALIAS, + .help = "string" + }, + {.name = "hi", + .type = VSH_OT_ALIAS, + .help = "string=hello" + }, + {.name = "string", + .type = VSH_OT_ARGV, + .help = N_("arguments to echo") + }, + {.name = NULL} +}; + +const vshCmdInfo info_echo[] = { + {.name = "help", + .data = N_("echo arguments") + }, + {.name = "desc", + .data = N_("Echo back arguments, possibly with quoting.") + }, + {.name = NULL} +}; + +/* Exists mainly for debugging virsh, but also handy for adding back + * quotes for later evaluation. + */ +bool +cmdEcho(vshControl *ctl, const vshCmd *cmd) +{ + bool shell = false; + bool xml = false; + int count = 0; + const vshCmdOpt *opt = NULL; + char *arg; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (vshCommandOptBool(cmd, "shell")) + shell = true; + if (vshCommandOptBool(cmd, "xml")) + xml = true; + + while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { + char *str; + virBuffer xmlbuf = VIR_BUFFER_INITIALIZER; + + arg = opt->data; + + if (count) + virBufferAddChar(&buf, ' '); + + if (xml) { + virBufferEscapeString(&xmlbuf, "%s", arg); + if (virBufferError(&xmlbuf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + return false; + } + str = virBufferContentAndReset(&xmlbuf); + } else { + str = vshStrdup(ctl, arg); + } + + if (shell) + virBufferEscapeShell(&buf, str); + else + virBufferAdd(&buf, str, -1); + count++; + VIR_FREE(str); + } + + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + return false; + } + arg = virBufferContentAndReset(&buf); + if (arg) + vshPrint(ctl, "%s", arg); + VIR_FREE(arg); + return true; +} + +const vshCmdInfo info_pwd[] = { + {.name = "help", + .data = N_("print the current directory") + }, + {.name = "desc", + .data = N_("Print the current directory.") + }, + {.name = NULL} +}; + +bool +cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + char *cwd; + bool ret = true; + char ebuf[1024]; + + cwd = getcwd(NULL, 0); + if (!cwd) { + vshError(ctl, _("pwd: cannot get current directory: %s"), + virStrerror(errno, ebuf, sizeof(ebuf))); + ret = false; + } else { + vshPrint(ctl, _("%s\n"), cwd); + VIR_FREE(cwd); + } + + return ret; +} + +const vshCmdInfo info_quit[] = { + {.name = "help", + .data = N_("quit this interactive terminal") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +bool +cmdQuit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + ctl->imode = false; + return true; +} diff --git a/tools/vsh.h b/tools/vsh.h index 19862d0..37416c7 100644 --- a/tools/vsh.h +++ b/tools/vsh.h @@ -363,6 +363,77 @@ int vshEventStart(vshControl *ctl, int timeout_ms); void vshEventTimeout(int timer, void *opaque); int vshEventWait(vshControl *ctl); +/* generic commands */ +extern const vshCmdOptDef opts_help[]; +extern const vshCmdInfo info_help[]; +extern const vshCmdOptDef opts_cd[]; +extern const vshCmdInfo info_cd[]; +extern const vshCmdOptDef opts_echo[]; +extern const vshCmdInfo info_echo[]; +extern const vshCmdInfo info_pwd[]; +extern const vshCmdInfo info_quit[]; + +bool cmdHelp(vshControl *ctl, const vshCmd *cmd); +bool cmdCd(vshControl *ctl, const vshCmd *cmd); +bool cmdEcho(vshControl *ctl, const vshCmd *cmd); +bool cmdPwd(vshControl *ctl, const vshCmd *cmd); +bool cmdQuit(vshControl *ctl, const vshCmd *cmd); + +# define VSH_CMD_CD \ + { \ + .name = "cd", \ + .handler = cmdCd, \ + .opts = opts_cd, \ + .info = info_cd, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + +# define VSH_CMD_ECHO \ + { \ + .name = "echo", \ + .handler = cmdEcho, \ + .opts = opts_echo, \ + .info = info_echo, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + +# define VSH_CMD_EXIT \ + { \ + .name = "exit", \ + .handler = cmdQuit, \ + .opts = NULL, \ + .info = info_quit, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + +# define VSH_CMD_HELP \ + { \ + .name = "help", \ + .handler = cmdHelp, \ + .opts = opts_help, \ + .info = info_help, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + +# define VSH_CMD_PWD \ + { \ + .name = "pwd", \ + .handler = cmdPwd, \ + .opts = NULL, \ + .info = info_pwd, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + +# define VSH_CMD_QUIT \ + { \ + .name = "quit", \ + .handler = cmdQuit, \ + .opts = NULL, \ + .info = info_quit, \ + .flags = VSH_CMD_FLAG_NOCONNECT \ + } + + /* readline */ char * vshReadline(vshControl *ctl, const char *prompt); -- 2.4.3

On 14.08.2015 15:24, Erik Skultety wrote:
Generic commands like 'help', 'cd', 'pwd',etc. can be reused by any client, so the clients should profit from this implementation rather than providing their own similar implementation (if it's not intensional and there's a reason for this) --- tools/virsh.c | 351 +++++----------------------------------------------------- tools/vsh.c | 245 ++++++++++++++++++++++++++++++++++++++++ tools/vsh.h | 71 ++++++++++++ 3 files changed, 346 insertions(+), 321 deletions(-)
ACK Michal

On 13.08.2015 13:39, Erik Skultety wrote:
v3: - renamed virshCommandOptTimeoutToMs - resolved conflicts caused by virsh block job handling refactor - generic commands implementation moved to vsh.c
As usual, for testing purposes, everything is available on my remote branch https://github.com/eskultety/libvirt/tree/virt-shell
Erik Skultety (3): virt-shell: Resolve conflicts and some forgotten substitution from v2 virt-shell: Support command history for individual clients virt-shell: Move generic commands implementation to vsh.c
src/libvirt_private.syms | 1 + src/util/virstring.c | 32 ++++ src/util/virstring.h | 1 + tools/virsh-domain.c | 150 +++++++++--------- tools/virsh-network.c | 2 +- tools/virsh.c | 390 ++++++++++++----------------------------------- tools/virsh.h | 1 - tools/vsh.c | 242 ++++++++++++++++++++++++++--- tools/vsh.h | 13 +- 9 files changed, 441 insertions(+), 391 deletions(-)
ACK with the following squashed in: diff --git a/po/POTFILES.in b/po/POTFILES.in index 1a063b6..1e52e6a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -258,7 +258,6 @@ src/xenconfig/xen_xm.c tests/virpolkittest.c tools/libvirt-guests.sh.in tools/virsh.c -tools/virsh.h tools/virsh-console.c tools/virsh-domain-monitor.c tools/virsh-domain.c diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 9707463..eca9014 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -9813,7 +9813,7 @@ cmdDomrename(vshControl *ctl, const vshCmd *cmd) const char *new_name = NULL; bool ret = false; - if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) return ret; if (vshCommandOptStringReq(ctl, cmd, "new-name", &new_name) < 0) Michal

On 14/08/15 13:52, Michal Privoznik wrote:
On 13.08.2015 13:39, Erik Skultety wrote:
v3: - renamed virshCommandOptTimeoutToMs - resolved conflicts caused by virsh block job handling refactor - generic commands implementation moved to vsh.c
As usual, for testing purposes, everything is available on my remote branch https://github.com/eskultety/libvirt/tree/virt-shell
Erik Skultety (3): virt-shell: Resolve conflicts and some forgotten substitution from v2 virt-shell: Support command history for individual clients virt-shell: Move generic commands implementation to vsh.c
src/libvirt_private.syms | 1 + src/util/virstring.c | 32 ++++ src/util/virstring.h | 1 + tools/virsh-domain.c | 150 +++++++++--------- tools/virsh-network.c | 2 +- tools/virsh.c | 390 ++++++++++++----------------------------------- tools/virsh.h | 1 - tools/vsh.c | 242 ++++++++++++++++++++++++++--- tools/vsh.h | 13 +- 9 files changed, 441 insertions(+), 391 deletions(-)
ACK with the following squashed in:
diff --git a/po/POTFILES.in b/po/POTFILES.in index 1a063b6..1e52e6a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -258,7 +258,6 @@ src/xenconfig/xen_xm.c tests/virpolkittest.c tools/libvirt-guests.sh.in tools/virsh.c -tools/virsh.h tools/virsh-console.c tools/virsh-domain-monitor.c tools/virsh-domain.c diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 9707463..eca9014 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -9813,7 +9813,7 @@ cmdDomrename(vshControl *ctl, const vshCmd *cmd) const char *new_name = NULL; bool ret = false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) return ret;
if (vshCommandOptStringReq(ctl, cmd, "new-name", &new_name) < 0)
Michal
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Thank you for review, now pushed. Erik
participants (2)
-
Erik Skultety
-
Michal Privoznik