Add command trace functionality.
authorRichard Jones <rjones@trick.home.annexia.org>
Wed, 9 Sep 2009 16:48:30 +0000 (17:48 +0100)
committerRichard Jones <rjones@trick.home.annexia.org>
Mon, 14 Sep 2009 10:23:52 +0000 (11:23 +0100)
Enable this by calling guestfs_trace (handle, 1) or by
setting the LIBGUESTFS_TRACE=1 environment variable.

guestfish.pod
guestfs.pod
src/generator.ml
src/guestfs.c

index 2e50873..d24c162 100644 (file)
@@ -568,6 +568,10 @@ Set the default qemu binary that libguestfs uses.  If not set, then
 the qemu which was found at compile time by the configure script is
 used.
 
 the qemu which was found at compile time by the configure script is
 used.
 
+=item LIBGUESTFS_TRACE
+
+Set C<LIBGUESTFS_TRACE=1> to enable command traces.
+
 =item PAGER
 
 The C<more> command uses C<$PAGER> as the pager.  If not
 =item PAGER
 
 The C<more> command uses C<$PAGER> as the pager.  If not
index d8e4da3..b8379d0 100644 (file)
@@ -983,6 +983,11 @@ used.
 
 See also L</QEMU WRAPPERS> above.
 
 
 See also L</QEMU WRAPPERS> above.
 
+=item LIBGUESTFS_TRACE
+
+Set C<LIBGUESTFS_TRACE=1> to enable command traces.  This
+has the same effect as calling C<guestfs_set_trace (handle, 1)>.
+
 =item TMPDIR
 
 Location of temporary directory, defaults to C</tmp>.
 =item TMPDIR
 
 Location of temporary directory, defaults to C</tmp>.
index b0f9543..71aeeed 100755 (executable)
@@ -805,6 +805,32 @@ is passed to the appliance at boot time.  See C<guestfs_set_selinux>.
 For more information on the architecture of libguestfs,
 see L<guestfs(3)>.");
 
 For more information on the architecture of libguestfs,
 see L<guestfs(3)>.");
 
+  ("set_trace", (RErr, [Bool "trace"]), -1, [FishAlias "trace"],
+   [InitNone, Always, TestOutputTrue (
+      [["set_trace"; "true"];
+       ["get_trace"]])],
+   "enable or disable command traces",
+   "\
+If the command trace flag is set to 1, then commands are
+printed on stdout before they are executed in a format
+which is very similar to the one used by guestfish.  In
+other words, you can run a program with this enabled, and
+you will get out a script which you can feed to guestfish
+to perform the same set of actions.
+
+If you want to trace C API calls into libguestfs (and
+other libraries) then possibly a better way is to use
+the external ltrace(1) command.
+
+Command traces are disabled unless the environment variable
+C<LIBGUESTFS_TRACE> is defined and set to C<1>.");
+
+  ("get_trace", (RBool "trace", []), -1, [],
+   [],
+   "get command trace enabled flag",
+   "\
+Return the command trace flag.");
+
 ]
 
 (* daemon_functions are any functions which cause some action
 ]
 
 (* daemon_functions are any functions which cause some action
@@ -4643,6 +4669,52 @@ check_state (guestfs_h *g, const char *caller)
 
 ";
 
 
 ";
 
+  (* Generate code to generate guestfish call traces. *)
+  let trace_call shortname style =
+    pr "  if (guestfs__get_trace (g)) {\n";
+
+    let needs_i =
+      List.exists (function
+                  | StringList _ | DeviceList _ -> true
+                  | _ -> false) (snd style) in
+    if needs_i then (
+      pr "    int i;\n";
+      pr "\n"
+    );
+
+    pr "    printf (\"%s\");\n" shortname;
+    List.iter (
+      function
+      | String n                       (* strings *)
+      | Device n
+      | Pathname n
+      | Dev_or_Path n
+      | FileIn n
+      | FileOut n ->
+         (* guestfish doesn't support string escaping, so neither do we *)
+         pr "    printf (\" \\\"%%s\\\"\", %s);\n" n
+      | OptString n ->                 (* string option *)
+         pr "    if (%s) printf (\" \\\"%%s\\\"\", %s);\n" n n;
+         pr "    else printf (\" null\");\n"
+      | StringList n
+      | DeviceList n ->                        (* string list *)
+         pr "    putchar (' ');\n";
+         pr "    putchar ('\"');\n";
+         pr "    for (i = 0; %s[i]; ++i) {\n" n;
+         pr "      if (i > 0) putchar (' ');\n";
+         pr "      fputs (%s[i], stdout);\n" n;
+         pr "    }\n";
+         pr "    putchar ('\"');\n";
+      | Bool n ->                      (* boolean *)
+         pr "    fputs (%s ? \" true\" : \" false\", stdout);\n" n
+      | Int n ->                       (* int *)
+         pr "    printf (\" %%d\", %s);\n" n
+    ) (snd style);
+    pr "    putchar ('\\n');\n";
+    pr "  }\n";
+    pr "\n";
+  in
+
   (* For non-daemon functions, generate a wrapper around each function. *)
   List.iter (
     fun (shortname, style, _, _, _, _, _) ->
   (* For non-daemon functions, generate a wrapper around each function. *)
   List.iter (
     fun (shortname, style, _, _, _, _, _) ->
@@ -4651,6 +4723,7 @@ check_state (guestfs_h *g, const char *caller)
       generate_prototype ~extern:false ~semicolon:false ~newline:true
         ~handle:"g" name style;
       pr "{\n";
       generate_prototype ~extern:false ~semicolon:false ~newline:true
         ~handle:"g" name style;
       pr "{\n";
+      trace_call shortname style;
       pr "  return guestfs__%s " shortname;
       generate_c_call_args ~handle:"g" style;
       pr ";\n";
       pr "  return guestfs__%s " shortname;
       generate_c_call_args ~handle:"g" style;
       pr ";\n";
@@ -4758,6 +4831,7 @@ check_state (guestfs_h *g, const char *caller)
       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
       pr "  int serial;\n";
       pr "\n";
       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
       pr "  int serial;\n";
       pr "\n";
+      trace_call shortname style;
       pr "  if (check_state (g, \"%s\") == -1) return %s;\n" name error_code;
       pr "  guestfs_set_busy (g);\n";
       pr "\n";
       pr "  if (check_state (g, \"%s\") == -1) return %s;\n" name error_code;
       pr "  guestfs_set_busy (g);\n";
       pr "\n";
index 571205f..98d99b8 100644 (file)
@@ -170,6 +170,7 @@ struct guestfs_h
   int cmdline_size;
 
   int verbose;
   int cmdline_size;
 
   int verbose;
+  int trace;
   int autosync;
 
   char *path;                  /* Path to kernel, initrd. */
   int autosync;
 
   char *path;                  /* Path to kernel, initrd. */
@@ -238,6 +239,9 @@ guestfs_create (void)
   str = getenv ("LIBGUESTFS_DEBUG");
   g->verbose = str != NULL && strcmp (str, "1") == 0;
 
   str = getenv ("LIBGUESTFS_DEBUG");
   g->verbose = str != NULL && strcmp (str, "1") == 0;
 
+  str = getenv ("LIBGUESTFS_TRACE");
+  g->trace = str != NULL && strcmp (str, "1") == 0;
+
   str = getenv ("LIBGUESTFS_PATH");
   g->path = str != NULL ? strdup (str) : strdup (GUESTFS_DEFAULT_PATH);
   if (!g->path) goto error;
   str = getenv ("LIBGUESTFS_PATH");
   g->path = str != NULL ? strdup (str) : strdup (GUESTFS_DEFAULT_PATH);
   if (!g->path) goto error;
@@ -734,6 +738,19 @@ guestfs__version (guestfs_h *g)
   return r;
 }
 
   return r;
 }
 
+int
+guestfs__set_trace (guestfs_h *g, int t)
+{
+  g->trace = !!t;
+  return 0;
+}
+
+int
+guestfs__get_trace (guestfs_h *g)
+{
+  return g->trace;
+}
+
 /* Add a string to the current command line. */
 static void
 incr_cmdline_size (guestfs_h *g)
 /* Add a string to the current command line. */
 static void
 incr_cmdline_size (guestfs_h *g)