From da3c5638f9cef5cc91e9f4678f324f38a4e27b14 Mon Sep 17 00:00:00 2001 From: Peter Krempa Date: Wed, 6 Mar 2024 17:26:56 +0100 Subject: [PATCH] virsh: Introduce new 'VSH_OT_ARGV' accessors In preparation for internal parser refactor introduce new accessors for the VSH_OT_ARGV type which will return a NULL-terminated string list or even a concatenated string for the given argument. Signed-off-by: Peter Krempa Reviewed-by: Michal Privoznik --- tools/virsh-checkpoint.c | 10 +-- tools/virsh-domain-monitor.c | 8 +-- tools/virsh-domain.c | 103 +++++++++---------------------- tools/virsh-network.c | 9 +-- tools/virsh-snapshot.c | 9 +-- tools/vsh.c | 116 ++++++++++++++++++++++++----------- tools/vsh.h | 10 ++- 7 files changed, 134 insertions(+), 131 deletions(-) diff --git a/tools/virsh-checkpoint.c b/tools/virsh-checkpoint.c index fea6b4fb4b..972b2f979c 100644 --- a/tools/virsh-checkpoint.c +++ b/tools/virsh-checkpoint.c @@ -226,7 +226,7 @@ cmdCheckpointCreateAs(vshControl *ctl, const char *desc = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; unsigned int flags = 0; - const vshCmdOpt *opt = NULL; + const char **diskspec = NULL; if (vshCommandOptBool(cmd, "quiesce")) flags |= VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE; @@ -243,13 +243,15 @@ cmdCheckpointCreateAs(vshControl *ctl, virBufferEscapeString(&buf, "%s\n", name); virBufferEscapeString(&buf, "%s\n", desc); - if (vshCommandOptBool(cmd, "diskspec")) { + if ((diskspec = vshCommandOptArgv(cmd, "diskspec"))) { virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - if (virshParseCheckpointDiskspec(ctl, &buf, opt->data) < 0) + + for (; *diskspec; diskspec++) { + if (virshParseCheckpointDiskspec(ctl, &buf, *diskspec) < 0) return false; } + virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); } diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c index 700f3ae094..ef07ace577 100644 --- a/tools/virsh-domain-monitor.c +++ b/tools/virsh-domain-monitor.c @@ -2096,7 +2096,7 @@ cmdDomstats(vshControl *ctl, const vshCmd *cmd) virDomainStatsRecordPtr *next; bool raw = vshCommandOptBool(cmd, "raw"); int flags = 0; - const vshCmdOpt *opt = NULL; + const char **doms; bool ret = false; virshControl *priv = ctl->privData; @@ -2166,12 +2166,12 @@ cmdDomstats(vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool(cmd, "nowait")) flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_NOWAIT; - if (vshCommandOptBool(cmd, "domain")) { + if ((doms = vshCommandOptArgv(cmd, "domain"))) { domlist = g_new0(virDomainPtr, 1); ndoms = 1; - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - if (!(dom = virshLookupDomainBy(ctl, opt->data, + for (; *doms; doms++) { + if (!(dom = virshLookupDomainBy(ctl, *doms, VIRSH_BYID | VIRSH_BYUUID | VIRSH_BYNAME))) goto cleanup; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 28d90377a1..50e80689a2 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -5065,15 +5065,15 @@ cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd, { char *set_val = NULL; const char *val = NULL; - const vshCmdOpt *opt = NULL; + const char **opt; virTypedParameterPtr params = NULL; int nparams = 0; int maxparams = 0; int ret = -1; int rv; - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - g_autofree char *set_field = g_strdup(opt->data); + for (opt = vshCommandOptArgv(cmd, "set"); opt && *opt; opt++) { + g_autofree char *set_field = g_strdup(*opt); if (!(set_val = strchr(set_field, '='))) { vshError(ctl, "%s", _("Invalid syntax for --set, expecting name=value")); @@ -8248,8 +8248,6 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) int state; int type; g_autofree char *descArg = NULL; - const vshCmdOpt *opt = NULL; - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; unsigned int queryflags = 0; @@ -8274,12 +8272,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) else type = VIR_DOMAIN_METADATA_DESCRIPTION; - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) - virBufferAsprintf(&buf, "%s ", opt->data); - - virBufferTrim(&buf, " "); - - descArg = virBufferContentAndReset(&buf); + descArg = g_strdup(vshCommandOptArgvString(cmd, "new-desc")); if (edit || descArg) { g_autofree char *descDom = NULL; @@ -8559,7 +8552,7 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd) int codeset; unsigned int holdtime = 0; int count = 0; - const vshCmdOpt *opt = NULL; + const char **opt = NULL; int keycode; unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; @@ -8583,15 +8576,15 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd) return false; } - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { + for (opt = vshCommandOptArgv(cmd, "keycode"); opt && *opt; opt++) { if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) { vshError(ctl, _("too many keycodes")); return false; } - if ((keycode = virshKeyCodeGetInt(opt->data)) < 0) { - if ((keycode = virKeycodeValueFromString(codeset, opt->data)) < 0) { - vshError(ctl, _("invalid keycode: '%1$s'"), opt->data); + if ((keycode = virshKeyCodeGetInt(*opt)) < 0) { + if ((keycode = virKeycodeValueFromString(codeset, *opt)) < 0) { + vshError(ctl, _("invalid keycode: '%1$s'"), *opt); return false; } } @@ -9659,31 +9652,15 @@ static const vshCmdOptDef opts_qemu_monitor_command[] = { }; -static char * -cmdQemuMonitorCommandConcatCmd(vshControl *ctl, - const vshCmd *cmd, - const vshCmdOpt *opt) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) - virBufferAsprintf(&buf, "%s ", opt->data); - - virBufferTrim(&buf, " "); - - return virBufferContentAndReset(&buf); -} - - static char * cmdQemuMonitorCommandQMPWrap(vshControl *ctl, const vshCmd *cmd) { - g_autofree char *fullcmd = cmdQemuMonitorCommandConcatCmd(ctl, cmd, NULL); + const char *fullcmd = vshCommandOptArgvString(cmd, "cmd"); g_autoptr(virJSONValue) fullcmdjson = NULL; g_autofree char *fullargs = NULL; g_autoptr(virJSONValue) fullargsjson = NULL; - const vshCmdOpt *opt = NULL; + const char **opt = NULL; const char *commandname = NULL; g_autoptr(virJSONValue) command = NULL; g_autoptr(virJSONValue) arguments = NULL; @@ -9695,16 +9672,17 @@ cmdQemuMonitorCommandQMPWrap(vshControl *ctl, /* if we've got a JSON object, pass it through */ if (virJSONValueIsObject(fullcmdjson)) - return g_steal_pointer(&fullcmd); + return g_strdup(fullcmd); /* we try to wrap the command and possible arguments into a JSON object, if * we as fall back we pass through what we've got from the user */ - if ((opt = vshCommandOptArgv(ctl, cmd, opt))) - commandname = opt->data; + opt = vshCommandOptArgv(cmd, "cmd"); + commandname = *opt; + opt++; /* now we process arguments similarly to how we've dealt with the full command */ - if ((fullargs = cmdQemuMonitorCommandConcatCmd(ctl, cmd, opt)) && + if ((fullargs = g_strjoinv(" ", (GStrv) opt)) && !(fullargsjson = virJSONValueFromString(fullargs))) { /* Reset the error before adding wrapping. */ vshResetLibvirtError(); @@ -9721,8 +9699,8 @@ cmdQemuMonitorCommandQMPWrap(vshControl *ctl, virBufferAddLit(&buf, "{"); /* opt points to the _ARGV option bit containing the command so we'll * iterate through the arguments now */ - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) - virBufferAsprintf(&buf, "%s,", opt->data); + for (; *opt; opt++) + virBufferAsprintf(&buf, "%s,", *opt); virBufferTrim(&buf, ","); virBufferAddLit(&buf, "}"); @@ -9767,7 +9745,7 @@ cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool(cmd, "hmp")) { flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP; - monitor_cmd = cmdQemuMonitorCommandConcatCmd(ctl, cmd, NULL); + monitor_cmd = g_strdup(vshCommandOptArgvString(cmd, "cmd")); } else { monitor_cmd = cmdQemuMonitorCommandQMPWrap(ctl, cmd); } @@ -10062,26 +10040,16 @@ cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshDomain) dom = NULL; bool ret = false; - g_autofree char *guest_agent_cmd = NULL; char *result = NULL; int timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT; int judge = 0; unsigned int flags = 0; - const vshCmdOpt *opt = NULL; - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; virJSONValue *pretty = NULL; dom = virshCommandOptDomain(ctl, cmd, NULL); if (dom == NULL) goto cleanup; - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) - virBufferAsprintf(&buf, "%s ", opt->data); - - virBufferTrim(&buf, " "); - - guest_agent_cmd = virBufferContentAndReset(&buf); - judge = vshCommandOptInt(ctl, cmd, "timeout", &timeout); if (judge < 0) goto cleanup; @@ -10106,7 +10074,7 @@ cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd) goto cleanup; } - result = virDomainQemuAgentCommand(dom, guest_agent_cmd, timeout, flags); + result = virDomainQemuAgentCommand(dom, vshCommandOptArgvString(cmd, "cmd"), timeout, flags); if (!result) goto cleanup; @@ -10158,9 +10126,7 @@ static bool cmdLxcEnterNamespace(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshDomain) dom = NULL; - const vshCmdOpt *opt = NULL; - g_autofree char **cmdargv = NULL; - size_t ncmdargv = 0; + const char **cmdargv = NULL; pid_t pid; int nfdlist; int *fdlist; @@ -10178,12 +10144,7 @@ cmdLxcEnterNamespace(vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool(cmd, "noseclabel")) setlabel = false; - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - VIR_EXPAND_N(cmdargv, ncmdargv, 1); - cmdargv[ncmdargv-1] = opt->data; - } - VIR_EXPAND_N(cmdargv, ncmdargv, 1); - cmdargv[ncmdargv - 1] = NULL; + cmdargv = vshCommandOptArgv(cmd, "cmd"); if ((nfdlist = virDomainLxcOpenNamespace(dom, &fdlist, 0)) < 0) return false; @@ -10233,7 +10194,7 @@ cmdLxcEnterNamespace(vshControl *ctl, const vshCmd *cmd) _exit(EXIT_CANCELED); if (pid == 0) { - execv(cmdargv[0], cmdargv); + execv(cmdargv[0], (char **) cmdargv); _exit(errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE); } @@ -12872,18 +12833,15 @@ static bool cmdDomFSFreeze(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshDomain) dom = NULL; - const vshCmdOpt *opt = NULL; - g_autofree const char **mountpoints = NULL; + const char **mountpoints = vshCommandOptArgv(cmd, "mountpoint"); size_t nmountpoints = 0; int count = 0; if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) return false; - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - VIR_EXPAND_N(mountpoints, nmountpoints, 1); - mountpoints[nmountpoints-1] = opt->data; - } + if (mountpoints) + nmountpoints = g_strv_length((GStrv) mountpoints); if ((count = virDomainFSFreeze(dom, mountpoints, nmountpoints, 0)) < 0) { vshError(ctl, _("Unable to freeze filesystems")); @@ -12913,18 +12871,15 @@ static bool cmdDomFSThaw(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshDomain) dom = NULL; - const vshCmdOpt *opt = NULL; - g_autofree const char **mountpoints = NULL; + const char **mountpoints = vshCommandOptArgv(cmd, "mountpoint"); size_t nmountpoints = 0; int count = 0; if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) return false; - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - VIR_EXPAND_N(mountpoints, nmountpoints, 1); - mountpoints[nmountpoints-1] = opt->data; - } + if (mountpoints) + nmountpoints = g_strv_length((GStrv) mountpoints); if ((count = virDomainFSThaw(dom, mountpoints, nmountpoints, 0)) < 0) { vshError(ctl, _("Unable to thaw filesystems")); diff --git a/tools/virsh-network.c b/tools/virsh-network.c index 24049a66f3..6fcc7fd8ee 100644 --- a/tools/virsh-network.c +++ b/tools/virsh-network.c @@ -388,8 +388,6 @@ cmdNetworkDesc(vshControl *ctl, const vshCmd *cmd) int type; g_autofree char *descArg = NULL; - const vshCmdOpt *opt = NULL; - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; unsigned int flags = VIR_NETWORK_UPDATE_AFFECT_CURRENT; unsigned int queryflags = 0; @@ -411,12 +409,7 @@ cmdNetworkDesc(vshControl *ctl, const vshCmd *cmd) else type = VIR_NETWORK_METADATA_DESCRIPTION; - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) - virBufferAsprintf(&buf, "%s ", opt->data); - - virBufferTrim(&buf, " "); - - descArg = virBufferContentAndReset(&buf); + descArg = g_strdup(vshCommandOptArgvString(cmd, "new-desc")); if (edit || descArg) { g_autofree char *descNet = NULL; diff --git a/tools/virsh-snapshot.c b/tools/virsh-snapshot.c index 415a390786..8b6a950a01 100644 --- a/tools/virsh-snapshot.c +++ b/tools/virsh-snapshot.c @@ -379,7 +379,7 @@ cmdSnapshotCreateAs(vshControl *ctl, const vshCmd *cmd) const char *memspec = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; unsigned int flags = 0; - const vshCmdOpt *opt = NULL; + const char **diskspec; if (vshCommandOptBool(cmd, "no-metadata")) flags |= VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA; @@ -416,11 +416,12 @@ cmdSnapshotCreateAs(vshControl *ctl, const vshCmd *cmd) if (memspec && virshParseSnapshotMemspec(ctl, &buf, memspec) < 0) return false; - if (vshCommandOptBool(cmd, "diskspec")) { + if ((diskspec = vshCommandOptArgv(cmd, "diskspec"))) { virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - if (virshParseSnapshotDiskspec(ctl, &buf, opt->data) < 0) + + for (; *diskspec; diskspec++) { + if (virshParseSnapshotDiskspec(ctl, &buf, *diskspec) < 0) return false; } virBufferAdjustIndent(&buf, -2); diff --git a/tools/vsh.c b/tools/vsh.c index 69948a36a5..91d01cc73f 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -779,6 +779,9 @@ vshCommandOptFree(vshCmdOpt * arg) a = a->next; g_free(tmp->data); + /* 'argv' doesn't own the strings themselves */ + g_free(tmp->argv); + g_free(tmp->argvstr); g_free(tmp); } } @@ -1226,30 +1229,80 @@ vshCommandOptBool(const vshCmd *cmd, const char *name) return vshCommandOpt(cmd, name, &dummy, false) == 1; } + +static vshCmdOpt * +vshCommandOptArgvInternal(const vshCmd *cmd, + const char *name) +{ + vshCmdOpt *first = NULL; + vshCmdOpt *opt; + size_t nargv = 0; + size_t nargv_alloc = 0; + + for (opt = cmd->opts; opt; opt = opt->next) { + if (STRNEQ(opt->def->name, name)) + continue; + + if (!first) { + /* Return existing data if we'we already processed it */ + if (opt->argv) + return opt; + + first = opt; + } + + /* extra NULL terminator */ + VIR_RESIZE_N(first->argv, nargv_alloc, nargv, 2); + first->argv[nargv++] = opt->data; + } + + if (first) + first->argvstr = g_strjoinv(" ", (GStrv) first->argv); + + return first; +} + + /** * vshCommandOptArgv: - * @ctl virtshell control structure - * @cmd command reference - * @opt starting point for the search + * @cmd: command reference + * @name: name of argument * - * Returns the next argv argument after OPT (or the first one if OPT - * is NULL), or NULL if no more are present. - * - * Requires that a VSH_OT_ARGV option be last in the - * list of supported options in CMD->def->opts. + * Returns a NULL terminated list of strings of values passed as argument of + * ARGV argument named @name. The returned string list is owned by @cmd and + * caller must not free or modify it. */ -const vshCmdOpt * -vshCommandOptArgv(vshControl *ctl G_GNUC_UNUSED, const vshCmd *cmd, - const vshCmdOpt *opt) +const char ** +vshCommandOptArgv(const vshCmd *cmd, + const char *name) { - opt = opt ? opt->next : cmd->opts; + vshCmdOpt *opt = vshCommandOptArgvInternal(cmd, name); - while (opt) { - if (opt->def->type == VSH_OT_ARGV) - return opt; - opt = opt->next; - } - return NULL; + if (!opt) + return NULL; + + return opt->argv; +} + + +/** + * vshCommandOptArgvString: + * @cmd: command reference + * @name: name of argument + * + * Returns a string containing all values passed as ARGV argument @name + * delimited/concatenated by adding spaces. + */ +const char * +vshCommandOptArgvString(const vshCmd *cmd, + const char *name) +{ + vshCmdOpt *opt = vshCommandOptArgvInternal(cmd, name); + + if (!opt) + return NULL; + + return opt->argvstr; } @@ -3279,9 +3332,9 @@ cmdEcho(vshControl *ctl, const vshCmd *cmd) bool err = vshCommandOptBool(cmd, "err"); bool split = vshCommandOptBool(cmd, "split"); const char *prefix; - const vshCmdOpt *opt = NULL; g_autofree char *arg = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + const char **o; VSH_EXCLUSIVE_OPTIONS_VAR(shell, xml); VSH_EXCLUSIVE_OPTIONS_VAR(shell, split); @@ -3292,8 +3345,8 @@ cmdEcho(vshControl *ctl, const vshCmd *cmd) if (prefix) virBufferAsprintf(&buf, "%s ", prefix); - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - const char *curr = opt->data; + for (o = vshCommandOptArgv(cmd, "string"); o && *o; o++) { + const char *curr = *o; if (xml) { virBufferEscapeString(&buf, "%s", curr); @@ -3435,14 +3488,14 @@ bool cmdComplete(vshControl *ctl, const vshCmd *cmd) { const vshClientHooks *hooks = ctl->hooks; - const char *arg = ""; - const vshCmdOpt *opt = NULL; + const char *lastArg = NULL; + const char **args = NULL; g_auto(GStrv) matches = NULL; char **iter; - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - if (vshCommandOptStringQuiet(ctl, cmd, "string", &arg) <= 0) - return false; + /* The completer needs also the last component */ + for (args = vshCommandOptArgv(cmd, "string"); args && *args; args++) + lastArg = *args; /* This command is flagged VSH_CMD_FLAG_NOCONNECT because we * need to prevent auth hooks reading any input. Therefore, we @@ -3455,23 +3508,16 @@ cmdComplete(vshControl *ctl, const vshCmd *cmd) if (!(hooks && hooks->connHandler && hooks->connHandler(ctl))) return false; - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) { - if (virBufferUse(&buf) != 0) - virBufferAddChar(&buf, ' '); - virBufferAddStr(&buf, opt->data); - arg = opt->data; - } - vshReadlineInit(ctl); - if (!(rl_line_buffer = virBufferContentAndReset(&buf))) + if (!(rl_line_buffer = g_strdup(vshCommandOptArgvString(cmd, "string")))) rl_line_buffer = g_strdup(""); /* rl_point is current cursor position in rl_line_buffer. * In our case it's at the end of the whole line. */ rl_point = strlen(rl_line_buffer); - matches = vshReadlineCompletion(arg, 0, 0); + matches = vshReadlineCompletion(lastArg, 0, 0); g_clear_pointer(&rl_line_buffer, g_free); if (!matches) diff --git a/tools/vsh.h b/tools/vsh.h index fa3f1406e7..04d7e163f1 100644 --- a/tools/vsh.h +++ b/tools/vsh.h @@ -147,6 +147,8 @@ struct _vshCmdOptDef { struct _vshCmdOpt { const vshCmdOptDef *def; /* non-NULL pointer to option definition */ char *data; /* allocated data, or NULL for bool option */ + const char **argv; /* for VSH_OT_ARGV, the list of options */ + char *argvstr; /* space-joined @argv */ bool completeThis; /* true if this is the option user's wishing to autocomplete */ vshCmdOpt *next; @@ -292,8 +294,12 @@ bool vshCommandRun(vshControl *ctl, const vshCmd *cmd); bool vshCommandStringParse(vshControl *ctl, char *cmdstr, vshCmd **partial, size_t point); -const vshCmdOpt *vshCommandOptArgv(vshControl *ctl, const vshCmd *cmd, - const vshCmdOpt *opt); +const char ** +vshCommandOptArgv(const vshCmd *cmd, + const char *name); +const char * +vshCommandOptArgvString(const vshCmd *cmd, + const char *name); bool vshCommandArgvParse(vshControl *ctl, int nargs, char **argv); int vshCommandOptTimeoutToMs(vshControl *ctl, const vshCmd *cmd, int *timeout);