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 <pkrempa@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Peter Krempa 2024-03-06 17:26:56 +01:00
parent 1818cbda3b
commit da3c5638f9
7 changed files with 134 additions and 131 deletions

View File

@ -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, "<name>%s</name>\n", name);
virBufferEscapeString(&buf, "<description>%s</description>\n", desc);
if (vshCommandOptBool(cmd, "diskspec")) {
if ((diskspec = vshCommandOptArgv(cmd, "diskspec"))) {
virBufferAddLit(&buf, "<disks>\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, "</disks>\n");
}

View File

@ -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;

View File

@ -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"));

View File

@ -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;

View File

@ -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, "<disks>\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);

View File

@ -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)

View File

@ -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);