Add 'command' and 'command-lines'. Fix args freeing in Perl bindings.
authorRichard Jones <rjones@redhat.com>
Tue, 14 Apr 2009 12:51:12 +0000 (13:51 +0100)
committerRichard Jones <rjones@redhat.com>
Tue, 14 Apr 2009 12:51:12 +0000 (13:51 +0100)
20 files changed:
daemon/Makefile.am
daemon/actions.h
daemon/command.c [new file with mode: 0644]
daemon/stubs.c
fish/cmds.c
guestfish-actions.pod
guestfs-actions.pod
ocaml/guestfs.ml
ocaml/guestfs.mli
ocaml/guestfs_c_actions.c
perl/Guestfs.xs
perl/lib/Sys/Guestfs.pm
python/guestfs-py.c
python/guestfs.py
src/generator.ml
src/guestfs-actions.c
src/guestfs-actions.h
src/guestfs_protocol.c
src/guestfs_protocol.h
src/guestfs_protocol.x

index 400f164..1c52f7a 100644 (file)
@@ -21,6 +21,7 @@ noinst_PROGRAMS = guestfsd
 guestfsd_SOURCES = \
        actions.h \
        augeas.c \
 guestfsd_SOURCES = \
        actions.h \
        augeas.c \
+       command.c \
        daemon.h \
        devsparts.c \
        dir.c \
        daemon.h \
        devsparts.c \
        dir.c \
index d53729b..90aeb8d 100644 (file)
@@ -70,3 +70,5 @@ extern char **do_mounts (void);
 extern int do_umount_all (void);
 extern int do_lvm_remove_all (void);
 extern char *do_file (const char *path);
 extern int do_umount_all (void);
 extern int do_lvm_remove_all (void);
 extern char *do_file (const char *path);
+extern char *do_command (char * const* const arguments);
+extern char **do_command_lines (char * const* const arguments);
diff --git a/daemon/command.c b/daemon/command.c
new file mode 100644 (file)
index 0000000..589ca76
--- /dev/null
@@ -0,0 +1,101 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2009 Red Hat Inc. 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../src/guestfs_protocol.h"
+#include "daemon.h"
+#include "actions.h"
+
+char *
+do_command (char * const * const argv)
+{
+  char *out, *err;
+  int r;
+
+  /* We need a root filesystem mounted to do this. */
+  NEED_ROOT (NULL);
+
+  /* Conveniently, argv is already a NULL-terminated argv-style array
+   * of parameters, so we can pass it straight in to our internal
+   * commandv.  We just have to check the list is non-empty.
+   */
+  if (argv[0] == NULL) {
+    reply_with_error ("command: passed an empty list");
+    return NULL;
+  }
+
+  CHROOT_IN;
+  r = commandv (&out, &err, argv);
+  CHROOT_OUT;
+
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (out);
+    free (err);
+    return NULL;
+  }
+
+  free (err);
+
+  return out;                  /* Caller frees. */
+}
+
+char **
+do_command_lines (char * const * const argv)
+{
+  char *out;
+  char **lines = NULL;
+  int size = 0, alloc = 0;
+  char *p, *pend;
+
+  out = do_command (argv);
+  if (out == NULL)
+    return NULL;
+
+  /* Now convert the output to a list of lines. */
+  p = out;
+  while (p) {
+    pend = strchr (p, '\n');
+    if (pend) {
+      *pend = '\0';
+      pend++;
+    }
+
+    /* Final \n?  Don't return an empty final element. */
+    if (pend && *pend == '\0') break;
+
+    if (add_string (&lines, &size, &alloc, p) == -1) {
+      free (out);
+      return NULL;
+    }
+
+    p = pend;
+  }
+
+  free (out);
+
+  if (add_string (&lines, &size, &alloc, NULL) == -1)
+    return NULL;
+
+  return lines;
+}
index af0f14c..9a0dc91 100644 (file)
@@ -1164,6 +1164,65 @@ done:
   xdr_free ((xdrproc_t) xdr_guestfs_file_args, (char *) &args);
 }
 
   xdr_free ((xdrproc_t) xdr_guestfs_file_args, (char *) &args);
 }
 
+static void command_stub (XDR *xdr_in)
+{
+  char *r;
+  struct guestfs_command_args args;
+  char **arguments;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_command_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "command");
+    return;
+  }
+  args.arguments.arguments_val = realloc (args.arguments.arguments_val, sizeof (char *) * (args.arguments.arguments_len+1));
+  args.arguments.arguments_val[args.arguments.arguments_len] = NULL;
+  arguments = args.arguments.arguments_val;
+
+  r = do_command (arguments);
+  if (r == NULL)
+    /* do_command has already called reply_with_error */
+    goto done;
+
+  struct guestfs_command_ret ret;
+  ret.output = r;
+  reply ((xdrproc_t) &xdr_guestfs_command_ret, (char *) &ret);
+  free (r);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_command_args, (char *) &args);
+}
+
+static void command_lines_stub (XDR *xdr_in)
+{
+  char **r;
+  struct guestfs_command_lines_args args;
+  char **arguments;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_command_lines_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "command_lines");
+    return;
+  }
+  args.arguments.arguments_val = realloc (args.arguments.arguments_val, sizeof (char *) * (args.arguments.arguments_len+1));
+  args.arguments.arguments_val[args.arguments.arguments_len] = NULL;
+  arguments = args.arguments.arguments_val;
+
+  r = do_command_lines (arguments);
+  if (r == NULL)
+    /* do_command_lines has already called reply_with_error */
+    goto done;
+
+  struct guestfs_command_lines_ret ret;
+  ret.lines.lines_len = count_strings (r);
+  ret.lines.lines_val = r;
+  reply ((xdrproc_t) &xdr_guestfs_command_lines_ret, (char *) &ret);
+  free_strings (r);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_command_lines_args, (char *) &args);
+}
+
 void dispatch_incoming_message (XDR *xdr_in)
 {
   switch (proc_nr) {
 void dispatch_incoming_message (XDR *xdr_in)
 {
   switch (proc_nr) {
@@ -1314,6 +1373,12 @@ void dispatch_incoming_message (XDR *xdr_in)
     case GUESTFS_PROC_FILE:
       file_stub (xdr_in);
       break;
     case GUESTFS_PROC_FILE:
       file_stub (xdr_in);
       break;
+    case GUESTFS_PROC_COMMAND:
+      command_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_COMMAND_LINES:
+      command_lines_stub (xdr_in);
+      break;
     default:
       reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr);
   }
     default:
       reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr);
   }
index 1fb8b9d..2e536a5 100644 (file)
@@ -49,6 +49,8 @@ void list_commands (void)
   printf ("%-20s %s\n", "cat", "list the contents of a file");
   printf ("%-20s %s\n", "chmod", "change file mode");
   printf ("%-20s %s\n", "chown", "change file owner and group");
   printf ("%-20s %s\n", "cat", "list the contents of a file");
   printf ("%-20s %s\n", "chmod", "change file mode");
   printf ("%-20s %s\n", "chown", "change file owner and group");
+  printf ("%-20s %s\n", "command", "run a command from the guest filesystem");
+  printf ("%-20s %s\n", "command-lines", "run a command, returning lines");
   printf ("%-20s %s\n", "config", "add qemu parameters");
   printf ("%-20s %s\n", "exists", "test if file or directory exists");
   printf ("%-20s %s\n", "file", "determine file type");
   printf ("%-20s %s\n", "config", "add qemu parameters");
   printf ("%-20s %s\n", "exists", "test if file or directory exists");
   printf ("%-20s %s\n", "file", "determine file type");
@@ -276,6 +278,12 @@ void display_command (const char *cmd)
   if (strcasecmp (cmd, "file") == 0)
     pod2text ("file - determine file type", " file <path>\n\nThis call uses the standard L<file(1)> command to determine\nthe type or contents of the file.  This also works on devices,\nfor example to find out whether a partition contains a filesystem.\n\nThe exact command which runs is C<file -bsL path>.  Note in\nparticular that the filename is not prepended to the output\n(the C<-b> option).");
   else
   if (strcasecmp (cmd, "file") == 0)
     pod2text ("file - determine file type", " file <path>\n\nThis call uses the standard L<file(1)> command to determine\nthe type or contents of the file.  This also works on devices,\nfor example to find out whether a partition contains a filesystem.\n\nThe exact command which runs is C<file -bsL path>.  Note in\nparticular that the filename is not prepended to the output\n(the C<-b> option).");
   else
+  if (strcasecmp (cmd, "command") == 0)
+    pod2text ("command - run a command from the guest filesystem", " command <arguments>\n\nThis calls runs a command from the guest filesystem.  The\nfilesystem must be mounted, and must contain a compatible\noperating system (ie. something Linux, with the same\nor compatible processor architecture).\n\nThe single parameter is an argv-style list of arguments.\nThe first element is the name of the program to run.\nSubsequent elements are parameters.  The list must be\nnon-empty (ie. must contain a program name).\n\nThe C<$PATH> environment variable will contain at least\nC</usr/bin> and C</bin>.  If you require a program from\nanother location, you should provide the full path in the\nfirst parameter.\n\nShared libraries and data files required by the program\nmust be available on filesystems which are mounted in the\ncorrect places.  It is the caller's responsibility to ensure\nall filesystems that are needed are mounted at the right\nlocations.");
+  else
+  if (strcasecmp (cmd, "command_lines") == 0 || strcasecmp (cmd, "command-lines") == 0)
+    pod2text ("command-lines - run a command, returning lines", " command-lines <arguments>\n\nThis is the same as C<command>, but splits the\nresult into a list of lines.");
+  else
     display_builtin_command (cmd);
 }
 
     display_builtin_command (cmd);
 }
 
@@ -1298,6 +1306,40 @@ static int run_file (const char *cmd, int argc, char *argv[])
   return 0;
 }
 
   return 0;
 }
 
+static int run_command (const char *cmd, int argc, char *argv[])
+{
+  char *r;
+  char **arguments;
+  if (argc != 1) {
+    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  arguments = parse_string_list (argv[0]);
+  r = guestfs_command (g, arguments);
+  if (r == NULL) return -1;
+  printf ("%s\n", r);
+  free (r);
+  return 0;
+}
+
+static int run_command_lines (const char *cmd, int argc, char *argv[])
+{
+  char **r;
+  char **arguments;
+  if (argc != 1) {
+    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  arguments = parse_string_list (argv[0]);
+  r = guestfs_command_lines (g, arguments);
+  if (r == NULL) return -1;
+  print_strings (r);
+  free_strings (r);
+  return 0;
+}
+
 int run_action (const char *cmd, int argc, char *argv[])
 {
   if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0)
 int run_action (const char *cmd, int argc, char *argv[])
 {
   if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0)
@@ -1480,6 +1522,12 @@ int run_action (const char *cmd, int argc, char *argv[])
   if (strcasecmp (cmd, "file") == 0)
     return run_file (cmd, argc, argv);
   else
   if (strcasecmp (cmd, "file") == 0)
     return run_file (cmd, argc, argv);
   else
+  if (strcasecmp (cmd, "command") == 0)
+    return run_command (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "command_lines") == 0 || strcasecmp (cmd, "command-lines") == 0)
+    return run_command_lines (cmd, argc, argv);
+  else
     {
       fprintf (stderr, "%s: unknown command\n", cmd);
       return -1;
     {
       fprintf (stderr, "%s: unknown command\n", cmd);
       return -1;
index 606c5b9..2d0a045 100644 (file)
@@ -214,6 +214,38 @@ Only numeric uid and gid are supported.  If you want to use
 names, you will need to locate and parse the password file
 yourself (Augeas support makes this relatively easy).
 
 names, you will need to locate and parse the password file
 yourself (Augeas support makes this relatively easy).
 
+=head2 command
+
+ command arguments,...
+
+This calls runs a command from the guest filesystem.  The
+filesystem must be mounted, and must contain a compatible
+operating system (ie. something Linux, with the same
+or compatible processor architecture).
+
+The single parameter is an argv-style list of arguments.
+The first element is the name of the program to run.
+Subsequent elements are parameters.  The list must be
+non-empty (ie. must contain a program name).
+
+The C<$PATH> environment variable will contain at least
+C</usr/bin> and C</bin>.  If you require a program from
+another location, you should provide the full path in the
+first parameter.
+
+Shared libraries and data files required by the program
+must be available on filesystems which are mounted in the
+correct places.  It is the caller's responsibility to ensure
+all filesystems that are needed are mounted at the right
+locations.
+
+=head2 command-lines
+
+ command-lines arguments,...
+
+This is the same as C<command>, but splits the
+result into a list of lines.
+
 =head2 config
 
  config qemuparam qemuvalue
 =head2 config
 
  config qemuparam qemuvalue
index 32c6d67..551b3e3 100644 (file)
@@ -283,6 +283,47 @@ yourself (Augeas support makes this relatively easy).
 
 This function returns 0 on success or -1 on error.
 
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_command
+
+ char *guestfs_command (guestfs_h *handle,
+               char * const* const arguments);
+
+This calls runs a command from the guest filesystem.  The
+filesystem must be mounted, and must contain a compatible
+operating system (ie. something Linux, with the same
+or compatible processor architecture).
+
+The single parameter is an argv-style list of arguments.
+The first element is the name of the program to run.
+Subsequent elements are parameters.  The list must be
+non-empty (ie. must contain a program name).
+
+The C<$PATH> environment variable will contain at least
+C</usr/bin> and C</bin>.  If you require a program from
+another location, you should provide the full path in the
+first parameter.
+
+Shared libraries and data files required by the program
+must be available on filesystems which are mounted in the
+correct places.  It is the caller's responsibility to ensure
+all filesystems that are needed are mounted at the right
+locations.
+
+This function returns a string or NULL on error.
+I<The caller must free the returned string after use>.
+
+=head2 guestfs_command_lines
+
+ char **guestfs_command_lines (guestfs_h *handle,
+               char * const* const arguments);
+
+This is the same as C<guestfs_command>, but splits the
+result into a list of lines.
+
+This function returns a NULL-terminated array of strings
+(like L<environ(3)>), or NULL if there was an error.
+I<The caller must free the strings and the array after use>.
+
 =head2 guestfs_config
 
  int guestfs_config (guestfs_h *handle,
 =head2 guestfs_config
 
  int guestfs_config (guestfs_h *handle,
index 5330794..fa2e341 100644 (file)
@@ -146,3 +146,5 @@ external mounts : t -> string array = "ocaml_guestfs_mounts"
 external umount_all : t -> unit = "ocaml_guestfs_umount_all"
 external lvm_remove_all : t -> unit = "ocaml_guestfs_lvm_remove_all"
 external file : t -> string -> string = "ocaml_guestfs_file"
 external umount_all : t -> unit = "ocaml_guestfs_umount_all"
 external lvm_remove_all : t -> unit = "ocaml_guestfs_lvm_remove_all"
 external file : t -> string -> string = "ocaml_guestfs_file"
+external command : t -> string array -> string = "ocaml_guestfs_command"
+external command_lines : t -> string array -> string array = "ocaml_guestfs_command_lines"
index e3cee18..279ed4d 100644 (file)
@@ -277,3 +277,9 @@ val lvm_remove_all : t -> unit
 val file : t -> string -> string
 (** determine file type *)
 
 val file : t -> string -> string
 (** determine file type *)
 
+val command : t -> string array -> string
+(** run a command from the guest filesystem *)
+
+val command_lines : t -> string array -> string array
+(** run a command, returning lines *)
+
index decb838..356965d 100644 (file)
@@ -1689,3 +1689,55 @@ ocaml_guestfs_file (value gv, value pathv)
   CAMLreturn (rv);
 }
 
   CAMLreturn (rv);
 }
 
+CAMLprim value
+ocaml_guestfs_command (value gv, value argumentsv)
+{
+  CAMLparam2 (gv, argumentsv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("command: used handle after closing it");
+
+  char **arguments = ocaml_guestfs_strings_val (argumentsv);
+  char *r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_command (g, arguments);
+  caml_leave_blocking_section ();
+  ocaml_guestfs_free_strings (arguments);
+  if (r == NULL)
+    ocaml_guestfs_raise_error (g, "command");
+
+  rv = caml_copy_string (r);
+  free (r);
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_command_lines (value gv, value argumentsv)
+{
+  CAMLparam2 (gv, argumentsv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("command_lines: used handle after closing it");
+
+  char **arguments = ocaml_guestfs_strings_val (argumentsv);
+  int i;
+  char **r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_command_lines (g, arguments);
+  caml_leave_blocking_section ();
+  ocaml_guestfs_free_strings (arguments);
+  if (r == NULL)
+    ocaml_guestfs_raise_error (g, "command_lines");
+
+  rv = caml_copy_string_array ((const char **) r);
+  for (i = 0; r[i] != NULL; ++i) free (r[i]);
+  free (r);
+  CAMLreturn (rv);
+}
+
index 14a9225..2d564f4 100644 (file)
@@ -106,63 +106,77 @@ DESTROY (g)
 void
 launch (g)
       guestfs_h *g;
 void
 launch (g)
       guestfs_h *g;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_launch (g) == -1) {
+      r = guestfs_launch (g);
+      if (r == -1)
         croak ("launch: %s", guestfs_last_error (g));
         croak ("launch: %s", guestfs_last_error (g));
-      }
 
 void
 wait_ready (g)
       guestfs_h *g;
 
 void
 wait_ready (g)
       guestfs_h *g;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_wait_ready (g) == -1) {
+      r = guestfs_wait_ready (g);
+      if (r == -1)
         croak ("wait_ready: %s", guestfs_last_error (g));
         croak ("wait_ready: %s", guestfs_last_error (g));
-      }
 
 void
 kill_subprocess (g)
       guestfs_h *g;
 
 void
 kill_subprocess (g)
       guestfs_h *g;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_kill_subprocess (g) == -1) {
+      r = guestfs_kill_subprocess (g);
+      if (r == -1)
         croak ("kill_subprocess: %s", guestfs_last_error (g));
         croak ("kill_subprocess: %s", guestfs_last_error (g));
-      }
 
 void
 add_drive (g, filename)
       guestfs_h *g;
       char *filename;
 
 void
 add_drive (g, filename)
       guestfs_h *g;
       char *filename;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_add_drive (g, filename) == -1) {
+      r = guestfs_add_drive (g, filename);
+      if (r == -1)
         croak ("add_drive: %s", guestfs_last_error (g));
         croak ("add_drive: %s", guestfs_last_error (g));
-      }
 
 void
 add_cdrom (g, filename)
       guestfs_h *g;
       char *filename;
 
 void
 add_cdrom (g, filename)
       guestfs_h *g;
       char *filename;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_add_cdrom (g, filename) == -1) {
+      r = guestfs_add_cdrom (g, filename);
+      if (r == -1)
         croak ("add_cdrom: %s", guestfs_last_error (g));
         croak ("add_cdrom: %s", guestfs_last_error (g));
-      }
 
 void
 config (g, qemuparam, qemuvalue)
       guestfs_h *g;
       char *qemuparam;
       char *qemuvalue;
 
 void
 config (g, qemuparam, qemuvalue)
       guestfs_h *g;
       char *qemuparam;
       char *qemuvalue;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_config (g, qemuparam, qemuvalue) == -1) {
+      r = guestfs_config (g, qemuparam, qemuvalue);
+      if (r == -1)
         croak ("config: %s", guestfs_last_error (g));
         croak ("config: %s", guestfs_last_error (g));
-      }
 
 void
 set_path (g, path)
       guestfs_h *g;
       char *path;
 
 void
 set_path (g, path)
       guestfs_h *g;
       char *path;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_set_path (g, path) == -1) {
+      r = guestfs_set_path (g, path);
+      if (r == -1)
         croak ("set_path: %s", guestfs_last_error (g));
         croak ("set_path: %s", guestfs_last_error (g));
-      }
 
 SV *
 get_path (g)
 
 SV *
 get_path (g)
@@ -182,10 +196,12 @@ void
 set_autosync (g, autosync)
       guestfs_h *g;
       int autosync;
 set_autosync (g, autosync)
       guestfs_h *g;
       int autosync;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_set_autosync (g, autosync) == -1) {
+      r = guestfs_set_autosync (g, autosync);
+      if (r == -1)
         croak ("set_autosync: %s", guestfs_last_error (g));
         croak ("set_autosync: %s", guestfs_last_error (g));
-      }
 
 SV *
 get_autosync (g)
 
 SV *
 get_autosync (g)
@@ -205,10 +221,12 @@ void
 set_verbose (g, verbose)
       guestfs_h *g;
       int verbose;
 set_verbose (g, verbose)
       guestfs_h *g;
       int verbose;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_set_verbose (g, verbose) == -1) {
+      r = guestfs_set_verbose (g, verbose);
+      if (r == -1)
         croak ("set_verbose: %s", guestfs_last_error (g));
         croak ("set_verbose: %s", guestfs_last_error (g));
-      }
 
 SV *
 get_verbose (g)
 
 SV *
 get_verbose (g)
@@ -229,27 +247,33 @@ mount (g, device, mountpoint)
       guestfs_h *g;
       char *device;
       char *mountpoint;
       guestfs_h *g;
       char *device;
       char *mountpoint;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_mount (g, device, mountpoint) == -1) {
+      r = guestfs_mount (g, device, mountpoint);
+      if (r == -1)
         croak ("mount: %s", guestfs_last_error (g));
         croak ("mount: %s", guestfs_last_error (g));
-      }
 
 void
 sync (g)
       guestfs_h *g;
 
 void
 sync (g)
       guestfs_h *g;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_sync (g) == -1) {
+      r = guestfs_sync (g);
+      if (r == -1)
         croak ("sync: %s", guestfs_last_error (g));
         croak ("sync: %s", guestfs_last_error (g));
-      }
 
 void
 touch (g, path)
       guestfs_h *g;
       char *path;
 
 void
 touch (g, path)
       guestfs_h *g;
       char *path;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_touch (g, path) == -1) {
+      r = guestfs_touch (g, path);
+      if (r == -1)
         croak ("touch: %s", guestfs_last_error (g));
         croak ("touch: %s", guestfs_last_error (g));
-      }
 
 SV *
 cat (g, path)
 
 SV *
 cat (g, path)
@@ -407,8 +431,9 @@ PREINIT:
       HV *hv;
  PPCODE:
       physvols = guestfs_pvs_full (g);
       HV *hv;
  PPCODE:
       physvols = guestfs_pvs_full (g);
-      if (physvols == NULL)
+      if (physvols == NULL) {
         croak ("pvs_full: %s", guestfs_last_error (g));
         croak ("pvs_full: %s", guestfs_last_error (g));
+      }
       EXTEND (SP, physvols->len);
       for (i = 0; i < physvols->len; ++i) {
         hv = newHV ();
       EXTEND (SP, physvols->len);
       for (i = 0; i < physvols->len; ++i) {
         hv = newHV ();
@@ -439,8 +464,9 @@ PREINIT:
       HV *hv;
  PPCODE:
       volgroups = guestfs_vgs_full (g);
       HV *hv;
  PPCODE:
       volgroups = guestfs_vgs_full (g);
-      if (volgroups == NULL)
+      if (volgroups == NULL) {
         croak ("vgs_full: %s", guestfs_last_error (g));
         croak ("vgs_full: %s", guestfs_last_error (g));
+      }
       EXTEND (SP, volgroups->len);
       for (i = 0; i < volgroups->len; ++i) {
         hv = newHV ();
       EXTEND (SP, volgroups->len);
       for (i = 0; i < volgroups->len; ++i) {
         hv = newHV ();
@@ -476,8 +502,9 @@ PREINIT:
       HV *hv;
  PPCODE:
       logvols = guestfs_lvs_full (g);
       HV *hv;
  PPCODE:
       logvols = guestfs_lvs_full (g);
-      if (logvols == NULL)
+      if (logvols == NULL) {
         croak ("lvs_full: %s", guestfs_last_error (g));
         croak ("lvs_full: %s", guestfs_last_error (g));
+      }
       EXTEND (SP, logvols->len);
       for (i = 0; i < logvols->len; ++i) {
         hv = newHV ();
       EXTEND (SP, logvols->len);
       for (i = 0; i < logvols->len; ++i) {
         hv = newHV ();
@@ -526,18 +553,22 @@ aug_init (g, root, flags)
       guestfs_h *g;
       char *root;
       int flags;
       guestfs_h *g;
       char *root;
       int flags;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_aug_init (g, root, flags) == -1) {
+      r = guestfs_aug_init (g, root, flags);
+      if (r == -1)
         croak ("aug_init: %s", guestfs_last_error (g));
         croak ("aug_init: %s", guestfs_last_error (g));
-      }
 
 void
 aug_close (g)
       guestfs_h *g;
 
 void
 aug_close (g)
       guestfs_h *g;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_aug_close (g) == -1) {
+      r = guestfs_aug_close (g);
+      if (r == -1)
         croak ("aug_close: %s", guestfs_last_error (g));
         croak ("aug_close: %s", guestfs_last_error (g));
-      }
 
 SV *
 aug_defvar (g, name, expr)
 
 SV *
 aug_defvar (g, name, expr)
@@ -594,10 +625,12 @@ aug_set (g, path, val)
       guestfs_h *g;
       char *path;
       char *val;
       guestfs_h *g;
       char *path;
       char *val;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_aug_set (g, path, val) == -1) {
+      r = guestfs_aug_set (g, path, val);
+      if (r == -1)
         croak ("aug_set: %s", guestfs_last_error (g));
         croak ("aug_set: %s", guestfs_last_error (g));
-      }
 
 void
 aug_insert (g, path, label, before)
 
 void
 aug_insert (g, path, label, before)
@@ -605,10 +638,12 @@ aug_insert (g, path, label, before)
       char *path;
       char *label;
       int before;
       char *path;
       char *label;
       int before;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_aug_insert (g, path, label, before) == -1) {
+      r = guestfs_aug_insert (g, path, label, before);
+      if (r == -1)
         croak ("aug_insert: %s", guestfs_last_error (g));
         croak ("aug_insert: %s", guestfs_last_error (g));
-      }
 
 SV *
 aug_rm (g, path)
 
 SV *
 aug_rm (g, path)
@@ -630,10 +665,12 @@ aug_mv (g, src, dest)
       guestfs_h *g;
       char *src;
       char *dest;
       guestfs_h *g;
       char *src;
       char *dest;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_aug_mv (g, src, dest) == -1) {
+      r = guestfs_aug_mv (g, src, dest);
+      if (r == -1)
         croak ("aug_mv: %s", guestfs_last_error (g));
         croak ("aug_mv: %s", guestfs_last_error (g));
-      }
 
 void
 aug_match (g, path)
 
 void
 aug_match (g, path)
@@ -658,18 +695,22 @@ PREINIT:
 void
 aug_save (g)
       guestfs_h *g;
 void
 aug_save (g)
       guestfs_h *g;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_aug_save (g) == -1) {
+      r = guestfs_aug_save (g);
+      if (r == -1)
         croak ("aug_save: %s", guestfs_last_error (g));
         croak ("aug_save: %s", guestfs_last_error (g));
-      }
 
 void
 aug_load (g)
       guestfs_h *g;
 
 void
 aug_load (g)
       guestfs_h *g;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_aug_load (g) == -1) {
+      r = guestfs_aug_load (g);
+      if (r == -1)
         croak ("aug_load: %s", guestfs_last_error (g));
         croak ("aug_load: %s", guestfs_last_error (g));
-      }
 
 void
 aug_ls (g, path)
 
 void
 aug_ls (g, path)
@@ -695,56 +736,68 @@ void
 rm (g, path)
       guestfs_h *g;
       char *path;
 rm (g, path)
       guestfs_h *g;
       char *path;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_rm (g, path) == -1) {
+      r = guestfs_rm (g, path);
+      if (r == -1)
         croak ("rm: %s", guestfs_last_error (g));
         croak ("rm: %s", guestfs_last_error (g));
-      }
 
 void
 rmdir (g, path)
       guestfs_h *g;
       char *path;
 
 void
 rmdir (g, path)
       guestfs_h *g;
       char *path;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_rmdir (g, path) == -1) {
+      r = guestfs_rmdir (g, path);
+      if (r == -1)
         croak ("rmdir: %s", guestfs_last_error (g));
         croak ("rmdir: %s", guestfs_last_error (g));
-      }
 
 void
 rm_rf (g, path)
       guestfs_h *g;
       char *path;
 
 void
 rm_rf (g, path)
       guestfs_h *g;
       char *path;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_rm_rf (g, path) == -1) {
+      r = guestfs_rm_rf (g, path);
+      if (r == -1)
         croak ("rm_rf: %s", guestfs_last_error (g));
         croak ("rm_rf: %s", guestfs_last_error (g));
-      }
 
 void
 mkdir (g, path)
       guestfs_h *g;
       char *path;
 
 void
 mkdir (g, path)
       guestfs_h *g;
       char *path;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_mkdir (g, path) == -1) {
+      r = guestfs_mkdir (g, path);
+      if (r == -1)
         croak ("mkdir: %s", guestfs_last_error (g));
         croak ("mkdir: %s", guestfs_last_error (g));
-      }
 
 void
 mkdir_p (g, path)
       guestfs_h *g;
       char *path;
 
 void
 mkdir_p (g, path)
       guestfs_h *g;
       char *path;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_mkdir_p (g, path) == -1) {
+      r = guestfs_mkdir_p (g, path);
+      if (r == -1)
         croak ("mkdir_p: %s", guestfs_last_error (g));
         croak ("mkdir_p: %s", guestfs_last_error (g));
-      }
 
 void
 chmod (g, mode, path)
       guestfs_h *g;
       int mode;
       char *path;
 
 void
 chmod (g, mode, path)
       guestfs_h *g;
       int mode;
       char *path;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_chmod (g, mode, path) == -1) {
+      r = guestfs_chmod (g, mode, path);
+      if (r == -1)
         croak ("chmod: %s", guestfs_last_error (g));
         croak ("chmod: %s", guestfs_last_error (g));
-      }
 
 void
 chown (g, owner, group, path)
 
 void
 chown (g, owner, group, path)
@@ -752,10 +805,12 @@ chown (g, owner, group, path)
       int owner;
       int group;
       char *path;
       int owner;
       int group;
       char *path;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_chown (g, owner, group, path) == -1) {
+      r = guestfs_chown (g, owner, group, path);
+      if (r == -1)
         croak ("chown: %s", guestfs_last_error (g));
         croak ("chown: %s", guestfs_last_error (g));
-      }
 
 SV *
 exists (g, path)
 
 SV *
 exists (g, path)
@@ -806,22 +861,25 @@ void
 pvcreate (g, device)
       guestfs_h *g;
       char *device;
 pvcreate (g, device)
       guestfs_h *g;
       char *device;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_pvcreate (g, device) == -1) {
+      r = guestfs_pvcreate (g, device);
+      if (r == -1)
         croak ("pvcreate: %s", guestfs_last_error (g));
         croak ("pvcreate: %s", guestfs_last_error (g));
-      }
 
 void
 vgcreate (g, volgroup, physvols)
       guestfs_h *g;
       char *volgroup;
       char **physvols;
 
 void
 vgcreate (g, volgroup, physvols)
       guestfs_h *g;
       char *volgroup;
       char **physvols;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_vgcreate (g, volgroup, physvols) == -1) {
-        free (physvols);
+      r = guestfs_vgcreate (g, volgroup, physvols);
+      free (physvols);
+      if (r == -1)
         croak ("vgcreate: %s", guestfs_last_error (g));
         croak ("vgcreate: %s", guestfs_last_error (g));
-      }
-        free (physvols);
 
 void
 lvcreate (g, logvol, volgroup, mbytes)
 
 void
 lvcreate (g, logvol, volgroup, mbytes)
@@ -829,20 +887,24 @@ lvcreate (g, logvol, volgroup, mbytes)
       char *logvol;
       char *volgroup;
       int mbytes;
       char *logvol;
       char *volgroup;
       int mbytes;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_lvcreate (g, logvol, volgroup, mbytes) == -1) {
+      r = guestfs_lvcreate (g, logvol, volgroup, mbytes);
+      if (r == -1)
         croak ("lvcreate: %s", guestfs_last_error (g));
         croak ("lvcreate: %s", guestfs_last_error (g));
-      }
 
 void
 mkfs (g, fstype, device)
       guestfs_h *g;
       char *fstype;
       char *device;
 
 void
 mkfs (g, fstype, device)
       guestfs_h *g;
       char *fstype;
       char *device;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_mkfs (g, fstype, device) == -1) {
+      r = guestfs_mkfs (g, fstype, device);
+      if (r == -1)
         croak ("mkfs: %s", guestfs_last_error (g));
         croak ("mkfs: %s", guestfs_last_error (g));
-      }
 
 void
 sfdisk (g, device, cyls, heads, sectors, lines)
 
 void
 sfdisk (g, device, cyls, heads, sectors, lines)
@@ -852,12 +914,13 @@ sfdisk (g, device, cyls, heads, sectors, lines)
       int heads;
       int sectors;
       char **lines;
       int heads;
       int sectors;
       char **lines;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_sfdisk (g, device, cyls, heads, sectors, lines) == -1) {
-        free (lines);
+      r = guestfs_sfdisk (g, device, cyls, heads, sectors, lines);
+      free (lines);
+      if (r == -1)
         croak ("sfdisk: %s", guestfs_last_error (g));
         croak ("sfdisk: %s", guestfs_last_error (g));
-      }
-        free (lines);
 
 void
 write_file (g, path, content, size)
 
 void
 write_file (g, path, content, size)
@@ -865,19 +928,23 @@ write_file (g, path, content, size)
       char *path;
       char *content;
       int size;
       char *path;
       char *content;
       int size;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_write_file (g, path, content, size) == -1) {
+      r = guestfs_write_file (g, path, content, size);
+      if (r == -1)
         croak ("write_file: %s", guestfs_last_error (g));
         croak ("write_file: %s", guestfs_last_error (g));
-      }
 
 void
 umount (g, pathordevice)
       guestfs_h *g;
       char *pathordevice;
 
 void
 umount (g, pathordevice)
       guestfs_h *g;
       char *pathordevice;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_umount (g, pathordevice) == -1) {
+      r = guestfs_umount (g, pathordevice);
+      if (r == -1)
         croak ("umount: %s", guestfs_last_error (g));
         croak ("umount: %s", guestfs_last_error (g));
-      }
 
 void
 mounts (g)
 
 void
 mounts (g)
@@ -901,18 +968,22 @@ PREINIT:
 void
 umount_all (g)
       guestfs_h *g;
 void
 umount_all (g)
       guestfs_h *g;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_umount_all (g) == -1) {
+      r = guestfs_umount_all (g);
+      if (r == -1)
         croak ("umount_all: %s", guestfs_last_error (g));
         croak ("umount_all: %s", guestfs_last_error (g));
-      }
 
 void
 lvm_remove_all (g)
       guestfs_h *g;
 
 void
 lvm_remove_all (g)
       guestfs_h *g;
+PREINIT:
+      int r;
  PPCODE:
  PPCODE:
-      if (guestfs_lvm_remove_all (g) == -1) {
+      r = guestfs_lvm_remove_all (g);
+      if (r == -1)
         croak ("lvm_remove_all: %s", guestfs_last_error (g));
         croak ("lvm_remove_all: %s", guestfs_last_error (g));
-      }
 
 SV *
 file (g, path)
 
 SV *
 file (g, path)
@@ -930,3 +1001,41 @@ PREINIT:
  OUTPUT:
       RETVAL
 
  OUTPUT:
       RETVAL
 
+SV *
+command (g, arguments)
+      guestfs_h *g;
+      char **arguments;
+PREINIT:
+      char *output;
+   CODE:
+      output = guestfs_command (g, arguments);
+      free (arguments);
+      if (output == NULL) {
+        croak ("command: %s", guestfs_last_error (g));
+      }
+      RETVAL = newSVpv (output, 0);
+      free (output);
+ OUTPUT:
+      RETVAL
+
+void
+command_lines (g, arguments)
+      guestfs_h *g;
+      char **arguments;
+PREINIT:
+      char **lines;
+      int i, n;
+ PPCODE:
+      lines = guestfs_command_lines (g, arguments);
+      free (arguments);
+      if (lines == NULL) {
+        croak ("command_lines: %s", guestfs_last_error (g));
+      }
+      for (n = 0; lines[n] != NULL; ++n) /**/;
+      EXTEND (SP, n);
+      for (i = 0; i < n; ++i) {
+        PUSHs (sv_2mortal (newSVpv (lines[i], 0)));
+        free (lines[i]);
+      }
+      free (lines);
+
index 9d050b4..17ab740 100644 (file)
@@ -271,6 +271,34 @@ Only numeric uid and gid are supported.  If you want to use
 names, you will need to locate and parse the password file
 yourself (Augeas support makes this relatively easy).
 
 names, you will need to locate and parse the password file
 yourself (Augeas support makes this relatively easy).
 
+=item $output = $h->command (\@arguments);
+
+This calls runs a command from the guest filesystem.  The
+filesystem must be mounted, and must contain a compatible
+operating system (ie. something Linux, with the same
+or compatible processor architecture).
+
+The single parameter is an argv-style list of arguments.
+The first element is the name of the program to run.
+Subsequent elements are parameters.  The list must be
+non-empty (ie. must contain a program name).
+
+The C<$PATH> environment variable will contain at least
+C</usr/bin> and C</bin>.  If you require a program from
+another location, you should provide the full path in the
+first parameter.
+
+Shared libraries and data files required by the program
+must be available on filesystems which are mounted in the
+correct places.  It is the caller's responsibility to ensure
+all filesystems that are needed are mounted at the right
+locations.
+
+=item @lines = $h->command_lines (\@arguments);
+
+This is the same as C<$h-E<gt>command>, but splits the
+result into a list of lines.
+
 =item $h->config ($qemuparam, $qemuvalue);
 
 This can be used to add arbitrary qemu command line parameters
 =item $h->config ($qemuparam, $qemuvalue);
 
 This can be used to add arbitrary qemu command line parameters
index d3d6999..8c7ad22 100644 (file)
@@ -1831,6 +1831,64 @@ py_guestfs_file (PyObject *self, PyObject *args)
   return py_r;
 }
 
   return py_r;
 }
 
+static PyObject *
+py_guestfs_command (PyObject *self, PyObject *args)
+{
+  PyObject *py_g;
+  guestfs_h *g;
+  PyObject *py_r;
+  char *r;
+  PyObject *py_arguments;
+  const char **arguments;
+
+  if (!PyArg_ParseTuple (args, (char *) "OO:guestfs_command",
+                         &py_g, &py_arguments))
+    return NULL;
+  g = get_handle (py_g);
+  arguments = get_string_list (py_arguments);
+  if (!arguments) return NULL;
+
+  r = guestfs_command (g, arguments);
+  free (arguments);
+  if (r == NULL) {
+    PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g));
+    return NULL;
+  }
+
+  py_r = PyString_FromString (r);
+  free (r);
+  return py_r;
+}
+
+static PyObject *
+py_guestfs_command_lines (PyObject *self, PyObject *args)
+{
+  PyObject *py_g;
+  guestfs_h *g;
+  PyObject *py_r;
+  char **r;
+  PyObject *py_arguments;
+  const char **arguments;
+
+  if (!PyArg_ParseTuple (args, (char *) "OO:guestfs_command_lines",
+                         &py_g, &py_arguments))
+    return NULL;
+  g = get_handle (py_g);
+  arguments = get_string_list (py_arguments);
+  if (!arguments) return NULL;
+
+  r = guestfs_command_lines (g, arguments);
+  free (arguments);
+  if (r == NULL) {
+    PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g));
+    return NULL;
+  }
+
+  py_r = put_string_list (r);
+  free_strings (r);
+  return py_r;
+}
+
 static PyMethodDef methods[] = {
   { (char *) "create", py_guestfs_create, METH_VARARGS, NULL },
   { (char *) "close", py_guestfs_close, METH_VARARGS, NULL },
 static PyMethodDef methods[] = {
   { (char *) "create", py_guestfs_create, METH_VARARGS, NULL },
   { (char *) "close", py_guestfs_close, METH_VARARGS, NULL },
@@ -1895,6 +1953,8 @@ static PyMethodDef methods[] = {
   { (char *) "umount_all", py_guestfs_umount_all, METH_VARARGS, NULL },
   { (char *) "lvm_remove_all", py_guestfs_lvm_remove_all, METH_VARARGS, NULL },
   { (char *) "file", py_guestfs_file, METH_VARARGS, NULL },
   { (char *) "umount_all", py_guestfs_umount_all, METH_VARARGS, NULL },
   { (char *) "lvm_remove_all", py_guestfs_lvm_remove_all, METH_VARARGS, NULL },
   { (char *) "file", py_guestfs_file, METH_VARARGS, NULL },
+  { (char *) "command", py_guestfs_command, METH_VARARGS, NULL },
+  { (char *) "command_lines", py_guestfs_command_lines, METH_VARARGS, NULL },
   { NULL, NULL, 0, NULL }
 };
 
   { NULL, NULL, 0, NULL }
 };
 
index d4cd46d..6cf7e19 100644 (file)
@@ -210,3 +210,9 @@ class GuestFS:
     def file (self, path):
         return libguestfsmod.file (self._o, path)
 
     def file (self, path):
         return libguestfsmod.file (self._o, path)
 
+    def command (self, arguments):
+        return libguestfsmod.command (self._o, arguments)
+
+    def command_lines (self, arguments):
+        return libguestfsmod.command_lines (self._o, arguments)
+
index c9da57e..1017ad1 100755 (executable)
@@ -969,6 +969,38 @@ The exact command which runs is C<file -bsL path>.  Note in
 particular that the filename is not prepended to the output
 (the C<-b> option).");
 
 particular that the filename is not prepended to the output
 (the C<-b> option).");
 
+  ("command", (RString "output", [StringList "arguments"]), 50, [],
+   [], (* XXX how to test? *)
+   "run a command from the guest filesystem",
+   "\
+This calls runs a command from the guest filesystem.  The
+filesystem must be mounted, and must contain a compatible
+operating system (ie. something Linux, with the same
+or compatible processor architecture).
+
+The single parameter is an argv-style list of arguments.
+The first element is the name of the program to run.
+Subsequent elements are parameters.  The list must be
+non-empty (ie. must contain a program name).
+
+The C<$PATH> environment variable will contain at least
+C</usr/bin> and C</bin>.  If you require a program from
+another location, you should provide the full path in the
+first parameter.
+
+Shared libraries and data files required by the program
+must be available on filesystems which are mounted in the
+correct places.  It is the caller's responsibility to ensure
+all filesystems that are needed are mounted at the right
+locations.");
+
+  ("command_lines", (RStringList "lines", [StringList "arguments"]), 51, [],
+   [], (* XXX how to test? *)
+   "run a command, returning lines",
+   "\
+This is the same as C<guestfs_command>, but splits the
+result into a list of lines.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
@@ -1163,7 +1195,9 @@ let check_functions () =
          failwithf "%s param/ret %s should not contain '-' or '_'"
            name n;
        if n = "value" then
          failwithf "%s param/ret %s should not contain '-' or '_'"
            name n;
        if n = "value" then
-         failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" n
+         failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" n;
+       if n = "argv" || n = "args" then
+         failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" n
       in
 
       (match fst style with
       in
 
       (match fst style with
@@ -3351,20 +3385,22 @@ DESTROY (g)
          | OptString _
          | Bool _
          | Int _ -> ()
          | OptString _
          | Bool _
          | Int _ -> ()
-         | StringList n -> pr "        free (%s);\n" n
+         | StringList n -> pr "      free (%s);\n" n
        ) (snd style)
       in
 
       (* Code. *)
       (match fst style with
        | RErr ->
        ) (snd style)
       in
 
       (* Code. *)
       (match fst style with
        | RErr ->
+          pr "PREINIT:\n";
+          pr "      int r;\n";
           pr " PPCODE:\n";
           pr " PPCODE:\n";
-          pr "      if (guestfs_%s " name;
+          pr "      r = guestfs_%s " name;
           generate_call_args ~handle:"g" style;
           generate_call_args ~handle:"g" style;
-          pr " == -1) {\n";
+          pr ";\n";
           do_cleanups ();
           do_cleanups ();
+          pr "      if (r == -1)\n";
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n"
        | RInt n
        | RBool n ->
           pr "PREINIT:\n";
        | RInt n
        | RBool n ->
           pr "PREINIT:\n";
@@ -3373,10 +3409,9 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == -1) {\n" n;
           do_cleanups ();
           do_cleanups ();
+          pr "      if (%s == -1)\n" n;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n";
           pr "      RETVAL = newSViv (%s);\n" n;
           pr " OUTPUT:\n";
           pr "      RETVAL\n"
           pr "      RETVAL = newSViv (%s);\n" n;
           pr " OUTPUT:\n";
           pr "      RETVAL\n"
@@ -3387,10 +3422,9 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == NULL) {\n" n;
           do_cleanups ();
           do_cleanups ();
+          pr "      if (%s == NULL)\n" n;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n";
           pr "      RETVAL = newSVpv (%s, 0);\n" n;
           pr " OUTPUT:\n";
           pr "      RETVAL\n"
           pr "      RETVAL = newSVpv (%s, 0);\n" n;
           pr " OUTPUT:\n";
           pr "      RETVAL\n"
@@ -3401,10 +3435,9 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == NULL) {\n" n;
           do_cleanups ();
           do_cleanups ();
+          pr "      if (%s == NULL)\n" n;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n";
           pr "      RETVAL = newSVpv (%s, 0);\n" n;
           pr "      free (%s);\n" n;
           pr " OUTPUT:\n";
           pr "      RETVAL = newSVpv (%s, 0);\n" n;
           pr "      free (%s);\n" n;
           pr " OUTPUT:\n";
@@ -3417,10 +3450,9 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == NULL) {\n" n;
           do_cleanups ();
           do_cleanups ();
+          pr "      if (%s == NULL)\n" n;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n";
           pr "      for (n = 0; %s[n] != NULL; ++n) /**/;\n" n;
           pr "      EXTEND (SP, n);\n";
           pr "      for (i = 0; i < n; ++i) {\n";
           pr "      for (n = 0; %s[n] != NULL; ++n) /**/;\n" n;
           pr "      EXTEND (SP, n);\n";
           pr "      for (i = 0; i < n; ++i) {\n";
@@ -3435,28 +3467,25 @@ DESTROY (g)
           pr "      r = guestfs_%s " name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
           pr "      r = guestfs_%s " name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (r == NULL) {\n";
           do_cleanups ();
           do_cleanups ();
+          pr "      if (r == NULL)\n";
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n";
           pr "      EXTEND (SP, 2);\n";
           pr "      PUSHs (sv_2mortal (newSViv (r->i)));\n";
           pr "      PUSHs (sv_2mortal (newSViv (r->b)));\n";
           pr "      guestfs_free_int_bool (r);\n";
        | RPVList n ->
           pr "      EXTEND (SP, 2);\n";
           pr "      PUSHs (sv_2mortal (newSViv (r->i)));\n";
           pr "      PUSHs (sv_2mortal (newSViv (r->b)));\n";
           pr "      guestfs_free_int_bool (r);\n";
        | RPVList n ->
-          generate_perl_lvm_code "pv" pv_cols name style n;
+          generate_perl_lvm_code "pv" pv_cols name style n do_cleanups;
        | RVGList n ->
        | RVGList n ->
-          generate_perl_lvm_code "vg" vg_cols name style n;
+          generate_perl_lvm_code "vg" vg_cols name style n do_cleanups;
        | RLVList n ->
        | RLVList n ->
-          generate_perl_lvm_code "lv" lv_cols name style n;
+          generate_perl_lvm_code "lv" lv_cols name style n do_cleanups;
       );
 
       );
 
-      do_cleanups ();
-
       pr "\n"
   ) all_functions
 
       pr "\n"
   ) all_functions
 
-and generate_perl_lvm_code typ cols name style n =
+and generate_perl_lvm_code typ cols name style n do_cleanups =
   pr "PREINIT:\n";
   pr "      struct guestfs_lvm_%s_list *%s;\n" typ n;
   pr "      int i;\n";
   pr "PREINIT:\n";
   pr "      struct guestfs_lvm_%s_list *%s;\n" typ n;
   pr "      int i;\n";
@@ -3465,6 +3494,7 @@ and generate_perl_lvm_code typ cols name style n =
   pr "      %s = guestfs_%s " n name;
   generate_call_args ~handle:"g" style;
   pr ";\n";
   pr "      %s = guestfs_%s " n name;
   generate_call_args ~handle:"g" style;
   pr ";\n";
+  do_cleanups ();
   pr "      if (%s == NULL)\n" n;
   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
   pr "      EXTEND (SP, %s->len);\n" n;
   pr "      if (%s == NULL)\n" n;
   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
   pr "      EXTEND (SP, %s->len);\n" n;
index 4b6b982..c6f574a 100644 (file)
@@ -3545,3 +3545,157 @@ char *guestfs_file (guestfs_h *g,
   return rv.ret.description; /* caller will free */
 }
 
   return rv.ret.description; /* caller will free */
 }
 
+struct command_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_command_ret ret;
+};
+
+static void command_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct command_rv *rv = (struct command_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_command: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_command: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_command_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_command: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+char *guestfs_command (guestfs_h *g,
+               char * const* const arguments)
+{
+  struct guestfs_command_args args;
+  struct command_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_command called from the wrong state, %d != READY",
+      g->state);
+    return NULL;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.arguments.arguments_val = (char **) arguments;
+  for (args.arguments.arguments_len = 0; arguments[args.arguments.arguments_len]; args.arguments.arguments_len++) ;
+  serial = dispatch (g, GUESTFS_PROC_COMMAND,
+                     (xdrproc_t) xdr_guestfs_command_args, (char *) &args);
+  if (serial == -1)
+    return NULL;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = command_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_command failed, see earlier error messages");
+    return NULL;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_COMMAND, serial) == -1)
+    return NULL;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return NULL;
+  }
+
+  return rv.ret.output; /* caller will free */
+}
+
+struct command_lines_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_command_lines_ret ret;
+};
+
+static void command_lines_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct command_lines_rv *rv = (struct command_lines_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_command_lines: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_command_lines: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_command_lines_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_command_lines: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+char **guestfs_command_lines (guestfs_h *g,
+               char * const* const arguments)
+{
+  struct guestfs_command_lines_args args;
+  struct command_lines_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_command_lines called from the wrong state, %d != READY",
+      g->state);
+    return NULL;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.arguments.arguments_val = (char **) arguments;
+  for (args.arguments.arguments_len = 0; arguments[args.arguments.arguments_len]; args.arguments.arguments_len++) ;
+  serial = dispatch (g, GUESTFS_PROC_COMMAND_LINES,
+                     (xdrproc_t) xdr_guestfs_command_lines_args, (char *) &args);
+  if (serial == -1)
+    return NULL;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = command_lines_cb;
+  g->reply_cb_internal_data = &rv;
+  main_loop.main_loop_run (g);
+  g->reply_cb_internal = NULL;
+  g->reply_cb_internal_data = NULL;
+  if (!rv.cb_done) {
+    error (g, "guestfs_command_lines failed, see earlier error messages");
+    return NULL;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_COMMAND_LINES, serial) == -1)
+    return NULL;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return NULL;
+  }
+
+  /* caller will free this, but we need to add a NULL entry */
+  rv.ret.lines.lines_val =    safe_realloc (g, rv.ret.lines.lines_val,
+                  sizeof (char *) * (rv.ret.lines.lines_len + 1));
+  rv.ret.lines.lines_val[rv.ret.lines.lines_len] = NULL;
+  return rv.ret.lines.lines_val;
+}
+
index 7373964..06efabf 100644 (file)
@@ -80,3 +80,5 @@ extern char **guestfs_mounts (guestfs_h *handle);
 extern int guestfs_umount_all (guestfs_h *handle);
 extern int guestfs_lvm_remove_all (guestfs_h *handle);
 extern char *guestfs_file (guestfs_h *handle, const char *path);
 extern int guestfs_umount_all (guestfs_h *handle);
 extern int guestfs_lvm_remove_all (guestfs_h *handle);
 extern char *guestfs_file (guestfs_h *handle, const char *path);
+extern char *guestfs_command (guestfs_h *handle, char * const* const arguments);
+extern char **guestfs_command_lines (guestfs_h *handle, char * const* const arguments);
index 82b227c..0849f7a 100644 (file)
@@ -847,6 +847,49 @@ xdr_guestfs_file_ret (XDR *xdrs, guestfs_file_ret *objp)
 }
 
 bool_t
 }
 
 bool_t
+xdr_guestfs_command_args (XDR *xdrs, guestfs_command_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->arguments.arguments_val, (u_int *) &objp->arguments.arguments_len, ~0,
+               sizeof (str), (xdrproc_t) xdr_str))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_command_ret (XDR *xdrs, guestfs_command_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->output, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_command_lines_args (XDR *xdrs, guestfs_command_lines_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->arguments.arguments_val, (u_int *) &objp->arguments.arguments_len, ~0,
+               sizeof (str), (xdrproc_t) xdr_str))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_command_lines_ret (XDR *xdrs, guestfs_command_lines_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0,
+               sizeof (str), (xdrproc_t) xdr_str))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
 xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp)
 {
        register int32_t *buf;
 xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp)
 {
        register int32_t *buf;
index 339f069..4b31468 100644 (file)
@@ -436,6 +436,35 @@ struct guestfs_file_ret {
 };
 typedef struct guestfs_file_ret guestfs_file_ret;
 
 };
 typedef struct guestfs_file_ret guestfs_file_ret;
 
+struct guestfs_command_args {
+       struct {
+               u_int arguments_len;
+               str *arguments_val;
+       } arguments;
+};
+typedef struct guestfs_command_args guestfs_command_args;
+
+struct guestfs_command_ret {
+       char *output;
+};
+typedef struct guestfs_command_ret guestfs_command_ret;
+
+struct guestfs_command_lines_args {
+       struct {
+               u_int arguments_len;
+               str *arguments_val;
+       } arguments;
+};
+typedef struct guestfs_command_lines_args guestfs_command_lines_args;
+
+struct guestfs_command_lines_ret {
+       struct {
+               u_int lines_len;
+               str *lines_val;
+       } lines;
+};
+typedef struct guestfs_command_lines_ret guestfs_command_lines_ret;
+
 enum guestfs_procedure {
        GUESTFS_PROC_MOUNT = 1,
        GUESTFS_PROC_SYNC = 2,
 enum guestfs_procedure {
        GUESTFS_PROC_MOUNT = 1,
        GUESTFS_PROC_SYNC = 2,
@@ -486,7 +515,9 @@ enum guestfs_procedure {
        GUESTFS_PROC_UMOUNT_ALL = 47,
        GUESTFS_PROC_LVM_REMOVE_ALL = 48,
        GUESTFS_PROC_FILE = 49,
        GUESTFS_PROC_UMOUNT_ALL = 47,
        GUESTFS_PROC_LVM_REMOVE_ALL = 48,
        GUESTFS_PROC_FILE = 49,
-       GUESTFS_PROC_dummy = 49 + 1,
+       GUESTFS_PROC_COMMAND = 50,
+       GUESTFS_PROC_COMMAND_LINES = 51,
+       GUESTFS_PROC_dummy = 51 + 1,
 };
 typedef enum guestfs_procedure guestfs_procedure;
 #define GUESTFS_MESSAGE_MAX 4194304
 };
 typedef enum guestfs_procedure guestfs_procedure;
 #define GUESTFS_MESSAGE_MAX 4194304
@@ -588,6 +619,10 @@ extern  bool_t xdr_guestfs_umount_args (XDR *, guestfs_umount_args*);
 extern  bool_t xdr_guestfs_mounts_ret (XDR *, guestfs_mounts_ret*);
 extern  bool_t xdr_guestfs_file_args (XDR *, guestfs_file_args*);
 extern  bool_t xdr_guestfs_file_ret (XDR *, guestfs_file_ret*);
 extern  bool_t xdr_guestfs_mounts_ret (XDR *, guestfs_mounts_ret*);
 extern  bool_t xdr_guestfs_file_args (XDR *, guestfs_file_args*);
 extern  bool_t xdr_guestfs_file_ret (XDR *, guestfs_file_ret*);
+extern  bool_t xdr_guestfs_command_args (XDR *, guestfs_command_args*);
+extern  bool_t xdr_guestfs_command_ret (XDR *, guestfs_command_ret*);
+extern  bool_t xdr_guestfs_command_lines_args (XDR *, guestfs_command_lines_args*);
+extern  bool_t xdr_guestfs_command_lines_ret (XDR *, guestfs_command_lines_ret*);
 extern  bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*);
 extern  bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*);
 extern  bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*);
 extern  bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*);
 extern  bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*);
 extern  bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*);
@@ -659,6 +694,10 @@ extern bool_t xdr_guestfs_umount_args ();
 extern bool_t xdr_guestfs_mounts_ret ();
 extern bool_t xdr_guestfs_file_args ();
 extern bool_t xdr_guestfs_file_ret ();
 extern bool_t xdr_guestfs_mounts_ret ();
 extern bool_t xdr_guestfs_file_args ();
 extern bool_t xdr_guestfs_file_ret ();
+extern bool_t xdr_guestfs_command_args ();
+extern bool_t xdr_guestfs_command_ret ();
+extern bool_t xdr_guestfs_command_lines_args ();
+extern bool_t xdr_guestfs_command_lines_ret ();
 extern bool_t xdr_guestfs_procedure ();
 extern bool_t xdr_guestfs_message_direction ();
 extern bool_t xdr_guestfs_message_status ();
 extern bool_t xdr_guestfs_procedure ();
 extern bool_t xdr_guestfs_message_direction ();
 extern bool_t xdr_guestfs_message_status ();
index d133bb5..68362d6 100644 (file)
@@ -336,6 +336,22 @@ struct guestfs_file_ret {
   string description<>;
 };
 
   string description<>;
 };
 
+struct guestfs_command_args {
+  str arguments<>;
+};
+
+struct guestfs_command_ret {
+  string output<>;
+};
+
+struct guestfs_command_lines_args {
+  str arguments<>;
+};
+
+struct guestfs_command_lines_ret {
+  str lines<>;
+};
+
 enum guestfs_procedure {
   GUESTFS_PROC_MOUNT = 1,
   GUESTFS_PROC_SYNC = 2,
 enum guestfs_procedure {
   GUESTFS_PROC_MOUNT = 1,
   GUESTFS_PROC_SYNC = 2,
@@ -386,6 +402,8 @@ enum guestfs_procedure {
   GUESTFS_PROC_UMOUNT_ALL = 47,
   GUESTFS_PROC_LVM_REMOVE_ALL = 48,
   GUESTFS_PROC_FILE = 49,
   GUESTFS_PROC_UMOUNT_ALL = 47,
   GUESTFS_PROC_LVM_REMOVE_ALL = 48,
   GUESTFS_PROC_FILE = 49,
+  GUESTFS_PROC_COMMAND = 50,
+  GUESTFS_PROC_COMMAND_LINES = 51,
   GUESTFS_PROC_dummy
 };
 
   GUESTFS_PROC_dummy
 };