Implement RString and RStringList return types.
authorRichard Jones <rjones@redhat.com>
Sat, 4 Apr 2009 15:38:28 +0000 (16:38 +0100)
committerRichard Jones <rjones@redhat.com>
Sat, 4 Apr 2009 15:38:28 +0000 (16:38 +0100)
 - implement 'll' command.
 - outlines for 'ls' and 'cat' commands.

17 files changed:
daemon/Makefile.am
daemon/actions.h
daemon/daemon.h
daemon/file.c
daemon/guestfsd.c
daemon/ls.c [new file with mode: 0644]
daemon/stubs.c
fish/cmds.c
fish/fish.c
fish/fish.h
guestfs-actions.pod
src/generator.ml
src/guestfs-actions.c
src/guestfs-actions.h
src/guestfs_protocol.c
src/guestfs_protocol.h
src/guestfs_protocol.x

index ea60792..76e75c6 100644 (file)
@@ -23,6 +23,7 @@ guestfsd_SOURCES = \
        daemon.h \
        file.c \
        guestfsd.c \
        daemon.h \
        file.c \
        guestfsd.c \
+       ls.c \
        mount.c \
        proto.c \
        stubs.c \
        mount.c \
        proto.c \
        stubs.c \
index 61b583c..c4b9b15 100644 (file)
@@ -19,6 +19,9 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+extern char *do_cat (const char *path);
+extern char *do_ll (const char *directory);
+extern char **do_ls (const char *directory);
 extern int do_mount (const char *device, const char *mountpoint);
 extern int do_sync ();
 extern int do_touch (const char *path);
 extern int do_mount (const char *device, const char *mountpoint);
 extern int do_sync ();
 extern int do_touch (const char *path);
index fa896e1..42d7727 100644 (file)
@@ -26,6 +26,8 @@
 /* in guestfsd.c */
 extern void xwrite (int sock, const void *buf, size_t len);
 extern void xread (int sock, void *buf, size_t len);
 /* in guestfsd.c */
 extern void xwrite (int sock, const void *buf, size_t len);
 extern void xread (int sock, void *buf, size_t len);
+extern int count_strings (char **argv);
+extern void free_strings (char **argv);
 extern int command (char **stdoutput, char **stderror, const char *name, ...);
 
 /* in proto.c */
 extern int command (char **stdoutput, char **stderror, const char *name, ...);
 
 /* in proto.c */
index 1e633d8..db19918 100644 (file)
@@ -61,3 +61,10 @@ do_touch (const char *path)
   close (fd);
   return 0;
 }
   close (fd);
   return 0;
 }
+
+char *
+do_cat (const char *path)
+{
+  reply_with_error ("cat command is not yet implemented");
+  return NULL;
+}
index 810d9d0..9d110d7 100644 (file)
@@ -35,8 +35,6 @@
 
 #include "daemon.h"
 
 
 #include "daemon.h"
 
-void xwrite (int sock, const void *buf, size_t len);
-
 static void usage (void);
 
 /* Also in guestfs.c */
 static void usage (void);
 
 /* Also in guestfs.c */
@@ -232,6 +230,26 @@ usage (void)
   fprintf (stderr, "guestfsd [-f] [-h host -p port]\n");
 }
 
   fprintf (stderr, "guestfsd [-f] [-h host -p port]\n");
 }
 
+int
+count_strings (char **argv)
+{
+  int argc;
+
+  for (argc = 0; argv[argc] != NULL; ++argc)
+    ;
+  return argc;
+}
+
+void
+free_strings (char **argv)
+{
+  int argc;
+
+  for (argc = 0; argv[argc] != NULL; ++argc)
+    free (argv[argc]);
+  free (argv);
+}
+
 /* This is a more sane version of 'system(3)' for running external
  * commands.  It uses fork/execvp, so we don't need to worry about
  * quoting of parameters, and it allows us to capture any error
 /* This is a more sane version of 'system(3)' for running external
  * commands.  It uses fork/execvp, so we don't need to worry about
  * quoting of parameters, and it allows us to capture any error
@@ -355,19 +373,15 @@ command (char **stdoutput, char **stderror, const char *name, ...)
   }
 
   /* Make sure the output buffers are \0-terminated.  Also remove any
   }
 
   /* Make sure the output buffers are \0-terminated.  Also remove any
-   * trailing \n characters.
+   * trailing \n characters from the error buffer (not from stdout).
    */
   if (stdoutput) {
     *stdoutput = realloc (*stdoutput, so_size+1);
     if (*stdoutput == NULL) {
       perror ("realloc");
       *stdoutput = NULL;
    */
   if (stdoutput) {
     *stdoutput = realloc (*stdoutput, so_size+1);
     if (*stdoutput == NULL) {
       perror ("realloc");
       *stdoutput = NULL;
-    } else {
+    } else
       (*stdoutput)[so_size] = '\0';
       (*stdoutput)[so_size] = '\0';
-      so_size--;
-      while (so_size >= 0 && (*stdoutput)[so_size] == '\n')
-       (*stdoutput)[so_size--] = '\0';
-    }
   }
   if (stderror) {
     *stderror = realloc (*stderror, se_size+1);
   }
   if (stderror) {
     *stderror = realloc (*stderror, se_size+1);
diff --git a/daemon/ls.c b/daemon/ls.c
new file mode 100644 (file)
index 0000000..1bea5f1
--- /dev/null
@@ -0,0 +1,75 @@
+/* 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 <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "daemon.h"
+#include "actions.h"
+
+char **
+do_ls (const char *path)
+{
+  reply_with_error ("ls command is not yet implemented");
+  return NULL;
+}
+
+char *
+do_ll (const char *path)
+{
+  int r, len;
+  char *out, *err;
+  char *spath;
+
+  if (path[0] != '/') {
+    reply_with_error ("ll: path must start with a / character");
+    return NULL;
+  }
+
+  /* This exposes the /sysroot, because we can't chroot and run the ls
+   * command (since 'ls' won't necessarily exist in the chroot).  This
+   * command is not meant for serious use anyway, just for quick
+   * interactive sessions.  For the same reason, you can also "escape"
+   * the sysroot (eg. 'll /..').
+   */
+  len = strlen (path) + 9;
+  spath = malloc (len);
+  if (!spath) {
+    reply_with_perror ("malloc");
+    return NULL;
+  }
+  snprintf (spath, len, "/sysroot%s", path);
+
+  r = command (&out, &err, "ls", "-la", spath, NULL);
+  free (spath);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (out);
+    free (err);
+    return NULL;
+  }
+
+  free (err);
+  return out;                  /* caller frees */
+}
index 54fa8e0..4eccfb5 100644 (file)
 #include "../src/guestfs_protocol.h"
 #include "actions.h"
 
 #include "../src/guestfs_protocol.h"
 #include "actions.h"
 
+static void cat_stub (XDR *xdr_in)
+{
+  char *r;
+  struct guestfs_cat_args args;
+  const char *path;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_cat_args (xdr_in, &args)) {
+    reply_with_error ("cat: daemon failed to decode procedure arguments");
+    return;
+  }
+  path = args.path;
+
+  r = do_cat (path);
+  if (r == NULL)
+    /* do_cat has already called reply_with_error, so just return */
+    return;
+
+  struct guestfs_cat_ret ret;
+  ret.content = r;
+  reply ((xdrproc_t) &xdr_guestfs_cat_ret, (char *) &ret);
+  free (r);
+}
+
+static void ll_stub (XDR *xdr_in)
+{
+  char *r;
+  struct guestfs_ll_args args;
+  const char *directory;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_ll_args (xdr_in, &args)) {
+    reply_with_error ("ll: daemon failed to decode procedure arguments");
+    return;
+  }
+  directory = args.directory;
+
+  r = do_ll (directory);
+  if (r == NULL)
+    /* do_ll has already called reply_with_error, so just return */
+    return;
+
+  struct guestfs_ll_ret ret;
+  ret.listing = r;
+  reply ((xdrproc_t) &xdr_guestfs_ll_ret, (char *) &ret);
+  free (r);
+}
+
+static void ls_stub (XDR *xdr_in)
+{
+  char **r;
+  struct guestfs_ls_args args;
+  const char *directory;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_ls_args (xdr_in, &args)) {
+    reply_with_error ("ls: daemon failed to decode procedure arguments");
+    return;
+  }
+  directory = args.directory;
+
+  r = do_ls (directory);
+  if (r == NULL)
+    /* do_ls has already called reply_with_error, so just return */
+    return;
+
+  struct guestfs_ls_ret ret;
+  ret.listing.listing_len = count_strings (r);
+  ret.listing.listing_val = r;
+  reply ((xdrproc_t) &xdr_guestfs_ls_ret, (char *) &ret);
+  free_strings (r);
+}
+
 static void mount_stub (XDR *xdr_in)
 {
   int r;
 static void mount_stub (XDR *xdr_in)
 {
   int r;
@@ -86,6 +162,15 @@ static void touch_stub (XDR *xdr_in)
 void dispatch_incoming_message (XDR *xdr_in)
 {
   switch (proc_nr) {
 void dispatch_incoming_message (XDR *xdr_in)
 {
   switch (proc_nr) {
+    case GUESTFS_PROC_CAT:
+      cat_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_LL:
+      ll_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_LS:
+      ls_stub (xdr_in);
+      break;
     case GUESTFS_PROC_MOUNT:
       mount_stub (xdr_in);
       break;
     case GUESTFS_PROC_MOUNT:
       mount_stub (xdr_in);
       break;
index bc9d5a9..3b53587 100644 (file)
@@ -29,6 +29,9 @@ void list_commands (void)
 {
   printf ("    %-16s     %s\n", "Command", "Description");
   list_builtin_commands ();
 {
   printf ("    %-16s     %s\n", "Command", "Description");
   list_builtin_commands ();
+  printf ("%-20s %s\n", "cat", "list the files in a directory (long format)");
+  printf ("%-20s %s\n", "ll", "list the files in a directory (long format)");
+  printf ("%-20s %s\n", "ls", "list the files in a directory");
   printf ("%-20s %s\n", "mount", "mount a guest disk at a position in the filesystem");
   printf ("%-20s %s\n", "sync", "sync disks, writes are flushed through to the disk image");
   printf ("%-20s %s\n", "touch", "update file timestamps or create a new file");
   printf ("%-20s %s\n", "mount", "mount a guest disk at a position in the filesystem");
   printf ("%-20s %s\n", "sync", "sync disks, writes are flushed through to the disk image");
   printf ("%-20s %s\n", "touch", "update file timestamps or create a new file");
@@ -37,6 +40,15 @@ void list_commands (void)
 
 void display_command (const char *cmd)
 {
 
 void display_command (const char *cmd)
 {
+  if (strcasecmp (cmd, "cat") == 0)
+    pod2text ("cat - list the files in a directory (long format)", " cat <path>\n\nReturn the contents of the file named C<path>.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB.  To transfer large files you should use\nFTP.");
+  else
+  if (strcasecmp (cmd, "ll") == 0)
+    pod2text ("ll - list the files in a directory (long format)", " ll <directory>\n\nList the files in C<directory> (relative to the root directory,\nthere is no cwd) in the format of 'ls -la'.\n\nThis command is mostly useful for interactive sessions.  It\nis I<not> intended that you try to parse the output string.");
+  else
+  if (strcasecmp (cmd, "ls") == 0)
+    pod2text ("ls - list the files in a directory", " ls <directory>\n\nList the files in C<directory> (relative to the root directory,\nthere is no cwd).  The '.' and '..' entries are not returned, but\nhidden files are shown.\n\nThis command is mostly useful for interactive sessions.");
+  else
   if (strcasecmp (cmd, "mount") == 0)
     pod2text ("mount - mount a guest disk at a position in the filesystem", " mount <device> <mountpoint>\n\nMount a guest disk at a position in the filesystem.  Block devices\nare named C</dev/sda>, C</dev/sdb> and so on, as they were added to\nthe guest.  If those block devices contain partitions, they will have\nthe usual names (eg. C</dev/sda1>).  Also LVM C</dev/VG/LV>-style\nnames can be used.\n\nThe rules are the same as for L<mount(2)>:  A filesystem must\nfirst be mounted on C</> before others can be mounted.  Other\nfilesystems can only be mounted on directories which already\nexist.\n\nThe mounted filesystem is writable, if we have sufficient permissions\non the underlying device.\n\nThe filesystem options C<sync> and C<noatime> are set with this\ncall, in order to improve reliability.");
   else
   if (strcasecmp (cmd, "mount") == 0)
     pod2text ("mount - mount a guest disk at a position in the filesystem", " mount <device> <mountpoint>\n\nMount a guest disk at a position in the filesystem.  Block devices\nare named C</dev/sda>, C</dev/sdb> and so on, as they were added to\nthe guest.  If those block devices contain partitions, they will have\nthe usual names (eg. C</dev/sda1>).  Also LVM C</dev/VG/LV>-style\nnames can be used.\n\nThe rules are the same as for L<mount(2)>:  A filesystem must\nfirst be mounted on C</> before others can be mounted.  Other\nfilesystems can only be mounted on directories which already\nexist.\n\nThe mounted filesystem is writable, if we have sufficient permissions\non the underlying device.\n\nThe filesystem options C<sync> and C<noatime> are set with this\ncall, in order to improve reliability.");
   else
@@ -49,6 +61,57 @@ void display_command (const char *cmd)
     display_builtin_command (cmd);
 }
 
     display_builtin_command (cmd);
 }
 
+static int run_cat (const char *cmd, int argc, char *argv[])
+{
+  char *r;
+  const char *path;
+  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;
+  }
+  path = argv[0];
+  r = guestfs_cat (g, path);
+  if (r == NULL) return -1;
+  printf ("%s", r);
+  free (r);
+  return 0;
+}
+
+static int run_ll (const char *cmd, int argc, char *argv[])
+{
+  char *r;
+  const char *directory;
+  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;
+  }
+  directory = argv[0];
+  r = guestfs_ll (g, directory);
+  if (r == NULL) return -1;
+  printf ("%s", r);
+  free (r);
+  return 0;
+}
+
+static int run_ls (const char *cmd, int argc, char *argv[])
+{
+  char **r;
+  const char *directory;
+  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;
+  }
+  directory = argv[0];
+  r = guestfs_ls (g, directory);
+  if (r == NULL) return -1;
+  print_strings (r);
+  free_strings (r);
+  return 0;
+}
+
 static int run_mount (const char *cmd, int argc, char *argv[])
 {
   int r;
 static int run_mount (const char *cmd, int argc, char *argv[])
 {
   int r;
@@ -93,6 +156,15 @@ static int run_touch (const char *cmd, int argc, char *argv[])
 
 int run_action (const char *cmd, int argc, char *argv[])
 {
 
 int run_action (const char *cmd, int argc, char *argv[])
 {
+  if (strcasecmp (cmd, "cat") == 0)
+    return run_cat (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "ll") == 0)
+    return run_ll (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "ls") == 0)
+    return run_ls (cmd, argc, argv);
+  else
   if (strcasecmp (cmd, "mount") == 0)
     return run_mount (cmd, argc, argv);
   else
   if (strcasecmp (cmd, "mount") == 0)
     return run_mount (cmd, argc, argv);
   else
index 5c89335..374416e 100644 (file)
@@ -526,3 +526,22 @@ parse_size (const char *str, off_t *size_rtn)
 
   return 0;
 }
 
   return 0;
 }
+
+void
+free_strings (char **argv)
+{
+  int argc;
+
+  for (argc = 0; argv[argc] != NULL; ++argc)
+    free (argv[argc]);
+  free (argv);
+}
+
+void
+print_strings (char **argv)
+{
+  int argc;
+
+  for (argc = 0; argv[argc] != NULL; ++argc)
+    printf ("%s\n", argv[argc]);
+}
index f98464a..d398201 100644 (file)
@@ -29,6 +29,8 @@ extern int verbose;
 extern void pod2text (const char *heading, const char *body);
 extern void list_builtin_commands (void);
 extern void display_builtin_command (const char *cmd);
 extern void pod2text (const char *heading, const char *body);
 extern void list_builtin_commands (void);
 extern void display_builtin_command (const char *cmd);
+extern void free_strings (char **argv);
+extern void print_strings (char **argv);
 
 /* in cmds.c (auto-generated) */
 extern void list_commands (void);
 
 /* in cmds.c (auto-generated) */
 extern void list_commands (void);
index 2a3d62d..352760d 100644 (file)
@@ -1,3 +1,47 @@
+=head2 guestfs_cat
+
+ char *guestfs_cat (guestfs_h *handle,
+               const char *path);
+
+Return the contents of the file named C<path>.
+
+This function returns a string or NULL on error.  The caller
+must free the returned string after use.
+
+Because of the message protocol, there is a transfer limit 
+of somewhere between 2MB and 4MB.  To transfer large files you should use
+FTP.
+
+=head2 guestfs_ll
+
+ char *guestfs_ll (guestfs_h *handle,
+               const char *directory);
+
+List the files in C<directory> (relative to the root directory,
+there is no cwd) in the format of 'ls -la'.
+
+This command is mostly useful for interactive sessions.  It
+is I<not> intended that you try to parse the output string.
+
+This function returns a string or NULL on error.  The caller
+must free the returned string after use.
+
+=head2 guestfs_ls
+
+ char **guestfs_ls (guestfs_h *handle,
+               const char *directory);
+
+List the files in C<directory> (relative to the root directory,
+there is no cwd).  The '.' and '..' entries are not returned, but
+hidden files are shown.
+
+This command is mostly useful for interactive sessions.
+
+This function returns a NULL-terminated array of strings
+(like L<environ(3)>), or NULL if there was an error.
+
+The caller must free the strings I<and> the array after use.
+
 =head2 guestfs_mount
 
  int guestfs_mount (guestfs_h *handle,
 =head2 guestfs_mount
 
  int guestfs_mount (guestfs_h *handle,
@@ -21,7 +65,7 @@ on the underlying device.
 The filesystem options C<sync> and C<noatime> are set with this
 call, in order to improve reliability.
 
 The filesystem options C<sync> and C<noatime> are set with this
 call, in order to improve reliability.
 
-This function return 0 on success or -1 on error.
+This function returns 0 on success or -1 on error.
 
 =head2 guestfs_sync
 
 
 =head2 guestfs_sync
 
@@ -33,7 +77,7 @@ underlying disk image.
 You should always call this if you have modified a disk image, before
 calling C<guestfs_close>.
 
 You should always call this if you have modified a disk image, before
 calling C<guestfs_close>.
 
-This function return 0 on success or -1 on error.
+This function returns 0 on success or -1 on error.
 
 =head2 guestfs_touch
 
 
 =head2 guestfs_touch
 
@@ -44,5 +88,5 @@ Touch acts like the L<touch(1)> command.  It can be used to
 update the timestamps on a file, or, if the file does not exist,
 to create a new zero-length file.
 
 update the timestamps on a file, or, if the file does not exist,
 to create a new zero-length file.
 
-This function return 0 on success or -1 on error.
+This function returns 0 on success or -1 on error.
 
 
index 8fa5cbc..14b5155 100755 (executable)
@@ -32,6 +32,11 @@ and ret =
      * indication, ie. 0 or -1.
      *)
   | Err
      * indication, ie. 0 or -1.
      *)
   | Err
+    (* "RString" and "RStringList" require special treatment because
+     * the caller must free them.
+     *)
+  | RString of string
+  | RStringList of string
 and args =
     (* 0 arguments, 1 argument, etc. The guestfs_h param is implicit. *)
   | P0
 and args =
     (* 0 arguments, 1 argument, etc. The guestfs_h param is implicit. *)
   | P0
@@ -40,8 +45,33 @@ and args =
 and argt =
   | String of string   (* const char *name, cannot be NULL *)
 
 and argt =
   | String of string   (* const char *name, cannot be NULL *)
 
+type flags = ProtocolLimitWarning
+
 let functions = [
 let functions = [
-  ("mount", (Err, P2 (String "device", String "mountpoint")), 1,
+  ("cat", (RString "content", P1 (String "path")), 4, [ProtocolLimitWarning],
+   "list the files in a directory (long format)",
+   "\
+Return the contents of the file named C<path>.");
+
+  ("ll", (RString "listing", P1 (String "directory")), 5, [],
+   "list the files in a directory (long format)",
+   "\
+List the files in C<directory> (relative to the root directory,
+there is no cwd) in the format of 'ls -la'.
+
+This command is mostly useful for interactive sessions.  It
+is I<not> intended that you try to parse the output string.");
+
+  ("ls", (RStringList "listing", P1 (String "directory")), 6, [],
+   "list the files in a directory",
+   "\
+List the files in C<directory> (relative to the root directory,
+there is no cwd).  The '.' and '..' entries are not returned, but
+hidden files are shown.
+
+This command is mostly useful for interactive sessions.");
+
+  ("mount", (Err, P2 (String "device", String "mountpoint")), 1, [],
    "mount a guest disk at a position in the filesystem",
    "\
 Mount a guest disk at a position in the filesystem.  Block devices
    "mount a guest disk at a position in the filesystem",
    "\
 Mount a guest disk at a position in the filesystem.  Block devices
@@ -61,7 +91,7 @@ on the underlying device.
 The filesystem options C<sync> and C<noatime> are set with this
 call, in order to improve reliability.");
 
 The filesystem options C<sync> and C<noatime> are set with this
 call, in order to improve reliability.");
 
-  ("sync", (Err, P0), 2,
+  ("sync", (Err, P0), 2, [],
    "sync disks, writes are flushed through to the disk image",
    "\
 This syncs the disk, so that any writes are flushed through to the
    "sync disks, writes are flushed through to the disk image",
    "\
 This syncs the disk, so that any writes are flushed through to the
@@ -70,7 +100,7 @@ underlying disk image.
 You should always call this if you have modified a disk image, before
 calling C<guestfs_close>.");
 
 You should always call this if you have modified a disk image, before
 calling C<guestfs_close>.");
 
-  ("touch", (Err, P1 (String "path")), 3,
+  ("touch", (Err, P1 (String "path")), 3, [],
    "update file timestamps or create a new file",
    "\
 Touch acts like the L<touch(1)> command.  It can be used to
    "update file timestamps or create a new file",
    "\
 Touch acts like the L<touch(1)> command.  It can be used to
@@ -155,30 +185,46 @@ let rec generate_header comment license =
 (* Generate the pod documentation for the C API. *)
 and generate_pod () =
   List.iter (
 (* Generate the pod documentation for the C API. *)
 and generate_pod () =
   List.iter (
-    fun (shortname, style, _, _, longdesc) ->
+    fun (shortname, style, _, flags, _, longdesc) ->
       let name = "guestfs_" ^ shortname in
       pr "=head2 %s\n\n" name;
       pr " ";
       generate_prototype ~extern:false ~handle:"handle" name style;
       pr "\n\n";
       pr "%s\n\n" longdesc;
       let name = "guestfs_" ^ shortname in
       pr "=head2 %s\n\n" name;
       pr " ";
       generate_prototype ~extern:false ~handle:"handle" name style;
       pr "\n\n";
       pr "%s\n\n" longdesc;
-      (match style with
-       | (Err, _) ->
-          pr "This function return 0 on success or -1 on error.\n\n"
+      (match fst style with
+       | Err ->
+          pr "This function returns 0 on success or -1 on error.\n\n"
+       | RString _ ->
+          pr "This function returns a string or NULL on error.  The caller
+must free the returned string after use.\n\n"
+       | RStringList _ ->
+          pr "This function returns a NULL-terminated array of strings
+(like L<environ(3)>), or NULL if there was an error.
+
+The caller must free the strings I<and> the array after use.\n\n"
       );
       );
+      if List.mem ProtocolLimitWarning flags then
+       pr "Because of the message protocol, there is a transfer limit 
+of somewhere between 2MB and 4MB.  To transfer large files you should use
+FTP.\n\n";
   ) functions
 
 (* Generate the protocol (XDR) file. *)
 and generate_xdr () =
   generate_header CStyle LGPLv2;
 
   ) functions
 
 (* Generate the protocol (XDR) file. *)
 and generate_xdr () =
   generate_header CStyle LGPLv2;
 
+  (* This has to be defined to get around a limitation in Sun's rpcgen. *)
+  pr "typedef string str<>;\n";
+  pr "\n";
+
   List.iter (
   List.iter (
-    fun (shortname, style, _, _, _) ->
+    fun (shortname, style, _, _, _, _) ->
       let name = "guestfs_" ^ shortname in
       pr "/* %s */\n\n" name;
       let name = "guestfs_" ^ shortname in
       pr "/* %s */\n\n" name;
-      (match style with
-       | (_, P0) -> ()
-       | (_, args) ->
+      (match snd style with
+       | P0 -> ()
+       | args ->
           pr "struct %s_args {\n" name;
           iter_args (
             function
           pr "struct %s_args {\n" name;
           iter_args (
             function
@@ -186,16 +232,23 @@ and generate_xdr () =
           ) args;
           pr "};\n\n"
       );
           ) args;
           pr "};\n\n"
       );
-      (match style with
-       | (Err, _) -> () 
-          (* | ... -> pr "struct %s_ret ...\n" name; *)
+      (match fst style with
+       | Err -> () 
+       | RString n ->
+          pr "struct %s_ret {\n" name;
+          pr "  string %s<>;\n" n;
+          pr "};\n\n"
+       | RStringList n ->
+          pr "struct %s_ret {\n" name;
+          pr "  str %s<>;\n" n;
+          pr "};\n\n"
       );
   ) functions;
 
   (* Table of procedure numbers. *)
   pr "enum guestfs_procedure {\n";
   List.iter (
       );
   ) functions;
 
   (* Table of procedure numbers. *)
   pr "enum guestfs_procedure {\n";
   List.iter (
-    fun (shortname, _, proc_nr, _, _) ->
+    fun (shortname, _, proc_nr, _, _, _) ->
       pr "  GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr
   ) functions;
   pr "  GUESTFS_PROC_dummy\n"; (* so we don't have a "hanging comma" *)
       pr "  GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr
   ) functions;
   pr "  GUESTFS_PROC_dummy\n"; (* so we don't have a "hanging comma" *)
@@ -246,7 +299,7 @@ struct guestfs_message_header {
 and generate_actions_h () =
   generate_header CStyle LGPLv2;
   List.iter (
 and generate_actions_h () =
   generate_header CStyle LGPLv2;
   List.iter (
-    fun (shortname, style, _, _, _) ->
+    fun (shortname, style, _, _, _, _) ->
       let name = "guestfs_" ^ shortname in
       generate_prototype ~single_line:true ~newline:true ~handle:"handle"
        name style
       let name = "guestfs_" ^ shortname in
       generate_prototype ~single_line:true ~newline:true ~handle:"handle"
        name style
@@ -256,7 +309,7 @@ and generate_actions_h () =
 and generate_client_actions () =
   generate_header CStyle LGPLv2;
   List.iter (
 and generate_client_actions () =
   generate_header CStyle LGPLv2;
   List.iter (
-    fun (shortname, style, _, _, _) ->
+    fun (shortname, style, _, _, _, _) ->
       let name = "guestfs_" ^ shortname in
 
       (* Generate the return value struct. *)
       let name = "guestfs_" ^ shortname in
 
       (* Generate the return value struct. *)
@@ -264,9 +317,9 @@ and generate_client_actions () =
       pr "  int cb_done;  /* flag to indicate callback was called */\n";
       pr "  struct guestfs_message_header hdr;\n";
       pr "  struct guestfs_message_error err;\n";
       pr "  int cb_done;  /* flag to indicate callback was called */\n";
       pr "  struct guestfs_message_header hdr;\n";
       pr "  struct guestfs_message_error err;\n";
-      (match style with
-       | (Err, _) -> ()
-    (* | _ -> pr "  struct %s_ret ret;\n" name; *)
+      (match fst style with
+       | Err -> ()
+       | RString _ | RStringList _ -> pr "  struct %s_ret ret;\n" name;
       );
       pr "};\n\n";
 
       );
       pr "};\n\n";
 
@@ -287,9 +340,13 @@ and generate_client_actions () =
       pr "    goto done;\n";
       pr "  }\n";
 
       pr "    goto done;\n";
       pr "  }\n";
 
-      (match style with
-       | (Err, _) -> ()
-    (* |  _ -> pr "  if (!xdr_%s_ret (&xdr, &rv->ret)) ..." *)
+      (match fst style with
+       | Err -> ()
+       |  RString _ | RStringList _ ->
+           pr "  if (!xdr_%s_ret (xdr, &rv->ret)) {\n" name;
+           pr "    error (g, \"%s: failed to parse reply\");\n" name;
+           pr "    return;\n";
+           pr "  }\n";
       );
 
       pr " done:\n";
       );
 
       pr " done:\n";
@@ -302,13 +359,14 @@ and generate_client_actions () =
        ~handle:"g" name style;
 
       let error_code =
        ~handle:"g" name style;
 
       let error_code =
-       match style with
-       | (Err, _) -> "-1" in
+       match fst style with
+       | Err -> "-1"
+       | RString _ | RStringList _ -> "NULL" in
 
       pr "{\n";
 
 
       pr "{\n";
 
-      (match style with
-       | (_, P0) -> ()
+      (match snd style with
+       | P0 -> ()
        | _ -> pr "  struct %s_args args;\n" name
       );
 
        | _ -> pr "  struct %s_args args;\n" name
       );
 
@@ -325,11 +383,11 @@ and generate_client_actions () =
       pr "  memset (&rv, 0, sizeof rv);\n";
       pr "\n";
 
       pr "  memset (&rv, 0, sizeof rv);\n";
       pr "\n";
 
-      (match style with
-       | (_, P0) ->
+      (match snd style with
+       | P0 ->
           pr "  serial = dispatch (g, GUESTFS_PROC_%s, NULL, NULL);\n"
             (String.uppercase shortname)
           pr "  serial = dispatch (g, GUESTFS_PROC_%s, NULL, NULL);\n"
             (String.uppercase shortname)
-       | (_, args) ->
+       | args ->
           iter_args (
             function
             | String name -> pr "  args.%s = (char *) %s;\n" name name
           iter_args (
             function
             | String name -> pr "  args.%s = (char *) %s;\n" name name
@@ -366,8 +424,15 @@ and generate_client_actions () =
       pr "  }\n";
       pr "\n";
 
       pr "  }\n";
       pr "\n";
 
-      (match style with
-       | (Err, _) -> pr "  return 0;\n"
+      (match fst style with
+       | Err -> pr "  return 0;\n"
+       | RString n ->
+          pr "  return rv.ret.%s; /* caller will free */\n" n
+       | RStringList n ->
+          pr "  /* caller will free this, but we need to add a NULL entry */\n";
+          pr "  rv.ret.%s.%s_val = safe_realloc (g, rv.ret.%s.%s_val, rv.ret.%s.%s_len + 1);\n" n n n n n n;
+          pr "  rv.ret.%s.%s_val[rv.ret.%s.%s_len] = NULL;\n" n n n n;
+          pr "  return rv.ret.%s.%s_val;\n" n n
       );
 
       pr "}\n\n"
       );
 
       pr "}\n\n"
@@ -377,7 +442,7 @@ and generate_client_actions () =
 and generate_daemon_actions_h () =
   generate_header CStyle GPLv2;
   List.iter (
 and generate_daemon_actions_h () =
   generate_header CStyle GPLv2;
   List.iter (
-    fun (name, style, _, _, _) ->
+    fun (name, style, _, _, _, _) ->
       generate_prototype ~single_line:true ~newline:true ("do_" ^ name) style;
   ) functions
 
       generate_prototype ~single_line:true ~newline:true ("do_" ^ name) style;
   ) functions
 
@@ -393,16 +458,18 @@ and generate_daemon_actions () =
   pr "\n";
 
   List.iter (
   pr "\n";
 
   List.iter (
-    fun (name, style, _, _, _) ->
+    fun (name, style, _, _, _, _) ->
       (* Generate server-side stubs. *)
       pr "static void %s_stub (XDR *xdr_in)\n" name;
       pr "{\n";
       let error_code =
       (* Generate server-side stubs. *)
       pr "static void %s_stub (XDR *xdr_in)\n" name;
       pr "{\n";
       let error_code =
-       match style with
-       | (Err, _) -> pr "  int r;\n"; "-1" in
-      (match style with
-       | (_, P0) -> ()
-       | (_, args) ->
+       match fst style with
+       | Err -> pr "  int r;\n"; "-1"
+       | RString _ -> pr "  char *r;\n"; "NULL"
+       | RStringList _ -> pr "  char **r;\n"; "NULL" in
+      (match snd style with
+       | P0 -> ()
+       | args ->
           pr "  struct guestfs_%s_args args;\n" name;
           iter_args (
             function
           pr "  struct guestfs_%s_args args;\n" name;
           iter_args (
             function
@@ -411,9 +478,9 @@ and generate_daemon_actions () =
       );
       pr "\n";
 
       );
       pr "\n";
 
-      (match style with
-       | (_, P0) -> ()
-       | (_, args) ->
+      (match snd style with
+       | P0 -> ()
+       | args ->
           pr "  memset (&args, 0, sizeof args);\n";
           pr "\n";
           pr "  if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
           pr "  memset (&args, 0, sizeof args);\n";
           pr "\n";
           pr "  if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
@@ -436,8 +503,19 @@ and generate_daemon_actions () =
       pr "    return;\n";
       pr "\n";
 
       pr "    return;\n";
       pr "\n";
 
-      (match style with
-       | (Err, _) -> pr "  reply (NULL, NULL);\n"
+      (match fst style with
+       | Err -> pr "  reply (NULL, NULL);\n"
+       | RString n ->
+          pr "  struct guestfs_%s_ret ret;\n" name;
+          pr "  ret.%s = r;\n" n;
+          pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
+          pr "  free (r);\n"
+       | RStringList n ->
+          pr "  struct guestfs_%s_ret ret;\n" name;
+          pr "  ret.%s.%s_len = count_strings (r);\n" n n;
+          pr "  ret.%s.%s_val = r;\n" n n;
+          pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
+          pr "  free_strings (r);\n"
       );
 
       pr "}\n\n";
       );
 
       pr "}\n\n";
@@ -449,7 +527,7 @@ and generate_daemon_actions () =
   pr "  switch (proc_nr) {\n";
 
   List.iter (
   pr "  switch (proc_nr) {\n";
 
   List.iter (
-    fun (name, style, _, _, _) ->
+    fun (name, style, _, _, _, _) ->
       pr "    case GUESTFS_PROC_%s:\n" (String.uppercase name);
       pr "      %s_stub (xdr_in);\n" name;
       pr "      break;\n"
       pr "    case GUESTFS_PROC_%s:\n" (String.uppercase name);
       pr "      %s_stub (xdr_in);\n" name;
       pr "      break;\n"
@@ -477,7 +555,7 @@ and generate_fish_cmds () =
   pr "  printf (\"    %%-16s     %%s\\n\", \"Command\", \"Description\");\n";
   pr "  list_builtin_commands ();\n";
   List.iter (
   pr "  printf (\"    %%-16s     %%s\\n\", \"Command\", \"Description\");\n";
   pr "  list_builtin_commands ();\n";
   List.iter (
-    fun (name, _, _, shortdesc, _) ->
+    fun (name, _, _, _, shortdesc, _) ->
       pr "  printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
        name shortdesc
   ) functions;
       pr "  printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
        name shortdesc
   ) functions;
@@ -489,11 +567,11 @@ and generate_fish_cmds () =
   pr "void display_command (const char *cmd)\n";
   pr "{\n";
   List.iter (
   pr "void display_command (const char *cmd)\n";
   pr "{\n";
   List.iter (
-    fun (name, style, _, shortdesc, longdesc) ->
+    fun (name, style, _, flags, shortdesc, longdesc) ->
       let synopsis =
       let synopsis =
-       match style with
-       | (Err, P0) -> name
-       | (Err, args) ->
+       match snd style with
+       | P0 -> name
+       | args ->
            sprintf "%s <%s>"
              name (
                String.concat "> <" (
            sprintf "%s <%s>"
              name (
                String.concat "> <" (
@@ -502,10 +580,17 @@ and generate_fish_cmds () =
                )
              ) in
 
                )
              ) in
 
+      let warnings =
+       if List.mem ProtocolLimitWarning flags then
+         "\n\nBecause of the message protocol, there is a transfer limit 
+of somewhere between 2MB and 4MB.  To transfer large files you should use
+FTP."
+       else "" in
+
       pr "  if (strcasecmp (cmd, \"%s\") == 0)\n" name;
       pr "    pod2text (\"%s - %s\", %S);\n"
        name shortdesc
       pr "  if (strcasecmp (cmd, \"%s\") == 0)\n" name;
       pr "    pod2text (\"%s - %s\", %S);\n"
        name shortdesc
-       (" " ^ synopsis ^ "\n\n" ^ longdesc);
+       (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings);
       pr "  else\n"
   ) functions;
   pr "    display_builtin_command (cmd);\n";
       pr "  else\n"
   ) functions;
   pr "    display_builtin_command (cmd);\n";
@@ -514,11 +599,13 @@ and generate_fish_cmds () =
 
   (* run_<action> actions *)
   List.iter (
 
   (* run_<action> actions *)
   List.iter (
-    fun (name, style, _, _, _) ->
+    fun (name, style, _, _, _, _) ->
       pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
       pr "{\n";
       pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
       pr "{\n";
-      (match style with
-       | (Err, _) -> pr "  int r;\n"
+      (match fst style with
+       | Err -> pr "  int r;\n"
+       | RString _ -> pr "  char *r;\n"
+       | RStringList _ -> pr "  char **r;\n"
       );
       iter_args (
        function
       );
       iter_args (
        function
@@ -544,9 +631,19 @@ and generate_fish_cmds () =
       generate_call_args ~handle:"g" style;
       pr ";\n";
 
       generate_call_args ~handle:"g" style;
       pr ";\n";
 
-      (* Check return value for errors. *)
-      (match style with
-       | (Err, _) -> pr "  return r;\n"
+      (* Check return value for errors and display command results. *)
+      (match fst style with
+       | Err -> pr "  return r;\n"
+       | RString _ ->
+          pr "  if (r == NULL) return -1;\n";
+          pr "  printf (\"%%s\", r);\n";
+          pr "  free (r);\n";
+          pr "  return 0;\n"
+       | RStringList _ ->
+          pr "  if (r == NULL) return -1;\n";
+          pr "  print_strings (r);\n";
+          pr "  free_strings (r);\n";
+          pr "  return 0;\n"
       );
       pr "}\n";
       pr "\n"
       );
       pr "}\n";
       pr "\n"
@@ -556,7 +653,7 @@ and generate_fish_cmds () =
   pr "int run_action (const char *cmd, int argc, char *argv[])\n";
   pr "{\n";
   List.iter (
   pr "int run_action (const char *cmd, int argc, char *argv[])\n";
   pr "{\n";
   List.iter (
-    fun (name, _, _, _, _) ->
+    fun (name, _, _, _, _, _) ->
       pr "  if (strcasecmp (cmd, \"%s\") == 0)\n" name;
       pr "    return run_%s (cmd, argc, argv);\n" name;
       pr "  else\n";
       pr "  if (strcasecmp (cmd, \"%s\") == 0)\n" name;
       pr "    return run_%s (cmd, argc, argv);\n" name;
       pr "  else\n";
@@ -575,8 +672,10 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
     ?handle name style =
   if extern then pr "extern ";
   if static then pr "static ";
     ?handle name style =
   if extern then pr "extern ";
   if static then pr "static ";
-  (match style with
-   | (Err, _) -> pr "int "
+  (match fst style with
+   | Err -> pr "int "
+   | RString _ -> pr "char *"
+   | RStringList _ -> pr "char **"
   );
   pr "%s (" name;
   let comma = ref false in
   );
   pr "%s (" name;
   let comma = ref false in
index 484bb8d..989424d 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+struct cat_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_cat_ret ret;
+};
+
+static void cat_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct cat_rv *rv = (struct cat_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_cat: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_cat: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_cat_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_cat: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+char *guestfs_cat (guestfs_h *g,
+               const char *path)
+{
+  struct guestfs_cat_args args;
+  struct cat_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_cat called from the wrong state, %d != READY",
+      g->state);
+    return NULL;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.path = (char *) path;
+  serial = dispatch (g, GUESTFS_PROC_CAT,
+                     (xdrproc_t) xdr_guestfs_cat_args, (char *) &args);
+  if (serial == -1)
+    return NULL;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = cat_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_cat failed, see earlier error messages");
+    return NULL;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_CAT, serial) == -1)
+    return NULL;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return NULL;
+  }
+
+  return rv.ret.content; /* caller will free */
+}
+
+struct ll_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_ll_ret ret;
+};
+
+static void ll_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct ll_rv *rv = (struct ll_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_ll: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_ll: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_ll_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_ll: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+char *guestfs_ll (guestfs_h *g,
+               const char *directory)
+{
+  struct guestfs_ll_args args;
+  struct ll_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_ll called from the wrong state, %d != READY",
+      g->state);
+    return NULL;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.directory = (char *) directory;
+  serial = dispatch (g, GUESTFS_PROC_LL,
+                     (xdrproc_t) xdr_guestfs_ll_args, (char *) &args);
+  if (serial == -1)
+    return NULL;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = ll_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_ll failed, see earlier error messages");
+    return NULL;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LL, serial) == -1)
+    return NULL;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return NULL;
+  }
+
+  return rv.ret.listing; /* caller will free */
+}
+
+struct ls_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_ls_ret ret;
+};
+
+static void ls_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct ls_rv *rv = (struct ls_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_ls: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_ls: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_ls_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_ls: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+char **guestfs_ls (guestfs_h *g,
+               const char *directory)
+{
+  struct guestfs_ls_args args;
+  struct ls_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_ls called from the wrong state, %d != READY",
+      g->state);
+    return NULL;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.directory = (char *) directory;
+  serial = dispatch (g, GUESTFS_PROC_LS,
+                     (xdrproc_t) xdr_guestfs_ls_args, (char *) &args);
+  if (serial == -1)
+    return NULL;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = ls_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_ls failed, see earlier error messages");
+    return NULL;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LS, 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.listing.listing_val = safe_realloc (g, rv.ret.listing.listing_val, rv.ret.listing.listing_len + 1);
+  rv.ret.listing.listing_val[rv.ret.listing.listing_len] = NULL;
+  return rv.ret.listing.listing_val;
+}
+
 struct mount_rv {
   int cb_done;  /* flag to indicate callback was called */
   struct guestfs_message_header hdr;
 struct mount_rv {
   int cb_done;  /* flag to indicate callback was called */
   struct guestfs_message_header hdr;
index 31ee734..7f14304 100644 (file)
@@ -19,6 +19,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+extern char *guestfs_cat (guestfs_h *handle, const char *path);
+extern char *guestfs_ll (guestfs_h *handle, const char *directory);
+extern char **guestfs_ls (guestfs_h *handle, const char *directory);
 extern int guestfs_mount (guestfs_h *handle, const char *device, const char *mountpoint);
 extern int guestfs_sync (guestfs_h *handle);
 extern int guestfs_touch (guestfs_h *handle, const char *path);
 extern int guestfs_mount (guestfs_h *handle, const char *device, const char *mountpoint);
 extern int guestfs_sync (guestfs_h *handle);
 extern int guestfs_touch (guestfs_h *handle, const char *path);
index aa5f4e3..c51aa5d 100644 (file)
@@ -6,6 +6,77 @@
 #include "guestfs_protocol.h"
 
 bool_t
 #include "guestfs_protocol.h"
 
 bool_t
+xdr_str (XDR *xdrs, str *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, objp, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_cat_args (XDR *xdrs, guestfs_cat_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->path, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_cat_ret (XDR *xdrs, guestfs_cat_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->content, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_ll_args (XDR *xdrs, guestfs_ll_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->directory, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_ll_ret (XDR *xdrs, guestfs_ll_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->listing, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_ls_args (XDR *xdrs, guestfs_ls_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->directory, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_ls_ret (XDR *xdrs, guestfs_ls_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->listing.listing_val, (u_int *) &objp->listing.listing_len, ~0,
+               sizeof (str), (xdrproc_t) xdr_str))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
 xdr_guestfs_mount_args (XDR *xdrs, guestfs_mount_args *objp)
 {
        register int32_t *buf;
 xdr_guestfs_mount_args (XDR *xdrs, guestfs_mount_args *objp)
 {
        register int32_t *buf;
index f8c8043..a83ad00 100644 (file)
@@ -14,6 +14,41 @@ extern "C" {
 #endif
 
 
 #endif
 
 
+typedef char *str;
+
+struct guestfs_cat_args {
+       char *path;
+};
+typedef struct guestfs_cat_args guestfs_cat_args;
+
+struct guestfs_cat_ret {
+       char *content;
+};
+typedef struct guestfs_cat_ret guestfs_cat_ret;
+
+struct guestfs_ll_args {
+       char *directory;
+};
+typedef struct guestfs_ll_args guestfs_ll_args;
+
+struct guestfs_ll_ret {
+       char *listing;
+};
+typedef struct guestfs_ll_ret guestfs_ll_ret;
+
+struct guestfs_ls_args {
+       char *directory;
+};
+typedef struct guestfs_ls_args guestfs_ls_args;
+
+struct guestfs_ls_ret {
+       struct {
+               u_int listing_len;
+               str *listing_val;
+       } listing;
+};
+typedef struct guestfs_ls_ret guestfs_ls_ret;
+
 struct guestfs_mount_args {
        char *device;
        char *mountpoint;
 struct guestfs_mount_args {
        char *device;
        char *mountpoint;
@@ -26,6 +61,9 @@ struct guestfs_touch_args {
 typedef struct guestfs_touch_args guestfs_touch_args;
 
 enum guestfs_procedure {
 typedef struct guestfs_touch_args guestfs_touch_args;
 
 enum guestfs_procedure {
+       GUESTFS_PROC_CAT = 4,
+       GUESTFS_PROC_LL = 5,
+       GUESTFS_PROC_LS = 6,
        GUESTFS_PROC_MOUNT = 1,
        GUESTFS_PROC_SYNC = 2,
        GUESTFS_PROC_TOUCH = 3,
        GUESTFS_PROC_MOUNT = 1,
        GUESTFS_PROC_SYNC = 2,
        GUESTFS_PROC_TOUCH = 3,
@@ -67,6 +105,13 @@ typedef struct guestfs_message_header guestfs_message_header;
 /* the xdr functions */
 
 #if defined(__STDC__) || defined(__cplusplus)
 /* the xdr functions */
 
 #if defined(__STDC__) || defined(__cplusplus)
+extern  bool_t xdr_str (XDR *, str*);
+extern  bool_t xdr_guestfs_cat_args (XDR *, guestfs_cat_args*);
+extern  bool_t xdr_guestfs_cat_ret (XDR *, guestfs_cat_ret*);
+extern  bool_t xdr_guestfs_ll_args (XDR *, guestfs_ll_args*);
+extern  bool_t xdr_guestfs_ll_ret (XDR *, guestfs_ll_ret*);
+extern  bool_t xdr_guestfs_ls_args (XDR *, guestfs_ls_args*);
+extern  bool_t xdr_guestfs_ls_ret (XDR *, guestfs_ls_ret*);
 extern  bool_t xdr_guestfs_mount_args (XDR *, guestfs_mount_args*);
 extern  bool_t xdr_guestfs_touch_args (XDR *, guestfs_touch_args*);
 extern  bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*);
 extern  bool_t xdr_guestfs_mount_args (XDR *, guestfs_mount_args*);
 extern  bool_t xdr_guestfs_touch_args (XDR *, guestfs_touch_args*);
 extern  bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*);
@@ -76,6 +121,13 @@ extern  bool_t xdr_guestfs_message_error (XDR *, guestfs_message_error*);
 extern  bool_t xdr_guestfs_message_header (XDR *, guestfs_message_header*);
 
 #else /* K&R C */
 extern  bool_t xdr_guestfs_message_header (XDR *, guestfs_message_header*);
 
 #else /* K&R C */
+extern bool_t xdr_str ();
+extern bool_t xdr_guestfs_cat_args ();
+extern bool_t xdr_guestfs_cat_ret ();
+extern bool_t xdr_guestfs_ll_args ();
+extern bool_t xdr_guestfs_ll_ret ();
+extern bool_t xdr_guestfs_ls_args ();
+extern bool_t xdr_guestfs_ls_ret ();
 extern bool_t xdr_guestfs_mount_args ();
 extern bool_t xdr_guestfs_touch_args ();
 extern bool_t xdr_guestfs_procedure ();
 extern bool_t xdr_guestfs_mount_args ();
 extern bool_t xdr_guestfs_touch_args ();
 extern bool_t xdr_guestfs_procedure ();
index 106bb60..df3e936 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+typedef string str<>;
+
+/* guestfs_cat */
+
+struct guestfs_cat_args {
+  string path<>;
+};
+
+struct guestfs_cat_ret {
+  string content<>;
+};
+
+/* guestfs_ll */
+
+struct guestfs_ll_args {
+  string directory<>;
+};
+
+struct guestfs_ll_ret {
+  string listing<>;
+};
+
+/* guestfs_ls */
+
+struct guestfs_ls_args {
+  string directory<>;
+};
+
+struct guestfs_ls_ret {
+  str listing<>;
+};
+
 /* guestfs_mount */
 
 struct guestfs_mount_args {
 /* guestfs_mount */
 
 struct guestfs_mount_args {
@@ -35,6 +67,9 @@ struct guestfs_touch_args {
 };
 
 enum guestfs_procedure {
 };
 
 enum guestfs_procedure {
+  GUESTFS_PROC_CAT = 4,
+  GUESTFS_PROC_LL = 5,
+  GUESTFS_PROC_LS = 6,
   GUESTFS_PROC_MOUNT = 1,
   GUESTFS_PROC_SYNC = 2,
   GUESTFS_PROC_TOUCH = 3,
   GUESTFS_PROC_MOUNT = 1,
   GUESTFS_PROC_SYNC = 2,
   GUESTFS_PROC_TOUCH = 3,