Implement list-devices and list-partitions.
authorRichard Jones <rjones@redhat.com>
Mon, 6 Apr 2009 11:55:26 +0000 (12:55 +0100)
committerRichard Jones <rjones@redhat.com>
Mon, 6 Apr 2009 11:55:26 +0000 (12:55 +0100)
15 files changed:
daemon/Makefile.am
daemon/actions.h
daemon/daemon.h
daemon/devsparts.c [new file with mode: 0644]
daemon/guestfsd.c
daemon/ls.c
daemon/stubs.c
fish/cmds.c
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 76e75c6..5faa652 100644 (file)
@@ -21,6 +21,7 @@ noinst_PROGRAMS = guestfsd
 guestfsd_SOURCES = \
        actions.h \
        daemon.h \
+       devsparts.c \
        file.c \
        guestfsd.c \
        ls.c \
index c4b9b15..58b8a24 100644 (file)
  * 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 char *do_cat (const char *path);
+extern char *do_ll (const char *directory);
+extern char **do_ls (const char *directory);
+extern char **do_list_devices ();
+extern char **do_list_partitions ();
index 3740595..60e1982 100644 (file)
@@ -29,7 +29,9 @@ extern void xread (int sock, void *buf, size_t len);
 
 extern int add_string (char ***argv, int *size, int *alloc, const char *str);
 extern int count_strings (char **argv);
+extern void sort_strings (char **argv, int len);
 extern void free_strings (char **argv);
+extern void free_stringslen (char **argv, int len);
 
 extern int command (char **stdoutput, char **stderror, const char *name, ...);
 
diff --git a/daemon/devsparts.c b/daemon/devsparts.c
new file mode 100644 (file)
index 0000000..b0d7956
--- /dev/null
@@ -0,0 +1,133 @@
+/* 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 <dirent.h>
+#include <sys/stat.h>
+
+#include "daemon.h"
+#include "actions.h"
+
+char **
+do_list_devices (void)
+{
+  char **r = NULL;
+  int size = 0, alloc = 0;
+  DIR *dir;
+  struct dirent *d;
+  char buf[256];
+
+  dir = opendir ("/sys/block");
+  if (!dir) {
+    reply_with_perror ("opendir: /sys/block");
+    return NULL;
+  }
+
+  while ((d = readdir (dir)) != NULL) {
+    if (strncmp (d->d_name, "sd", 2) == 0) {
+      snprintf (buf, sizeof buf, "/dev/%s", d->d_name);
+      if (add_string (&r, &size, &alloc, buf) == -1) {
+       closedir (dir);
+       return NULL;
+      }
+    }
+  }
+
+  if (add_string (&r, &size, &alloc, NULL) == -1) {
+    closedir (dir);
+    return NULL;
+  }
+
+  if (closedir (dir) == -1) {
+    reply_with_perror ("closedir: /sys/block");
+    free_strings (r);
+    return NULL;
+  }
+
+  sort_strings (r, size-1);
+  return r;
+}
+
+char **
+do_list_partitions (void)
+{
+  char **r = NULL;
+  int size = 0, alloc = 0;
+  DIR *dir, *dir2;
+  struct dirent *d;
+  char buf[256], devname[256];
+
+  dir = opendir ("/sys/block");
+  if (!dir) {
+    reply_with_perror ("opendir: /sys/block");
+    return NULL;
+  }
+
+  while ((d = readdir (dir)) != NULL) {
+    if (strncmp (d->d_name, "sd", 2) == 0) {
+      strncpy (devname, d->d_name, sizeof devname);
+      devname[sizeof devname - 1] = '\0';
+
+      snprintf (buf, sizeof buf, "/sys/block/%s", devname);
+
+      dir2 = opendir (buf);
+      if (!dir2) {
+       reply_with_perror ("opendir: %s", buf);
+       free_stringslen (r, size);
+       return NULL;
+      }
+      while ((d = readdir (dir2)) != NULL) {
+       if (strncmp (d->d_name, devname, strlen (devname)) == 0) {
+         snprintf (buf, sizeof buf, "/dev/%s", d->d_name);
+
+         if (add_string (&r, &size, &alloc, buf) == -1) {
+           closedir (dir2);
+           closedir (dir);
+           return NULL;
+         }
+       }
+      }
+
+      if (closedir (dir2) == -1) {
+       reply_with_perror ("closedir: /sys/block/%s", devname);
+       free_stringslen (r, size);
+       return NULL;
+      }
+    }
+  }
+
+  if (add_string (&r, &size, &alloc, NULL) == -1) {
+    closedir (dir);
+    return NULL;
+  }
+
+  if (closedir (dir) == -1) {
+    reply_with_perror ("closedir: /sys/block");
+    free_strings (r);
+    return NULL;
+  }
+
+  sort_strings (r, size-1);
+  return r;
+}
index 6730c1d..a243c0b 100644 (file)
@@ -272,6 +272,20 @@ count_strings (char **argv)
   return argc;
 }
 
+static int
+compare (const void *vp1, const void *vp2)
+{
+  char * const *p1 = (char * const *) vp1;
+  char * const *p2 = (char * const *) vp2;
+  return strcmp (*p1, *p2);
+}
+
+void
+sort_strings (char **argv, int len)
+{
+  qsort (argv, len, sizeof (char *), compare);
+}
+
 void
 free_strings (char **argv)
 {
@@ -282,6 +296,16 @@ free_strings (char **argv)
   free (argv);
 }
 
+void
+free_stringslen (char **argv, int len)
+{
+  int i;
+
+  for (i = 0; i < len; ++i)
+    free (argv[i]);
+  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
index 05e2cc7..261bc96 100644 (file)
 #include "daemon.h"
 #include "actions.h"
 
-static int
-compare (const void *vp1, const void *vp2)
-{
-  char * const *p1 = (char * const *) vp1;
-  char * const *p2 = (char * const *) vp2;
-  return strcmp (*p1, *p2);
-}
-
 char **
 do_ls (const char *path)
 {
@@ -78,7 +70,7 @@ do_ls (const char *path)
     return NULL;
   }
 
-  qsort (r, size-1, sizeof (char *), compare);
+  sort_strings (r, size-1);
   return r;
 }
 
index 4eccfb5..faece82 100644 (file)
 #include "../src/guestfs_protocol.h"
 #include "actions.h"
 
+static void mount_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_mount_args args;
+  const char *device;
+  const char *mountpoint;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_mount_args (xdr_in, &args)) {
+    reply_with_error ("mount: daemon failed to decode procedure arguments");
+    return;
+  }
+  device = args.device;
+  mountpoint = args.mountpoint;
+
+  r = do_mount (device, mountpoint);
+  if (r == -1)
+    /* do_mount has already called reply_with_error, so just return */
+    return;
+
+  reply (NULL, NULL);
+}
+
+static void sync_stub (XDR *xdr_in)
+{
+  int r;
+
+  r = do_sync ();
+  if (r == -1)
+    /* do_sync has already called reply_with_error, so just return */
+    return;
+
+  reply (NULL, NULL);
+}
+
+static void touch_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_touch_args args;
+  const char *path;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_touch_args (xdr_in, &args)) {
+    reply_with_error ("touch: daemon failed to decode procedure arguments");
+    return;
+  }
+  path = args.path;
+
+  r = do_touch (path);
+  if (r == -1)
+    /* do_touch has already called reply_with_error, so just return */
+    return;
+
+  reply (NULL, NULL);
+}
+
 static void cat_stub (XDR *xdr_in)
 {
   char *r;
@@ -101,67 +159,50 @@ static void ls_stub (XDR *xdr_in)
   free_strings (r);
 }
 
-static void mount_stub (XDR *xdr_in)
-{
-  int r;
-  struct guestfs_mount_args args;
-  const char *device;
-  const char *mountpoint;
-
-  memset (&args, 0, sizeof args);
-
-  if (!xdr_guestfs_mount_args (xdr_in, &args)) {
-    reply_with_error ("mount: daemon failed to decode procedure arguments");
-    return;
-  }
-  device = args.device;
-  mountpoint = args.mountpoint;
-
-  r = do_mount (device, mountpoint);
-  if (r == -1)
-    /* do_mount has already called reply_with_error, so just return */
-    return;
-
-  reply (NULL, NULL);
-}
-
-static void sync_stub (XDR *xdr_in)
+static void list_devices_stub (XDR *xdr_in)
 {
-  int r;
+  char **r;
 
-  r = do_sync ();
-  if (r == -1)
-    /* do_sync has already called reply_with_error, so just return */
+  r = do_list_devices ();
+  if (r == NULL)
+    /* do_list_devices has already called reply_with_error, so just return */
     return;
 
-  reply (NULL, NULL);
+  struct guestfs_list_devices_ret ret;
+  ret.devices.devices_len = count_strings (r);
+  ret.devices.devices_val = r;
+  reply ((xdrproc_t) &xdr_guestfs_list_devices_ret, (char *) &ret);
+  free_strings (r);
 }
 
-static void touch_stub (XDR *xdr_in)
+static void list_partitions_stub (XDR *xdr_in)
 {
-  int r;
-  struct guestfs_touch_args args;
-  const char *path;
-
-  memset (&args, 0, sizeof args);
-
-  if (!xdr_guestfs_touch_args (xdr_in, &args)) {
-    reply_with_error ("touch: daemon failed to decode procedure arguments");
-    return;
-  }
-  path = args.path;
+  char **r;
 
-  r = do_touch (path);
-  if (r == -1)
-    /* do_touch has already called reply_with_error, so just return */
+  r = do_list_partitions ();
+  if (r == NULL)
+    /* do_list_partitions has already called reply_with_error, so just return */
     return;
 
-  reply (NULL, NULL);
+  struct guestfs_list_partitions_ret ret;
+  ret.partitions.partitions_len = count_strings (r);
+  ret.partitions.partitions_val = r;
+  reply ((xdrproc_t) &xdr_guestfs_list_partitions_ret, (char *) &ret);
+  free_strings (r);
 }
 
 void dispatch_incoming_message (XDR *xdr_in)
 {
   switch (proc_nr) {
+    case GUESTFS_PROC_MOUNT:
+      mount_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_SYNC:
+      sync_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_TOUCH:
+      touch_stub (xdr_in);
+      break;
     case GUESTFS_PROC_CAT:
       cat_stub (xdr_in);
       break;
@@ -171,14 +212,11 @@ void dispatch_incoming_message (XDR *xdr_in)
     case GUESTFS_PROC_LS:
       ls_stub (xdr_in);
       break;
-    case GUESTFS_PROC_MOUNT:
-      mount_stub (xdr_in);
+    case GUESTFS_PROC_LIST_DEVICES:
+      list_devices_stub (xdr_in);
       break;
-    case GUESTFS_PROC_SYNC:
-      sync_stub (xdr_in);
-      break;
-    case GUESTFS_PROC_TOUCH:
-      touch_stub (xdr_in);
+    case GUESTFS_PROC_LIST_PARTITIONS:
+      list_partitions_stub (xdr_in);
       break;
     default:
       reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr);
index 3116c94..28dd956 100644 (file)
@@ -30,6 +30,8 @@ void list_commands (void)
   printf ("    %-16s     %s\n", "Command", "Description");
   list_builtin_commands ();
   printf ("%-20s %s\n", "cat", "list the contents of a file");
+  printf ("%-20s %s\n", "list-devices", "list the block devices");
+  printf ("%-20s %s\n", "list-partitions", "list the partitions");
   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");
@@ -40,8 +42,17 @@ void list_commands (void)
 
 void display_command (const char *cmd)
 {
+  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, "sync") == 0)
+    pod2text ("sync - sync disks, writes are flushed through to the disk image", " sync\n\nThis syncs the disk, so that any writes are flushed through to the\nunderlying disk image.\n\nYou should always call this if you have modified a disk image, before\ncalling C<guestfs_close>.");
+  else
+  if (strcasecmp (cmd, "touch") == 0)
+    pod2text ("touch - update file timestamps or create a new file", " touch <path>\n\nTouch acts like the L<touch(1)> command.  It can be used to\nupdate the timestamps on a file, or, if the file does not exist,\nto create a new zero-length file.");
+  else
   if (strcasecmp (cmd, "cat") == 0)
-    pod2text ("cat - list the contents of a file", " cat <path>\n\nReturn the contents of the file named C<path>.\n\nNote that this function cannot correctly handle binary files\n(specifically, files containing C<\\0> character which is treated\nas end of string).  For those you need to use the C<guestfs_read>\nfunction which has a more complex interface.\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.");
+    pod2text ("cat - list the contents of a file", " cat <path>\n\nReturn the contents of the file named C<path>.\n\nNote that this function cannot correctly handle binary files\n(specifically, files containing C<\\0> character which is treated\nas end of string).  For those you need to use the C<guestfs_read_file>\nfunction which has a more complex interface.\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.");
@@ -49,18 +60,57 @@ void display_command (const char *cmd)
   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.  Programs\nshould probably use C<guestfs_readdir> instead.");
   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.");
+  if (strcasecmp (cmd, "list_devices") == 0 || strcasecmp (cmd, "list-devices") == 0)
+    pod2text ("list-devices - list the block devices", " list-devices\n\nList all the block devices.\n\nThe full block device names are returned, eg. C</dev/sda>\n");
   else
-  if (strcasecmp (cmd, "sync") == 0)
-    pod2text ("sync - sync disks, writes are flushed through to the disk image", " sync\n\nThis syncs the disk, so that any writes are flushed through to the\nunderlying disk image.\n\nYou should always call this if you have modified a disk image, before\ncalling C<guestfs_close>.");
-  else
-  if (strcasecmp (cmd, "touch") == 0)
-    pod2text ("touch - update file timestamps or create a new file", " touch <path>\n\nTouch acts like the L<touch(1)> command.  It can be used to\nupdate the timestamps on a file, or, if the file does not exist,\nto create a new zero-length file.");
+  if (strcasecmp (cmd, "list_partitions") == 0 || strcasecmp (cmd, "list-partitions") == 0)
+    pod2text ("list-partitions - list the partitions", " list-partitions\n\nList all the partitions detected on all block devices.\n\nThe full partition device names are returned, eg. C</dev/sda1>\n\nThis does not return logical volumes.  For that you will need to\ncall C<guestfs_lvs>.");
   else
     display_builtin_command (cmd);
 }
 
+static int run_mount (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *device;
+  const char *mountpoint;
+  if (argc != 2) {
+    fprintf (stderr, "%s should have 2 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  device = argv[0];
+  mountpoint = argv[1];
+  r = guestfs_mount (g, device, mountpoint);
+  return r;
+}
+
+static int run_sync (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  if (argc != 0) {
+    fprintf (stderr, "%s should have 0 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  r = guestfs_sync (g);
+  return r;
+}
+
+static int run_touch (const char *cmd, int argc, char *argv[])
+{
+  int 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_touch (g, path);
+  return r;
+}
+
 static int run_cat (const char *cmd, int argc, char *argv[])
 {
   char *r;
@@ -112,50 +162,47 @@ static int run_ls (const char *cmd, int argc, char *argv[])
   return 0;
 }
 
-static int run_mount (const char *cmd, int argc, char *argv[])
+static int run_list_devices (const char *cmd, int argc, char *argv[])
 {
-  int r;
-  const char *device;
-  const char *mountpoint;
-  if (argc != 2) {
-    fprintf (stderr, "%s should have 2 parameter(s)\n", cmd);
-    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
-    return -1;
-  }
-  device = argv[0];
-  mountpoint = argv[1];
-  r = guestfs_mount (g, device, mountpoint);
-  return r;
-}
-
-static int run_sync (const char *cmd, int argc, char *argv[])
-{
-  int r;
+  char **r;
   if (argc != 0) {
     fprintf (stderr, "%s should have 0 parameter(s)\n", cmd);
     fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
     return -1;
   }
-  r = guestfs_sync (g);
-  return r;
+  r = guestfs_list_devices (g);
+  if (r == NULL) return -1;
+  print_strings (r);
+  free_strings (r);
+  return 0;
 }
 
-static int run_touch (const char *cmd, int argc, char *argv[])
+static int run_list_partitions (const char *cmd, int argc, char *argv[])
 {
-  int r;
-  const char *path;
-  if (argc != 1) {
-    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+  char **r;
+  if (argc != 0) {
+    fprintf (stderr, "%s should have 0 parameter(s)\n", cmd);
     fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
     return -1;
   }
-  path = argv[0];
-  r = guestfs_touch (g, path);
-  return r;
+  r = guestfs_list_partitions (g);
+  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, "mount") == 0)
+    return run_mount (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "sync") == 0)
+    return run_sync (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "touch") == 0)
+    return run_touch (cmd, argc, argv);
+  else
   if (strcasecmp (cmd, "cat") == 0)
     return run_cat (cmd, argc, argv);
   else
@@ -165,14 +212,11 @@ int run_action (const char *cmd, int argc, char *argv[])
   if (strcasecmp (cmd, "ls") == 0)
     return run_ls (cmd, argc, argv);
   else
-  if (strcasecmp (cmd, "mount") == 0)
-    return run_mount (cmd, argc, argv);
+  if (strcasecmp (cmd, "list_devices") == 0 || strcasecmp (cmd, "list-devices") == 0)
+    return run_list_devices (cmd, argc, argv);
   else
-  if (strcasecmp (cmd, "sync") == 0)
-    return run_sync (cmd, argc, argv);
-  else
-  if (strcasecmp (cmd, "touch") == 0)
-    return run_touch (cmd, argc, argv);
+  if (strcasecmp (cmd, "list_partitions") == 0 || strcasecmp (cmd, "list-partitions") == 0)
+    return run_list_partitions (cmd, argc, argv);
   else
     {
       fprintf (stderr, "%s: unknown command\n", cmd);
index eec6697..f89ae26 100644 (file)
@@ -7,16 +7,44 @@ Return the contents of the file named C<path>.
 
 Note that this function cannot correctly handle binary files
 (specifically, files containing C<\0> character which is treated
-as end of string).  For those you need to use the C<guestfs_read>
+as end of string).  For those you need to use the C<guestfs_read_file>
 function which has a more complex interface.
 
-This function returns a string or NULL on error.  The caller
-must free the returned string after use.
+This function returns a string or NULL on error.
+I<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_list_devices
+
+ char **guestfs_list_devices (guestfs_h *handle);
+
+List all the block devices.
+
+The full block device names are returned, eg. C</dev/sda>
+
+
+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_list_partitions
+
+ char **guestfs_list_partitions (guestfs_h *handle);
+
+List all the partitions detected on all block devices.
+
+The full partition device names are returned, eg. C</dev/sda1>
+
+This does not return logical volumes.  For that you will need to
+call C<guestfs_lvs>.
+
+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_ll
 
  char *guestfs_ll (guestfs_h *handle,
@@ -28,8 +56,8 @@ 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.
+This function returns a string or NULL on error.
+I<The caller must free the returned string after use>.
 
 =head2 guestfs_ls
 
@@ -45,8 +73,7 @@ should probably use C<guestfs_readdir> instead.
 
 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.
+I<The caller must free the strings and the array after use>.
 
 =head2 guestfs_mount
 
index da35a0f..12c51fc 100755 (executable)
@@ -48,35 +48,6 @@ and argt =
 type flags = ProtocolLimitWarning
 
 let functions = [
-  ("cat", (RString "content", P1 (String "path")), 4, [ProtocolLimitWarning],
-   "list the contents of a file",
-   "\
-Return the contents of the file named C<path>.
-
-Note that this function cannot correctly handle binary files
-(specifically, files containing C<\\0> character which is treated
-as end of string).  For those you need to use the C<guestfs_read>
-function which has a more complex interface.");
-
-  ("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.  Programs
-should probably use C<guestfs_readdir> instead.");
-
   ("mount", (Err, P2 (String "device", String "mountpoint")), 1, [],
    "mount a guest disk at a position in the filesystem",
    "\
@@ -112,8 +83,74 @@ calling C<guestfs_close>.");
 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.");
+
+  ("cat", (RString "content", P1 (String "path")), 4, [ProtocolLimitWarning],
+   "list the contents of a file",
+   "\
+Return the contents of the file named C<path>.
+
+Note that this function cannot correctly handle binary files
+(specifically, files containing C<\\0> character which is treated
+as end of string).  For those you need to use the C<guestfs_read_file>
+function which has a more complex interface.");
+
+  ("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.  Programs
+should probably use C<guestfs_readdir> instead.");
+
+  ("list_devices", (RStringList "devices", P0), 7, [],
+   "list the block devices",
+   "\
+List all the block devices.
+
+The full block device names are returned, eg. C</dev/sda>
+");
+
+  ("list_partitions", (RStringList "partitions", P0), 8, [],
+   "list the partitions",
+   "\
+List all the partitions detected on all block devices.
+
+The full partition device names are returned, eg. C</dev/sda1>
+
+This does not return logical volumes.  For that you will need to
+call C<guestfs_lvs>.");
 ]
 
+(* In some places we want the functions to be displayed sorted
+ * alphabetically, so this is useful:
+ *)
+let sorted_functions =
+  List.sort (fun (n1,_,_,_,_,_) (n2,_,_,_,_,_) -> compare n1 n2) functions
+
+(* Useful functions. *)
+let failwithf fs = ksprintf failwith fs
+let replace s c1 c2 =
+  let s2 = String.copy s in
+  let r = ref false in
+  for i = 0 to String.length s2 - 1 do
+    if String.unsafe_get s2 i = c1 then (
+      String.unsafe_set s2 i c2;
+      r := true
+    )
+  done;
+  if not !r then s else s2
+
 (* 'pr' prints to the current output file. *)
 let chan = ref stdout
 let pr fs = ksprintf (output_string !chan) fs
@@ -135,6 +172,30 @@ let map_args f = function
 
 let nr_args = function | P0 -> 0 | P1 _ -> 1 | P2 _ -> 2
 
+(* Check function names etc. for consistency. *)
+let check_functions () =
+  List.iter (
+    fun (name, _, _, _, _, _) ->
+      if String.contains name '-' then
+       failwithf "Function name '%s' should not contain '-', use '_' instead."
+         name
+  ) functions;
+
+  let proc_nrs =
+    List.map (fun (name, _, proc_nr, _, _, _) -> name, proc_nr) functions in
+  let proc_nrs =
+    List.sort (fun (_,nr1) (_,nr2) -> compare nr1 nr2) proc_nrs in
+  let rec loop = function
+    | [] -> ()
+    | [_] -> ()
+    | (name1,nr1) :: ((name2,nr2) :: _ as rest) when nr1 < nr2 ->
+       loop rest
+    | (name1,nr1) :: (name2,nr2) :: _ ->
+       failwithf "'%s' and '%s' have conflicting procedure numbers (%d, %d)"
+         name1 name2 nr1 nr2
+  in
+  loop proc_nrs
+
 type comment_style = CStyle | HashStyle | OCamlStyle
 type license = GPLv2 | LGPLv2
 
@@ -202,19 +263,18 @@ and generate_pod () =
        | 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"
+          pr "This function returns a string or NULL on error.
+I<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"
+I<The caller must free the strings 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
+  ) sorted_functions
 
 (* Generate the protocol (XDR) file. *)
 and generate_xdr () =
@@ -562,9 +622,10 @@ and generate_fish_cmds () =
   pr "  list_builtin_commands ();\n";
   List.iter (
     fun (name, _, _, _, shortdesc, _) ->
+      let name = replace name '_' '-' in
       pr "  printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
        name shortdesc
-  ) functions;
+  ) sorted_functions;
   pr "  printf (\"    Use -h <cmd> / help <cmd> to show detailed help for a command.\\n\");\n";
   pr "}\n";
   pr "\n";
@@ -574,12 +635,13 @@ and generate_fish_cmds () =
   pr "{\n";
   List.iter (
     fun (name, style, _, flags, shortdesc, longdesc) ->
+      let name2 = replace name '_' '-' in
       let synopsis =
        match snd style with
-       | P0 -> name
+       | P0 -> name2
        | args ->
            sprintf "%s <%s>"
-             name (
+             name2 (
                String.concat "> <" (
                  map_args (function
                            | String n -> n) args
@@ -593,9 +655,13 @@ 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 "  if (";
+      pr "strcasecmp (cmd, \"%s\") == 0" name;
+      if name <> name2 then
+       pr " || strcasecmp (cmd, \"%s\") == 0" name2;
+      pr ")\n";
       pr "    pod2text (\"%s - %s\", %S);\n"
-       name shortdesc
+       name2 shortdesc
        (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings);
       pr "  else\n"
   ) functions;
@@ -660,7 +726,12 @@ FTP."
   pr "{\n";
   List.iter (
     fun (name, _, _, _, _, _) ->
-      pr "  if (strcasecmp (cmd, \"%s\") == 0)\n" name;
+      let name2 = replace name '_' '-' in
+      pr "  if (";
+      pr "strcasecmp (cmd, \"%s\") == 0" name;
+      if name <> name2 then
+       pr " || strcasecmp (cmd, \"%s\") == 0" name2;
+      pr ")\n";
       pr "    return run_%s (cmd, argc, argv);\n" name;
       pr "  else\n";
   ) functions;
@@ -733,6 +804,8 @@ let output_to filename =
 
 (* Main program. *)
 let () =
+  check_functions ();
+
   let close = output_to "src/guestfs_protocol.x" in
   generate_xdr ();
   close ();
index 989424d..d2ec0c7 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+struct mount_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void mount_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct mount_rv *rv = (struct mount_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_mount: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_mount: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_mount (guestfs_h *g,
+               const char *device,
+               const char *mountpoint)
+{
+  struct guestfs_mount_args args;
+  struct mount_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_mount called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.device = (char *) device;
+  args.mountpoint = (char *) mountpoint;
+  serial = dispatch (g, GUESTFS_PROC_MOUNT,
+                     (xdrproc_t) xdr_guestfs_mount_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = mount_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_mount failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_MOUNT, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
+struct sync_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void sync_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct sync_rv *rv = (struct sync_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_sync: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_sync: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_sync (guestfs_h *g)
+{
+  struct sync_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_sync called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  serial = dispatch (g, GUESTFS_PROC_SYNC, NULL, NULL);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = sync_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_sync failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_SYNC, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
+struct touch_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+};
+
+static void touch_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct touch_rv *rv = (struct touch_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_touch: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_touch: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+int guestfs_touch (guestfs_h *g,
+               const char *path)
+{
+  struct guestfs_touch_args args;
+  struct touch_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_touch called from the wrong state, %d != READY",
+      g->state);
+    return -1;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  args.path = (char *) path;
+  serial = dispatch (g, GUESTFS_PROC_TOUCH,
+                     (xdrproc_t) xdr_guestfs_touch_args, (char *) &args);
+  if (serial == -1)
+    return -1;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = touch_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_touch failed, see earlier error messages");
+    return -1;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_TOUCH, serial) == -1)
+    return -1;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return -1;
+  }
+
+  return 0;
+}
+
 struct cat_rv {
   int cb_done;  /* flag to indicate callback was called */
   struct guestfs_message_header hdr;
@@ -244,208 +449,149 @@ char **guestfs_ls (guestfs_h *g,
   return rv.ret.listing.listing_val;
 }
 
-struct mount_rv {
+struct list_devices_rv {
   int cb_done;  /* flag to indicate callback was called */
   struct guestfs_message_header hdr;
   struct guestfs_message_error err;
+  struct guestfs_list_devices_ret ret;
 };
 
-static void mount_cb (guestfs_h *g, void *data, XDR *xdr)
+static void list_devices_cb (guestfs_h *g, void *data, XDR *xdr)
 {
-  struct mount_rv *rv = (struct mount_rv *) data;
+  struct list_devices_rv *rv = (struct list_devices_rv *) data;
 
   if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
-    error (g, "guestfs_mount: failed to parse reply header");
+    error (g, "guestfs_list_devices: failed to parse reply header");
     return;
   }
   if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
     if (!xdr_guestfs_message_error (xdr, &rv->err)) {
-      error (g, "guestfs_mount: failed to parse reply error");
+      error (g, "guestfs_list_devices: failed to parse reply error");
       return;
     }
     goto done;
   }
- done:
-  rv->cb_done = 1;
-  main_loop.main_loop_quit (g);
-}
-
-int guestfs_mount (guestfs_h *g,
-               const char *device,
-               const char *mountpoint)
-{
-  struct guestfs_mount_args args;
-  struct mount_rv rv;
-  int serial;
-
-  if (g->state != READY) {
-    error (g, "guestfs_mount called from the wrong state, %d != READY",
-      g->state);
-    return -1;
-  }
-
-  memset (&rv, 0, sizeof rv);
-
-  args.device = (char *) device;
-  args.mountpoint = (char *) mountpoint;
-  serial = dispatch (g, GUESTFS_PROC_MOUNT,
-                     (xdrproc_t) xdr_guestfs_mount_args, (char *) &args);
-  if (serial == -1)
-    return -1;
-
-  rv.cb_done = 0;
-  g->reply_cb_internal = mount_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_mount failed, see earlier error messages");
-    return -1;
-  }
-
-  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_MOUNT, serial) == -1)
-    return -1;
-
-  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
-    error (g, "%s", rv.err.error);
-    return -1;
-  }
-
-  return 0;
-}
-
-struct sync_rv {
-  int cb_done;  /* flag to indicate callback was called */
-  struct guestfs_message_header hdr;
-  struct guestfs_message_error err;
-};
-
-static void sync_cb (guestfs_h *g, void *data, XDR *xdr)
-{
-  struct sync_rv *rv = (struct sync_rv *) data;
-
-  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
-    error (g, "guestfs_sync: failed to parse reply header");
+  if (!xdr_guestfs_list_devices_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_list_devices: failed to parse reply");
     return;
   }
-  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
-    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
-      error (g, "guestfs_sync: failed to parse reply error");
-      return;
-    }
-    goto done;
-  }
  done:
   rv->cb_done = 1;
   main_loop.main_loop_quit (g);
 }
 
-int guestfs_sync (guestfs_h *g)
+char **guestfs_list_devices (guestfs_h *g)
 {
-  struct sync_rv rv;
+  struct list_devices_rv rv;
   int serial;
 
   if (g->state != READY) {
-    error (g, "guestfs_sync called from the wrong state, %d != READY",
+    error (g, "guestfs_list_devices called from the wrong state, %d != READY",
       g->state);
-    return -1;
+    return NULL;
   }
 
   memset (&rv, 0, sizeof rv);
 
-  serial = dispatch (g, GUESTFS_PROC_SYNC, NULL, NULL);
+  serial = dispatch (g, GUESTFS_PROC_LIST_DEVICES, NULL, NULL);
   if (serial == -1)
-    return -1;
+    return NULL;
 
   rv.cb_done = 0;
-  g->reply_cb_internal = sync_cb;
+  g->reply_cb_internal = list_devices_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_sync failed, see earlier error messages");
-    return -1;
+    error (g, "guestfs_list_devices failed, see earlier error messages");
+    return NULL;
   }
 
-  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_SYNC, serial) == -1)
-    return -1;
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LIST_DEVICES, serial) == -1)
+    return NULL;
 
   if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
     error (g, "%s", rv.err.error);
-    return -1;
+    return NULL;
   }
 
-  return 0;
+  /* caller will free this, but we need to add a NULL entry */
+  rv.ret.devices.devices_val = safe_realloc (g, rv.ret.devices.devices_val, rv.ret.devices.devices_len + 1);
+  rv.ret.devices.devices_val[rv.ret.devices.devices_len] = NULL;
+  return rv.ret.devices.devices_val;
 }
 
-struct touch_rv {
+struct list_partitions_rv {
   int cb_done;  /* flag to indicate callback was called */
   struct guestfs_message_header hdr;
   struct guestfs_message_error err;
+  struct guestfs_list_partitions_ret ret;
 };
 
-static void touch_cb (guestfs_h *g, void *data, XDR *xdr)
+static void list_partitions_cb (guestfs_h *g, void *data, XDR *xdr)
 {
-  struct touch_rv *rv = (struct touch_rv *) data;
+  struct list_partitions_rv *rv = (struct list_partitions_rv *) data;
 
   if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
-    error (g, "guestfs_touch: failed to parse reply header");
+    error (g, "guestfs_list_partitions: failed to parse reply header");
     return;
   }
   if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
     if (!xdr_guestfs_message_error (xdr, &rv->err)) {
-      error (g, "guestfs_touch: failed to parse reply error");
+      error (g, "guestfs_list_partitions: failed to parse reply error");
       return;
     }
     goto done;
   }
+  if (!xdr_guestfs_list_partitions_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_list_partitions: failed to parse reply");
+    return;
+  }
  done:
   rv->cb_done = 1;
   main_loop.main_loop_quit (g);
 }
 
-int guestfs_touch (guestfs_h *g,
-               const char *path)
+char **guestfs_list_partitions (guestfs_h *g)
 {
-  struct guestfs_touch_args args;
-  struct touch_rv rv;
+  struct list_partitions_rv rv;
   int serial;
 
   if (g->state != READY) {
-    error (g, "guestfs_touch called from the wrong state, %d != READY",
+    error (g, "guestfs_list_partitions called from the wrong state, %d != READY",
       g->state);
-    return -1;
+    return NULL;
   }
 
   memset (&rv, 0, sizeof rv);
 
-  args.path = (char *) path;
-  serial = dispatch (g, GUESTFS_PROC_TOUCH,
-                     (xdrproc_t) xdr_guestfs_touch_args, (char *) &args);
+  serial = dispatch (g, GUESTFS_PROC_LIST_PARTITIONS, NULL, NULL);
   if (serial == -1)
-    return -1;
+    return NULL;
 
   rv.cb_done = 0;
-  g->reply_cb_internal = touch_cb;
+  g->reply_cb_internal = list_partitions_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_touch failed, see earlier error messages");
-    return -1;
+    error (g, "guestfs_list_partitions failed, see earlier error messages");
+    return NULL;
   }
 
-  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_TOUCH, serial) == -1)
-    return -1;
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LIST_PARTITIONS, serial) == -1)
+    return NULL;
 
   if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
     error (g, "%s", rv.err.error);
-    return -1;
+    return NULL;
   }
 
-  return 0;
+  /* caller will free this, but we need to add a NULL entry */
+  rv.ret.partitions.partitions_val = safe_realloc (g, rv.ret.partitions.partitions_val, rv.ret.partitions.partitions_len + 1);
+  rv.ret.partitions.partitions_val[rv.ret.partitions.partitions_len] = NULL;
+  return rv.ret.partitions.partitions_val;
 }
 
index 7f14304..7019d98 100644 (file)
  * 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 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 char **guestfs_list_devices (guestfs_h *handle);
+extern char **guestfs_list_partitions (guestfs_h *handle);
index c51aa5d..80f618b 100644 (file)
@@ -16,6 +16,28 @@ xdr_str (XDR *xdrs, str *objp)
 }
 
 bool_t
+xdr_guestfs_mount_args (XDR *xdrs, guestfs_mount_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->device, ~0))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->mountpoint, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_touch_args (XDR *xdrs, guestfs_touch_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->path, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
 xdr_guestfs_cat_args (XDR *xdrs, guestfs_cat_args *objp)
 {
        register int32_t *buf;
@@ -77,23 +99,23 @@ xdr_guestfs_ls_ret (XDR *xdrs, guestfs_ls_ret *objp)
 }
 
 bool_t
-xdr_guestfs_mount_args (XDR *xdrs, guestfs_mount_args *objp)
+xdr_guestfs_list_devices_ret (XDR *xdrs, guestfs_list_devices_ret *objp)
 {
        register int32_t *buf;
 
-        if (!xdr_string (xdrs, &objp->device, ~0))
-                return FALSE;
-        if (!xdr_string (xdrs, &objp->mountpoint, ~0))
+        if (!xdr_array (xdrs, (char **)&objp->devices.devices_val, (u_int *) &objp->devices.devices_len, ~0,
+               sizeof (str), (xdrproc_t) xdr_str))
                 return FALSE;
        return TRUE;
 }
 
 bool_t
-xdr_guestfs_touch_args (XDR *xdrs, guestfs_touch_args *objp)
+xdr_guestfs_list_partitions_ret (XDR *xdrs, guestfs_list_partitions_ret *objp)
 {
        register int32_t *buf;
 
-        if (!xdr_string (xdrs, &objp->path, ~0))
+        if (!xdr_array (xdrs, (char **)&objp->partitions.partitions_val, (u_int *) &objp->partitions.partitions_len, ~0,
+               sizeof (str), (xdrproc_t) xdr_str))
                 return FALSE;
        return TRUE;
 }
index a83ad00..3e19212 100644 (file)
@@ -16,6 +16,17 @@ extern "C" {
 
 typedef char *str;
 
+struct guestfs_mount_args {
+       char *device;
+       char *mountpoint;
+};
+typedef struct guestfs_mount_args guestfs_mount_args;
+
+struct guestfs_touch_args {
+       char *path;
+};
+typedef struct guestfs_touch_args guestfs_touch_args;
+
 struct guestfs_cat_args {
        char *path;
 };
@@ -49,25 +60,32 @@ struct guestfs_ls_ret {
 };
 typedef struct guestfs_ls_ret guestfs_ls_ret;
 
-struct guestfs_mount_args {
-       char *device;
-       char *mountpoint;
+struct guestfs_list_devices_ret {
+       struct {
+               u_int devices_len;
+               str *devices_val;
+       } devices;
 };
-typedef struct guestfs_mount_args guestfs_mount_args;
+typedef struct guestfs_list_devices_ret guestfs_list_devices_ret;
 
-struct guestfs_touch_args {
-       char *path;
+struct guestfs_list_partitions_ret {
+       struct {
+               u_int partitions_len;
+               str *partitions_val;
+       } partitions;
 };
-typedef struct guestfs_touch_args guestfs_touch_args;
+typedef struct guestfs_list_partitions_ret guestfs_list_partitions_ret;
 
 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_dummy = 3 + 1,
+       GUESTFS_PROC_CAT = 4,
+       GUESTFS_PROC_LL = 5,
+       GUESTFS_PROC_LS = 6,
+       GUESTFS_PROC_LIST_DEVICES = 7,
+       GUESTFS_PROC_LIST_PARTITIONS = 8,
+       GUESTFS_PROC_dummy = 8 + 1,
 };
 typedef enum guestfs_procedure guestfs_procedure;
 #define GUESTFS_MESSAGE_MAX 4194304
@@ -106,14 +124,16 @@ typedef struct guestfs_message_header guestfs_message_header;
 
 #if defined(__STDC__) || defined(__cplusplus)
 extern  bool_t xdr_str (XDR *, str*);
+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_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_list_devices_ret (XDR *, guestfs_list_devices_ret*);
+extern  bool_t xdr_guestfs_list_partitions_ret (XDR *, guestfs_list_partitions_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*);
@@ -122,14 +142,16 @@ 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_mount_args ();
+extern bool_t xdr_guestfs_touch_args ();
 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_list_devices_ret ();
+extern bool_t xdr_guestfs_list_partitions_ret ();
 extern bool_t xdr_guestfs_procedure ();
 extern bool_t xdr_guestfs_message_direction ();
 extern bool_t xdr_guestfs_message_status ();
index df3e936..be0efdb 100644 (file)
 
 typedef string str<>;
 
+/* guestfs_mount */
+
+struct guestfs_mount_args {
+  string device<>;
+  string mountpoint<>;
+};
+
+/* guestfs_sync */
+
+/* guestfs_touch */
+
+struct guestfs_touch_args {
+  string path<>;
+};
+
 /* guestfs_cat */
 
 struct guestfs_cat_args {
@@ -51,28 +66,27 @@ struct guestfs_ls_ret {
   str listing<>;
 };
 
-/* guestfs_mount */
+/* guestfs_list_devices */
 
-struct guestfs_mount_args {
-  string device<>;
-  string mountpoint<>;
+struct guestfs_list_devices_ret {
+  str devices<>;
 };
 
-/* guestfs_sync */
-
-/* guestfs_touch */
+/* guestfs_list_partitions */
 
-struct guestfs_touch_args {
-  string path<>;
+struct guestfs_list_partitions_ret {
+  str partitions<>;
 };
 
 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_CAT = 4,
+  GUESTFS_PROC_LL = 5,
+  GUESTFS_PROC_LS = 6,
+  GUESTFS_PROC_LIST_DEVICES = 7,
+  GUESTFS_PROC_LIST_PARTITIONS = 8,
   GUESTFS_PROC_dummy
 };