]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
virsh: rework command parsing
authorLai Jiangshan <laijs@cn.fujitsu.com>
Tue, 12 Oct 2010 07:14:01 +0000 (15:14 +0800)
committerEric Blake <eblake@redhat.com>
Wed, 13 Oct 2010 13:52:32 +0000 (07:52 -0600)
Old virsh command parsing mashes all the args back into a string and
miss the quotes, this patches fix it. It is also needed for introducing
qemu-monitor-command which is very useful.

This patches uses the new vshCommandParser abstraction and adds
vshCommandArgvParse() for arguments vector, so we don't need
to mash arguments vector into a command sting.

And the usage was changed:
old:
virsh [options] [commands]

new:
virsh [options]... [<command_string>]
virsh [options]... <command> [args...]

So we still support commands like:
"define D.xml; dumpxml D" was parsed as a commands-string.

and support commands like:
we will not mash them into a string, we use new argv parser for it.

But we don't support the command like:
"define D.xml; dumpxml" was parsed as a command-name, but we have no such command-name.

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com>
tools/virsh.c
tools/virsh.pod

index 557cdf83e491aad399d0209b834b75be620ab00a..4101161ede5bb5dbf1c272a18a6c794383f8bace 100644 (file)
@@ -10336,11 +10336,51 @@ typedef enum {
 typedef struct __vshCommandParser {
     vshCommandToken (*getNextArg)(vshControl *, struct __vshCommandParser *,
                                   char **);
+    /* vshCommandStringGetArg() */
     char *pos;
+    /* vshCommandArgvGetArg() */
+    char **arg_pos;
+    char **arg_end;
 } vshCommandParser;
 
 static int vshCommandParse(vshControl *ctl, vshCommandParser *parser);
 
+/* ---------------
+ * Command argv parsing
+ * ---------------
+ */
+
+static vshCommandToken ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
+vshCommandArgvGetArg(vshControl *ctl, vshCommandParser *parser, char **res)
+{
+    if (parser->arg_pos == parser->arg_end) {
+        *res = NULL;
+        return VSH_TK_END;
+    }
+
+    *res = vshStrdup(ctl, *parser->arg_pos);
+    parser->arg_pos++;
+    return VSH_TK_ARG;
+}
+
+static int vshCommandArgvParse(vshControl *ctl, int nargs, char **argv)
+{
+    vshCommandParser parser;
+
+    if (nargs <= 0)
+        return FALSE;
+
+    parser.arg_pos = argv;
+    parser.arg_end = argv + nargs;
+    parser.getNextArg = vshCommandArgvGetArg;
+    return vshCommandParse(ctl, &parser);
+}
+
+/* ---------------
+ * Command string parsing
+ * ---------------
+ */
+
 static vshCommandToken ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
 vshCommandStringGetArg(vshControl *ctl, vshCommandParser *parser, char **res)
 {
@@ -11045,7 +11085,8 @@ static void
 vshUsage(void)
 {
     const vshCmdDef *cmd;
-    fprintf(stdout, _("\n%s [options] [commands]\n\n"
+    fprintf(stdout, _("\n%s [options]... [<command_string>]"
+                      "\n%s [options]... <command> [args...]\n\n"
                       "  options:\n"
                       "    -c | --connect <uri>    hypervisor connection URI\n"
                       "    -r | --readonly         connect readonly\n"
@@ -11055,7 +11096,7 @@ vshUsage(void)
                       "    -t | --timing           print timing information\n"
                       "    -l | --log <file>       output logging to file\n"
                       "    -v | --version          program version\n\n"
-                      "  commands (non interactive mode):\n"), progname);
+                      "  commands (non interactive mode):\n"), progname, progname);
 
     for (cmd = commands; cmd->name; cmd++)
         fprintf(stdout,
@@ -11175,26 +11216,13 @@ vshParseArgv(vshControl *ctl, int argc, char **argv)
 
     if (argc > end) {
         /* parse command */
-        char *cmdstr;
-        int sz = 0, ret;
-
         ctl->imode = FALSE;
-
-        for (i = end; i < argc; i++)
-            sz += strlen(argv[i]) + 1;  /* +1 is for blank space between items */
-
-        cmdstr = vshCalloc(ctl, sz + 1, 1);
-
-        for (i = end; i < argc; i++) {
-            strncat(cmdstr, argv[i], sz);
-            sz -= strlen(argv[i]);
-            strncat(cmdstr, " ", sz--);
+        if (argc - end == 1) {
+            vshDebug(ctl, 2, "commands: \"%s\"\n", argv[end]);
+            return vshCommandStringParse(ctl, argv[end]);
+        } else {
+            return vshCommandArgvParse(ctl, argc - end, argv + end);
         }
-        vshDebug(ctl, 2, "command: \"%s\"\n", cmdstr);
-        ret = vshCommandStringParse(ctl, cmdstr);
-
-        VIR_FREE(cmdstr);
-        return ret;
     }
     return TRUE;
 }
index e0471b1b1e3ea4c05140338ca8fa415a984a9819..209aa54055d060f8a3e550962facbab53c88cabf 100644 (file)
@@ -4,7 +4,9 @@ virsh - management user interface
 
 =head1 SYNOPSIS
 
-virsh <subcommand> [args]
+B<virsh> [I<OPTION>]... [I<COMMAND_STRING>]
+
+B<virsh> [I<OPTION>]... I<COMMAND> [I<ARG>]...
 
 =head1 DESCRIPTION
 
@@ -22,20 +24,25 @@ KVM, LXC, OpenVZ, VirtualBox, OpenNebula, and VMware ESX.
 
 The basic structure of most virsh usage is:
 
-  virsh <command> <domain-id> [OPTIONS]
+  virsh <command> <domain-id> [ARG]...
 
 Where I<command> is one of the commands listed below, I<domain-id>
 is the numeric domain id, or the domain name (which will be internally
-translated to domain id), and I<OPTIONS> are command specific
+translated to domain id), and I<ARGS> are command specific
 options.  There are a few exceptions to this rule in the cases where
 the command in question acts on all domains, the entire machine,
 or directly on the xen hypervisor.  Those exceptions will be clear for
 each of those commands.
 
-The B<virsh> program can be used either to run one command at a time
-by giving the command as an argument on the command line, or as a shell
-if no command is given in the command line, it will then start a minimal
-interpreter waiting for your commands and the B<quit> command will then exit
+The B<virsh> program can be used either to run one I<COMMAND> by giving the
+command and its arguments on the shell command line, or a I<COMMAND_STRING>
+which is a single shell argument consisting of multiple I<COMMAND> actions
+and their arguments joined with whitespace, and separated by semicolons
+between commands.  Within I<COMMAND_STRING>, virsh understands the
+same single, double, and backslash escapes as the shell, although you must
+add another layer of shell escaping in creating the single shell argument.
+If no command is given in the command line, B<virsh> will then start a minimal
+interpreter waiting for your commands, and the B<quit> command will then exit
 the program.
 
 =head1 NOTES