pvs/vgs/lvs commands working now.
authorRichard Jones <rjones@redhat.com>
Tue, 7 Apr 2009 05:50:31 +0000 (06:50 +0100)
committerRichard Jones <rjones@redhat.com>
Tue, 7 Apr 2009 08:14:17 +0000 (09:14 +0100)
22 files changed:
Makefile.am
daemon/Makefile.am
daemon/actions.h
daemon/daemon.h
daemon/lvm.c [new file with mode: 0644]
daemon/proto.c
daemon/stubs.c
fish/cmds.c
fish/fish.c
guestfs-actions.pod
guestfs-structs.pod [new file with mode: 0644]
guestfs.pod
src/Makefile.am
src/generator.ml
src/guestfs-actions.c
src/guestfs-actions.h
src/guestfs-structs.h [new file with mode: 0644]
src/guestfs.c
src/guestfs.h
src/guestfs_protocol.c
src/guestfs_protocol.h
src/guestfs_protocol.x

index 8e2b49d..39cf86c 100644 (file)
@@ -21,7 +21,7 @@ SUBDIRS = src daemon fish examples
 
 EXTRA_DIST = \
        make-initramfs.sh update-initramfs.sh \
-       guestfs.pod guestfs-actions.pod \
+       guestfs.pod guestfs-actions.pod guestfs-structs.pod \
        libguestfs.spec \
        HACKING
 
@@ -57,13 +57,16 @@ clean-local:
        rm -rf initramfs
 
 # Manual page.
-# guestfs-actions.pod is autogenerated.  There is no include mechanism
-# for POD, so we have to do it by hand.
+# guestfs-actions.pod and guestfs-structs are autogenerated.  There is
+# no include mechanism for POD, so we have to do it by hand.
 
 man_MANS = guestfs.3
 
-guestfs.3: guestfs.pod guestfs-actions.pod
-       sed -e '/@ACTIONS@/rguestfs-actions.pod' -e 's/@ACTIONS@//' < $< | \
+guestfs.3: guestfs.pod guestfs-actions.pod guestfs-structs.pod
+       sed \
+         -e '/@ACTIONS@/rguestfs-actions.pod' -e 's/@ACTIONS@//' \
+         -e '/@STRUCTS@/rguestfs-structs.pod' -e 's/@STRUCTS@//' \
+         < $< | \
        $(POD2MAN) \
          --section 3 \
          -c "Virtualization Support" \
index 5faa652..4d56034 100644 (file)
@@ -25,6 +25,7 @@ guestfsd_SOURCES = \
        file.c \
        guestfsd.c \
        ls.c \
+       lvm.c \
        mount.c \
        proto.c \
        stubs.c \
index 58b8a24..6527e0c 100644 (file)
@@ -19,6 +19,8 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#include "../src/guestfs_protocol.h"
+
 extern int do_mount (const char *device, const char *mountpoint);
 extern int do_sync ();
 extern int do_touch (const char *path);
@@ -27,3 +29,6 @@ extern char *do_ll (const char *directory);
 extern char **do_ls (const char *directory);
 extern char **do_list_devices ();
 extern char **do_list_partitions ();
+extern guestfs_lvm_int_pv_list *do_pvs ();
+extern guestfs_lvm_int_vg_list *do_vgs ();
+extern guestfs_lvm_int_lv_list *do_lvs ();
index 60e1982..e2b6a2a 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef GUESTFSD_DAEMON_H
 #define GUESTFSD_DAEMON_H
 
+#include "../src/guestfs_protocol.h"
+
 #include <stdarg.h>
 #include <rpc/types.h>
 #include <rpc/xdr.h>
@@ -44,6 +46,9 @@ extern int root_mounted;
 
 /* in stubs.c (auto-generated) */
 extern void dispatch_incoming_message (XDR *);
+extern guestfs_lvm_int_pv_list *parse_command_line_pvs (void);
+extern guestfs_lvm_int_vg_list *parse_command_line_vgs (void);
+extern guestfs_lvm_int_lv_list *parse_command_line_lvs (void);
 
 /* in proto.c */
 extern void main_loop (int sock);
diff --git a/daemon/lvm.c b/daemon/lvm.c
new file mode 100644 (file)
index 0000000..1b888f2
--- /dev/null
@@ -0,0 +1,50 @@
+/* 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 <ctype.h>
+
+#include "daemon.h"
+#include "actions.h"
+
+/* LVM actions.  Keep an eye on liblvm, although at the time
+ * of writing it hasn't progressed very far.
+ */
+
+guestfs_lvm_int_pv_list *
+do_pvs (void)
+{
+  return parse_command_line_pvs ();
+}
+
+guestfs_lvm_int_vg_list *
+do_vgs (void)
+{
+  return parse_command_line_vgs ();
+}
+
+guestfs_lvm_int_lv_list *
+do_lvs (void)
+{
+  return parse_command_line_lvs ();
+}
index 62d8da5..93d33c2 100644 (file)
@@ -35,7 +35,7 @@
 /* XXX We should make this configurable from /proc/cmdline so that the
  * verbose setting of the guestfs_h can be inherited here.
  */
-#define DEBUG 1
+#define DEBUG 0
 
 /* The message currently being processed. */
 int proc_nr;
index faece82..24f6042 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#define _GNU_SOURCE // for strchrnul
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <ctype.h>
 #include <rpc/types.h>
 #include <rpc/xdr.h>
+
 #include "daemon.h"
 #include "../src/guestfs_protocol.h"
 #include "actions.h"
@@ -191,6 +199,51 @@ static void list_partitions_stub (XDR *xdr_in)
   free_strings (r);
 }
 
+static void pvs_stub (XDR *xdr_in)
+{
+  guestfs_lvm_int_pv_list *r;
+
+  r = do_pvs ();
+  if (r == NULL)
+    /* do_pvs has already called reply_with_error, so just return */
+    return;
+
+  struct guestfs_pvs_ret ret;
+  ret.physvols = *r;
+  reply ((xdrproc_t) &xdr_guestfs_pvs_ret, (char *) &ret);
+  xdr_free ((xdrproc_t) xdr_guestfs_pvs_ret, (char *) &ret);
+}
+
+static void vgs_stub (XDR *xdr_in)
+{
+  guestfs_lvm_int_vg_list *r;
+
+  r = do_vgs ();
+  if (r == NULL)
+    /* do_vgs has already called reply_with_error, so just return */
+    return;
+
+  struct guestfs_vgs_ret ret;
+  ret.volgroups = *r;
+  reply ((xdrproc_t) &xdr_guestfs_vgs_ret, (char *) &ret);
+  xdr_free ((xdrproc_t) xdr_guestfs_vgs_ret, (char *) &ret);
+}
+
+static void lvs_stub (XDR *xdr_in)
+{
+  guestfs_lvm_int_lv_list *r;
+
+  r = do_lvs ();
+  if (r == NULL)
+    /* do_lvs has already called reply_with_error, so just return */
+    return;
+
+  struct guestfs_lvs_ret ret;
+  ret.logvols = *r;
+  reply ((xdrproc_t) &xdr_guestfs_lvs_ret, (char *) &ret);
+  xdr_free ((xdrproc_t) xdr_guestfs_lvs_ret, (char *) &ret);
+}
+
 void dispatch_incoming_message (XDR *xdr_in)
 {
   switch (proc_nr) {
@@ -218,7 +271,943 @@ void dispatch_incoming_message (XDR *xdr_in)
     case GUESTFS_PROC_LIST_PARTITIONS:
       list_partitions_stub (xdr_in);
       break;
+    case GUESTFS_PROC_PVS:
+      pvs_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_VGS:
+      vgs_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_LVS:
+      lvs_stub (xdr_in);
+      break;
     default:
       reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr);
   }
 }
+
+static const char *lvm_pv_cols = "pv_name,pv_uuid,pv_fmt,pv_size,dev_size,pv_free,pv_used,pv_attr,pv_pe_count,pv_pe_alloc_count,pv_tags,pe_start,pv_mda_count,pv_mda_free";
+
+static int lvm_tokenize_pv (char *str, struct guestfs_lvm_int_pv *r)
+{
+  char *tok, *p, *next;
+  int i, j;
+
+  if (!str) {
+    fprintf (stderr, "%s: failed: passed a NULL string\n", __func__);
+    return -1;
+  }
+  if (!*str || isspace (*str)) {
+    fprintf (stderr, "%s: failed: passed a empty string or one beginning with whitespace\n", __func__);
+    return -1;
+  }
+  tok = str;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_name");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->pv_name = strdup (tok);
+  if (r->pv_name == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_uuid");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  for (i = j = 0; i < 32; ++j) {
+    if (tok[j] == '\0') {
+      fprintf (stderr, "%s: failed to parse UUID from '%s'\n", __func__, tok);
+      return -1;
+    } else if (tok[j] != '-')
+      r->pv_uuid[i++] = tok[j];
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_fmt");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->pv_fmt = strdup (tok);
+  if (r->pv_fmt == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_size");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->pv_size) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "pv_size");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "dev_size");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->dev_size) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "dev_size");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_free");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->pv_free) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "pv_free");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_used");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->pv_used) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "pv_used");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_attr");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->pv_attr = strdup (tok);
+  if (r->pv_attr == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_pe_count");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->pv_pe_count) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "pv_pe_count");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_pe_alloc_count");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->pv_pe_alloc_count) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "pv_pe_alloc_count");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_tags");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->pv_tags = strdup (tok);
+  if (r->pv_tags == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pe_start");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->pe_start) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "pe_start");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_mda_count");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->pv_mda_count) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "pv_mda_count");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_mda_free");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->pv_mda_free) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "pv_mda_free");
+    return -1;
+  }
+  tok = next;
+  if (tok != NULL) {
+    fprintf (stderr, "%s: failed: extra tokens at end of string\n", __func__);
+    return -1;
+  }
+  return 0;
+}
+
+guestfs_lvm_int_pv_list *
+parse_command_line_pvs (void)
+{
+  char *out, *err;
+  char *p, *pend;
+  int r, i;
+  guestfs_lvm_int_pv_list *ret;
+  void *newp;
+
+  ret = malloc (sizeof *ret);
+  if (!ret) {
+    reply_with_perror ("malloc");
+    return NULL;
+  }
+
+  ret->guestfs_lvm_int_pv_list_len = 0;
+  ret->guestfs_lvm_int_pv_list_val = NULL;
+
+  r = command (&out, &err,
+              "/sbin/lvm", "pvs",
+              "-o", lvm_pv_cols, "--unbuffered", "--noheadings",
+              "--nosuffix", "--separator", ",", "--units", "b", NULL);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (out);
+    free (err);
+    return NULL;
+  }
+
+  free (err);
+
+  /* Tokenize each line of the output. */
+  p = out;
+  i = 0;
+  while (p) {
+    pend = strchr (p, '\n');   /* Get the next line of output. */
+    if (pend) {
+      *pend = '\0';
+      pend++;
+    }
+
+    while (*p && isspace (*p)) /* Skip any leading whitespace. */
+      p++;
+
+    if (!*p) {                 /* Empty line?  Skip it. */
+      p = pend;
+      continue;
+    }
+
+    /* Allocate some space to store this next entry. */
+    newp = realloc (ret->guestfs_lvm_int_pv_list_val,
+                   sizeof (guestfs_lvm_int_pv) * (i+1));
+    if (newp == NULL) {
+      reply_with_perror ("realloc");
+      free (ret->guestfs_lvm_int_pv_list_val);
+      free (ret);
+      free (out);
+      return NULL;
+    }
+    ret->guestfs_lvm_int_pv_list_val = newp;
+
+    /* Tokenize the next entry. */
+    r = lvm_tokenize_pv (p, &ret->guestfs_lvm_int_pv_list_val[i]);
+    if (r == -1) {
+      reply_with_error ("failed to parse output of 'pvs' command");
+      free (ret->guestfs_lvm_int_pv_list_val);
+      free (ret);
+      free (out);
+      return NULL;
+    }
+
+    ++i;
+    p = pend;
+  }
+
+  ret->guestfs_lvm_int_pv_list_len = i;
+
+  free (out);
+  return ret;
+}
+static const char *lvm_vg_cols = "vg_name,vg_uuid,vg_fmt,vg_attr,vg_size,vg_free,vg_sysid,vg_extent_size,vg_extent_count,vg_free_count,max_lv,max_pv,pv_count,lv_count,snap_count,vg_seqno,vg_tags,vg_mda_count,vg_mda_free";
+
+static int lvm_tokenize_vg (char *str, struct guestfs_lvm_int_vg *r)
+{
+  char *tok, *p, *next;
+  int i, j;
+
+  if (!str) {
+    fprintf (stderr, "%s: failed: passed a NULL string\n", __func__);
+    return -1;
+  }
+  if (!*str || isspace (*str)) {
+    fprintf (stderr, "%s: failed: passed a empty string or one beginning with whitespace\n", __func__);
+    return -1;
+  }
+  tok = str;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_name");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->vg_name = strdup (tok);
+  if (r->vg_name == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_uuid");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  for (i = j = 0; i < 32; ++j) {
+    if (tok[j] == '\0') {
+      fprintf (stderr, "%s: failed to parse UUID from '%s'\n", __func__, tok);
+      return -1;
+    } else if (tok[j] != '-')
+      r->vg_uuid[i++] = tok[j];
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_fmt");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->vg_fmt = strdup (tok);
+  if (r->vg_fmt == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_attr");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->vg_attr = strdup (tok);
+  if (r->vg_attr == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_size");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->vg_size) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "vg_size");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_free");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->vg_free) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "vg_free");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_sysid");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->vg_sysid = strdup (tok);
+  if (r->vg_sysid == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_extent_size");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->vg_extent_size) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "vg_extent_size");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_extent_count");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->vg_extent_count) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "vg_extent_count");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_free_count");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->vg_free_count) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "vg_free_count");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "max_lv");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->max_lv) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "max_lv");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "max_pv");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->max_pv) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "max_pv");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "pv_count");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->pv_count) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "pv_count");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "lv_count");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->lv_count) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "lv_count");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "snap_count");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->snap_count) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "snap_count");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_seqno");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->vg_seqno) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "vg_seqno");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_tags");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->vg_tags = strdup (tok);
+  if (r->vg_tags == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_mda_count");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->vg_mda_count) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "vg_mda_count");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "vg_mda_free");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->vg_mda_free) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "vg_mda_free");
+    return -1;
+  }
+  tok = next;
+  if (tok != NULL) {
+    fprintf (stderr, "%s: failed: extra tokens at end of string\n", __func__);
+    return -1;
+  }
+  return 0;
+}
+
+guestfs_lvm_int_vg_list *
+parse_command_line_vgs (void)
+{
+  char *out, *err;
+  char *p, *pend;
+  int r, i;
+  guestfs_lvm_int_vg_list *ret;
+  void *newp;
+
+  ret = malloc (sizeof *ret);
+  if (!ret) {
+    reply_with_perror ("malloc");
+    return NULL;
+  }
+
+  ret->guestfs_lvm_int_vg_list_len = 0;
+  ret->guestfs_lvm_int_vg_list_val = NULL;
+
+  r = command (&out, &err,
+              "/sbin/lvm", "vgs",
+              "-o", lvm_vg_cols, "--unbuffered", "--noheadings",
+              "--nosuffix", "--separator", ",", "--units", "b", NULL);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (out);
+    free (err);
+    return NULL;
+  }
+
+  free (err);
+
+  /* Tokenize each line of the output. */
+  p = out;
+  i = 0;
+  while (p) {
+    pend = strchr (p, '\n');   /* Get the next line of output. */
+    if (pend) {
+      *pend = '\0';
+      pend++;
+    }
+
+    while (*p && isspace (*p)) /* Skip any leading whitespace. */
+      p++;
+
+    if (!*p) {                 /* Empty line?  Skip it. */
+      p = pend;
+      continue;
+    }
+
+    /* Allocate some space to store this next entry. */
+    newp = realloc (ret->guestfs_lvm_int_vg_list_val,
+                   sizeof (guestfs_lvm_int_vg) * (i+1));
+    if (newp == NULL) {
+      reply_with_perror ("realloc");
+      free (ret->guestfs_lvm_int_vg_list_val);
+      free (ret);
+      free (out);
+      return NULL;
+    }
+    ret->guestfs_lvm_int_vg_list_val = newp;
+
+    /* Tokenize the next entry. */
+    r = lvm_tokenize_vg (p, &ret->guestfs_lvm_int_vg_list_val[i]);
+    if (r == -1) {
+      reply_with_error ("failed to parse output of 'vgs' command");
+      free (ret->guestfs_lvm_int_vg_list_val);
+      free (ret);
+      free (out);
+      return NULL;
+    }
+
+    ++i;
+    p = pend;
+  }
+
+  ret->guestfs_lvm_int_vg_list_len = i;
+
+  free (out);
+  return ret;
+}
+static const char *lvm_lv_cols = "lv_name,lv_uuid,lv_attr,lv_major,lv_minor,lv_kernel_major,lv_kernel_minor,lv_size,seg_count,origin,snap_percent,copy_percent,move_pv,lv_tags,mirror_log,modules";
+
+static int lvm_tokenize_lv (char *str, struct guestfs_lvm_int_lv *r)
+{
+  char *tok, *p, *next;
+  int i, j;
+
+  if (!str) {
+    fprintf (stderr, "%s: failed: passed a NULL string\n", __func__);
+    return -1;
+  }
+  if (!*str || isspace (*str)) {
+    fprintf (stderr, "%s: failed: passed a empty string or one beginning with whitespace\n", __func__);
+    return -1;
+  }
+  tok = str;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "lv_name");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->lv_name = strdup (tok);
+  if (r->lv_name == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "lv_uuid");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  for (i = j = 0; i < 32; ++j) {
+    if (tok[j] == '\0') {
+      fprintf (stderr, "%s: failed to parse UUID from '%s'\n", __func__, tok);
+      return -1;
+    } else if (tok[j] != '-')
+      r->lv_uuid[i++] = tok[j];
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "lv_attr");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->lv_attr = strdup (tok);
+  if (r->lv_attr == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "lv_major");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->lv_major) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "lv_major");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "lv_minor");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->lv_minor) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "lv_minor");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "lv_kernel_major");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->lv_kernel_major) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "lv_kernel_major");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "lv_kernel_minor");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->lv_kernel_minor) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "lv_kernel_minor");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "lv_size");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNu64, &r->lv_size) != 1) {
+    fprintf (stderr, "%s: failed to parse size '%s' from token %s\n", __func__, tok, "lv_size");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "seg_count");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (sscanf (tok, "%"SCNi64, &r->seg_count) != 1) {
+    fprintf (stderr, "%s: failed to parse int '%s' from token %s\n", __func__, tok, "seg_count");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "origin");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->origin = strdup (tok);
+  if (r->origin == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "snap_percent");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (tok[0] == '\0')
+    r->snap_percent = -1;
+  else if (sscanf (tok, "%f", &r->snap_percent) != 1) {
+    fprintf (stderr, "%s: failed to parse float '%s' from token %s\n", __func__, tok, "snap_percent");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "copy_percent");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  if (tok[0] == '\0')
+    r->copy_percent = -1;
+  else if (sscanf (tok, "%f", &r->copy_percent) != 1) {
+    fprintf (stderr, "%s: failed to parse float '%s' from token %s\n", __func__, tok, "copy_percent");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "move_pv");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->move_pv = strdup (tok);
+  if (r->move_pv == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "lv_tags");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->lv_tags = strdup (tok);
+  if (r->lv_tags == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "mirror_log");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->mirror_log = strdup (tok);
+  if (r->mirror_log == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (!tok) {
+    fprintf (stderr, "%s: failed: string finished early, around token %s\n", __func__, "modules");
+    return -1;
+  }
+  p = strchrnul (tok, ',');
+  if (*p) next = p+1; else next = NULL;
+  *p = '\0';
+  r->modules = strdup (tok);
+  if (r->modules == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+  tok = next;
+  if (tok != NULL) {
+    fprintf (stderr, "%s: failed: extra tokens at end of string\n", __func__);
+    return -1;
+  }
+  return 0;
+}
+
+guestfs_lvm_int_lv_list *
+parse_command_line_lvs (void)
+{
+  char *out, *err;
+  char *p, *pend;
+  int r, i;
+  guestfs_lvm_int_lv_list *ret;
+  void *newp;
+
+  ret = malloc (sizeof *ret);
+  if (!ret) {
+    reply_with_perror ("malloc");
+    return NULL;
+  }
+
+  ret->guestfs_lvm_int_lv_list_len = 0;
+  ret->guestfs_lvm_int_lv_list_val = NULL;
+
+  r = command (&out, &err,
+              "/sbin/lvm", "lvs",
+              "-o", lvm_lv_cols, "--unbuffered", "--noheadings",
+              "--nosuffix", "--separator", ",", "--units", "b", NULL);
+  if (r == -1) {
+    reply_with_error ("%s", err);
+    free (out);
+    free (err);
+    return NULL;
+  }
+
+  free (err);
+
+  /* Tokenize each line of the output. */
+  p = out;
+  i = 0;
+  while (p) {
+    pend = strchr (p, '\n');   /* Get the next line of output. */
+    if (pend) {
+      *pend = '\0';
+      pend++;
+    }
+
+    while (*p && isspace (*p)) /* Skip any leading whitespace. */
+      p++;
+
+    if (!*p) {                 /* Empty line?  Skip it. */
+      p = pend;
+      continue;
+    }
+
+    /* Allocate some space to store this next entry. */
+    newp = realloc (ret->guestfs_lvm_int_lv_list_val,
+                   sizeof (guestfs_lvm_int_lv) * (i+1));
+    if (newp == NULL) {
+      reply_with_perror ("realloc");
+      free (ret->guestfs_lvm_int_lv_list_val);
+      free (ret);
+      free (out);
+      return NULL;
+    }
+    ret->guestfs_lvm_int_lv_list_val = newp;
+
+    /* Tokenize the next entry. */
+    r = lvm_tokenize_lv (p, &ret->guestfs_lvm_int_lv_list_val[i]);
+    if (r == -1) {
+      reply_with_error ("failed to parse output of 'lvs' command");
+      free (ret->guestfs_lvm_int_lv_list_val);
+      free (ret);
+      free (out);
+      return NULL;
+    }
+
+    ++i;
+    p = pend;
+  }
+
+  ret->guestfs_lvm_int_lv_list_len = i;
+
+  free (out);
+  return ret;
+}
index 28dd956..6d8e454 100644 (file)
@@ -22,7 +22,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <inttypes.h>
 
+#include <guestfs.h>
 #include "fish.h"
 
 void list_commands (void)
@@ -34,9 +36,12 @@ void list_commands (void)
   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", "lvs", "list the LVM logical volumes (LVs)");
   printf ("%-20s %s\n", "mount", "mount a guest disk at a position in the filesystem");
+  printf ("%-20s %s\n", "pvs", "list the LVM physical volumes (PVs)");
   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", "vgs", "list the LVM volume groups (VGs)");
   printf ("    Use -h <cmd> / help <cmd> to show detailed help for a command.\n");
 }
 
@@ -66,9 +71,120 @@ void display_command (const char *cmd)
   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
+  if (strcasecmp (cmd, "pvs") == 0)
+    pod2text ("pvs - list the LVM physical volumes (PVs)", " pvs\n\nList all the physical volumes detected.  This is the equivalent\nof the L<pvs(8)> command.");
+  else
+  if (strcasecmp (cmd, "vgs") == 0)
+    pod2text ("vgs - list the LVM volume groups (VGs)", " vgs\n\nList all the volumes groups detected.  This is the equivalent\nof the L<vgs(8)> command.");
+  else
+  if (strcasecmp (cmd, "lvs") == 0)
+    pod2text ("lvs - list the LVM logical volumes (LVs)", " lvs\n\nList all the logical volumes detected.  This is the equivalent\nof the L<lvs(8)> command.");
+  else
     display_builtin_command (cmd);
 }
 
+static void print_pv (struct guestfs_lvm_pv *pv)
+{
+  int i;
+
+  printf ("pv_name: %s\n", pv->pv_name);
+  printf ("pv_uuid: ");
+  for (i = 0; i < 32; ++i)
+    printf ("%c", pv->pv_uuid[i]);
+  printf ("\n");
+  printf ("pv_fmt: %s\n", pv->pv_fmt);
+  printf ("pv_size: %" PRIu64 "\n", pv->pv_size);
+  printf ("dev_size: %" PRIu64 "\n", pv->dev_size);
+  printf ("pv_free: %" PRIu64 "\n", pv->pv_free);
+  printf ("pv_used: %" PRIu64 "\n", pv->pv_used);
+  printf ("pv_attr: %s\n", pv->pv_attr);
+  printf ("pv_pe_count: %" PRIi64 "\n", pv->pv_pe_count);
+  printf ("pv_pe_alloc_count: %" PRIi64 "\n", pv->pv_pe_alloc_count);
+  printf ("pv_tags: %s\n", pv->pv_tags);
+  printf ("pe_start: %" PRIu64 "\n", pv->pe_start);
+  printf ("pv_mda_count: %" PRIi64 "\n", pv->pv_mda_count);
+  printf ("pv_mda_free: %" PRIu64 "\n", pv->pv_mda_free);
+}
+
+static void print_pv_list (struct guestfs_lvm_pv_list *pvs)
+{
+  int i;
+
+  for (i = 0; i < pvs->len; ++i)
+    print_pv (&pvs->val[i]);
+}
+
+static void print_vg (struct guestfs_lvm_vg *vg)
+{
+  int i;
+
+  printf ("vg_name: %s\n", vg->vg_name);
+  printf ("vg_uuid: ");
+  for (i = 0; i < 32; ++i)
+    printf ("%c", vg->vg_uuid[i]);
+  printf ("\n");
+  printf ("vg_fmt: %s\n", vg->vg_fmt);
+  printf ("vg_attr: %s\n", vg->vg_attr);
+  printf ("vg_size: %" PRIu64 "\n", vg->vg_size);
+  printf ("vg_free: %" PRIu64 "\n", vg->vg_free);
+  printf ("vg_sysid: %s\n", vg->vg_sysid);
+  printf ("vg_extent_size: %" PRIu64 "\n", vg->vg_extent_size);
+  printf ("vg_extent_count: %" PRIi64 "\n", vg->vg_extent_count);
+  printf ("vg_free_count: %" PRIi64 "\n", vg->vg_free_count);
+  printf ("max_lv: %" PRIi64 "\n", vg->max_lv);
+  printf ("max_pv: %" PRIi64 "\n", vg->max_pv);
+  printf ("pv_count: %" PRIi64 "\n", vg->pv_count);
+  printf ("lv_count: %" PRIi64 "\n", vg->lv_count);
+  printf ("snap_count: %" PRIi64 "\n", vg->snap_count);
+  printf ("vg_seqno: %" PRIi64 "\n", vg->vg_seqno);
+  printf ("vg_tags: %s\n", vg->vg_tags);
+  printf ("vg_mda_count: %" PRIi64 "\n", vg->vg_mda_count);
+  printf ("vg_mda_free: %" PRIu64 "\n", vg->vg_mda_free);
+}
+
+static void print_vg_list (struct guestfs_lvm_vg_list *vgs)
+{
+  int i;
+
+  for (i = 0; i < vgs->len; ++i)
+    print_vg (&vgs->val[i]);
+}
+
+static void print_lv (struct guestfs_lvm_lv *lv)
+{
+  int i;
+
+  printf ("lv_name: %s\n", lv->lv_name);
+  printf ("lv_uuid: ");
+  for (i = 0; i < 32; ++i)
+    printf ("%c", lv->lv_uuid[i]);
+  printf ("\n");
+  printf ("lv_attr: %s\n", lv->lv_attr);
+  printf ("lv_major: %" PRIi64 "\n", lv->lv_major);
+  printf ("lv_minor: %" PRIi64 "\n", lv->lv_minor);
+  printf ("lv_kernel_major: %" PRIi64 "\n", lv->lv_kernel_major);
+  printf ("lv_kernel_minor: %" PRIi64 "\n", lv->lv_kernel_minor);
+  printf ("lv_size: %" PRIu64 "\n", lv->lv_size);
+  printf ("seg_count: %" PRIi64 "\n", lv->seg_count);
+  printf ("origin: %s\n", lv->origin);
+  if (lv->snap_percent >= 0) printf ("snap_percent: %g %%\n", lv->snap_percent);
+  else printf ("snap_percent: \n");
+  if (lv->copy_percent >= 0) printf ("copy_percent: %g %%\n", lv->copy_percent);
+  else printf ("copy_percent: \n");
+  printf ("move_pv: %s\n", lv->move_pv);
+  printf ("lv_tags: %s\n", lv->lv_tags);
+  printf ("mirror_log: %s\n", lv->mirror_log);
+  printf ("modules: %s\n", lv->modules);
+}
+
+static void print_lv_list (struct guestfs_lvm_lv_list *lvs)
+{
+  int i;
+
+  for (i = 0; i < lvs->len; ++i)
+    print_lv (&lvs->val[i]);
+}
+
 static int run_mount (const char *cmd, int argc, char *argv[])
 {
   int r;
@@ -192,6 +308,51 @@ static int run_list_partitions (const char *cmd, int argc, char *argv[])
   return 0;
 }
 
+static int run_pvs (const char *cmd, int argc, char *argv[])
+{
+  struct guestfs_lvm_pv_list *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_pvs (g);
+  if (r == NULL) return -1;
+  print_pv_list (r);
+  guestfs_free_lvm_pv_list (r);
+  return 0;
+}
+
+static int run_vgs (const char *cmd, int argc, char *argv[])
+{
+  struct guestfs_lvm_vg_list *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_vgs (g);
+  if (r == NULL) return -1;
+  print_vg_list (r);
+  guestfs_free_lvm_vg_list (r);
+  return 0;
+}
+
+static int run_lvs (const char *cmd, int argc, char *argv[])
+{
+  struct guestfs_lvm_lv_list *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_lvs (g);
+  if (r == NULL) return -1;
+  print_lv_list (r);
+  guestfs_free_lvm_lv_list (r);
+  return 0;
+}
+
 int run_action (const char *cmd, int argc, char *argv[])
 {
   if (strcasecmp (cmd, "mount") == 0)
@@ -218,6 +379,15 @@ int run_action (const char *cmd, int argc, char *argv[])
   if (strcasecmp (cmd, "list_partitions") == 0 || strcasecmp (cmd, "list-partitions") == 0)
     return run_list_partitions (cmd, argc, argv);
   else
+  if (strcasecmp (cmd, "pvs") == 0)
+    return run_pvs (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "vgs") == 0)
+    return run_vgs (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "lvs") == 0)
+    return run_lvs (cmd, argc, argv);
+  else
     {
       fprintf (stderr, "%s: unknown command\n", cmd);
       return -1;
index 374416e..e0d0470 100644 (file)
@@ -165,7 +165,7 @@ main (int argc, char *argv[])
        mp->mountpoint = "/";
       mp->device = optarg;
       mp->next = mps;
-      mps = mp->next;
+      mps = mp;
       break;
 
     case 'n':
index f89ae26..a2a19ba 100644 (file)
@@ -75,6 +75,16 @@ 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_lvs
+
+ struct guestfs_lvm_lv_list *guestfs_lvs (guestfs_h *handle);
+
+List all the logical volumes detected.  This is the equivalent
+of the L<lvs(8)> command.
+
+This function returns a C<struct guestfs_lvm_lv_list>.
+I<The caller must call C<guestfs_free_lvm_lv_list> after use.>.
+
 =head2 guestfs_mount
 
  int guestfs_mount (guestfs_h *handle,
@@ -100,6 +110,16 @@ call, in order to improve reliability.
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_pvs
+
+ struct guestfs_lvm_pv_list *guestfs_pvs (guestfs_h *handle);
+
+List all the physical volumes detected.  This is the equivalent
+of the L<pvs(8)> command.
+
+This function returns a C<struct guestfs_lvm_pv_list>.
+I<The caller must call C<guestfs_free_lvm_pv_list> after use.>.
+
 =head2 guestfs_sync
 
  int guestfs_sync (guestfs_h *handle);
@@ -123,3 +143,13 @@ to create a new zero-length file.
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_vgs
+
+ struct guestfs_lvm_vg_list *guestfs_vgs (guestfs_h *handle);
+
+List all the volumes groups detected.  This is the equivalent
+of the L<vgs(8)> command.
+
+This function returns a C<struct guestfs_lvm_vg_list>.
+I<The caller must call C<guestfs_free_lvm_vg_list> after use.>.
+
diff --git a/guestfs-structs.pod b/guestfs-structs.pod
new file mode 100644 (file)
index 0000000..31b9ff2
--- /dev/null
@@ -0,0 +1,87 @@
+=head2 guestfs_lvm_pv
+
+ struct guestfs_lvm_pv {
+  char *pv_name;
+  /* The next field is NOT nul-terminated, be careful when printing it: */
+  char pv_uuid[32];
+  char *pv_fmt;
+  uint64_t pv_size;
+  uint64_t dev_size;
+  uint64_t pv_free;
+  uint64_t pv_used;
+  char *pv_attr;
+  int64_t pv_pe_count;
+  int64_t pv_pe_alloc_count;
+  char *pv_tags;
+  uint64_t pe_start;
+  int64_t pv_mda_count;
+  uint64_t pv_mda_free;
+ struct guestfs_lvm_pv_list {
+   uint32_t len; /* Number of elements in list. */
+   struct guestfs_lvm_pv *val; /* Elements. */
+ };
+ void guestfs_free_lvm_pv_list (struct guestfs_free_lvm_pv_list *);
+
+=head2 guestfs_lvm_vg
+
+ struct guestfs_lvm_vg {
+  char *vg_name;
+  /* The next field is NOT nul-terminated, be careful when printing it: */
+  char vg_uuid[32];
+  char *vg_fmt;
+  char *vg_attr;
+  uint64_t vg_size;
+  uint64_t vg_free;
+  char *vg_sysid;
+  uint64_t vg_extent_size;
+  int64_t vg_extent_count;
+  int64_t vg_free_count;
+  int64_t max_lv;
+  int64_t max_pv;
+  int64_t pv_count;
+  int64_t lv_count;
+  int64_t snap_count;
+  int64_t vg_seqno;
+  char *vg_tags;
+  int64_t vg_mda_count;
+  uint64_t vg_mda_free;
+ struct guestfs_lvm_vg_list {
+   uint32_t len; /* Number of elements in list. */
+   struct guestfs_lvm_vg *val; /* Elements. */
+ };
+ void guestfs_free_lvm_vg_list (struct guestfs_free_lvm_vg_list *);
+
+=head2 guestfs_lvm_lv
+
+ struct guestfs_lvm_lv {
+  char *lv_name;
+  /* The next field is NOT nul-terminated, be careful when printing it: */
+  char lv_uuid[32];
+  char *lv_attr;
+  int64_t lv_major;
+  int64_t lv_minor;
+  int64_t lv_kernel_major;
+  int64_t lv_kernel_minor;
+  uint64_t lv_size;
+  int64_t seg_count;
+  char *origin;
+  /* The next field is [0..100] or -1 meaning 'not present': */
+  float snap_percent;
+  /* The next field is [0..100] or -1 meaning 'not present': */
+  float copy_percent;
+  char *move_pv;
+  char *lv_tags;
+  char *mirror_log;
+  char *modules;
+ struct guestfs_lvm_lv_list {
+   uint32_t len; /* Number of elements in list. */
+   struct guestfs_lvm_lv *val; /* Elements. */
+ };
+ void guestfs_free_lvm_lv_list (struct guestfs_free_lvm_lv_list *);
+
index 3c28b04..7396644 100644 (file)
@@ -312,6 +312,10 @@ This returns the verbose messages flag.
 
 @ACTIONS@
 
+=head1 STRUCTURES
+
+@STRUCTS@
+
 =head1 STATE MACHINE AND LOW-LEVEL EVENT API
 
 Internally, libguestfs is implemented by running a virtual machine
index 566fa41..7a4e360 100644 (file)
@@ -23,7 +23,7 @@ EXTRA_DIST += guestfs_protocol.x \
        guestfs_protocol.c \
        guestfs_protocol.h
 
-include_HEADERS = guestfs.h guestfs-actions.h
+include_HEADERS = guestfs.h guestfs-actions.h guestfs-structs.h
 
 lib_LTLIBRARIES = libguestfs.la
 
index 12c51fc..8d1dc04 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/ocamlrun ocaml
+#!/usr/bin/env ocaml
 (* libguestfs
  * Copyright (C) 2009 Red Hat Inc.
  *
@@ -37,6 +37,10 @@ and ret =
      *)
   | RString of string
   | RStringList of string
+    (* LVM PVs, VGs and LVs. *)
+  | RPVList of string
+  | RVGList of string
+  | RLVList of string
 and args =
     (* 0 arguments, 1 argument, etc. The guestfs_h param is implicit. *)
   | P0
@@ -130,6 +134,87 @@ 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>.");
+
+  ("pvs", (RPVList "physvols", P0), 9, [],
+   "list the LVM physical volumes (PVs)",
+   "\
+List all the physical volumes detected.  This is the equivalent
+of the L<pvs(8)> command.");
+
+  ("vgs", (RVGList "volgroups", P0), 10, [],
+   "list the LVM volume groups (VGs)",
+   "\
+List all the volumes groups detected.  This is the equivalent
+of the L<vgs(8)> command.");
+
+  ("lvs", (RLVList "logvols", P0), 11, [],
+   "list the LVM logical volumes (LVs)",
+   "\
+List all the logical volumes detected.  This is the equivalent
+of the L<lvs(8)> command.");
+]
+
+(* Column names and types from LVM PVs/VGs/LVs. *)
+let pv_cols = [
+  "pv_name", `String;
+  "pv_uuid", `UUID;
+  "pv_fmt", `String;
+  "pv_size", `Bytes;
+  "dev_size", `Bytes;
+  "pv_free", `Bytes;
+  "pv_used", `Bytes;
+  "pv_attr", `String (* XXX *);
+  "pv_pe_count", `Int;
+  "pv_pe_alloc_count", `Int;
+  "pv_tags", `String;
+  "pe_start", `Bytes;
+  "pv_mda_count", `Int;
+  "pv_mda_free", `Bytes;
+(* Not in Fedora 10:
+  "pv_mda_size", `Bytes;
+*)
+]
+let vg_cols = [
+  "vg_name", `String;
+  "vg_uuid", `UUID;
+  "vg_fmt", `String;
+  "vg_attr", `String (* XXX *);
+  "vg_size", `Bytes;
+  "vg_free", `Bytes;
+  "vg_sysid", `String;
+  "vg_extent_size", `Bytes;
+  "vg_extent_count", `Int;
+  "vg_free_count", `Int;
+  "max_lv", `Int;
+  "max_pv", `Int;
+  "pv_count", `Int;
+  "lv_count", `Int;
+  "snap_count", `Int;
+  "vg_seqno", `Int;
+  "vg_tags", `String;
+  "vg_mda_count", `Int;
+  "vg_mda_free", `Bytes;
+(* Not in Fedora 10:
+  "vg_mda_size", `Bytes;
+*)
+]
+let lv_cols = [
+  "lv_name", `String;
+  "lv_uuid", `UUID;
+  "lv_attr", `String (* XXX *);
+  "lv_major", `Int;
+  "lv_minor", `Int;
+  "lv_kernel_major", `Int;
+  "lv_kernel_minor", `Int;
+  "lv_size", `Bytes;
+  "seg_count", `Int;
+  "origin", `String;
+  "snap_percent", `OptPercent;
+  "copy_percent", `OptPercent;
+  "move_pv", `String;
+  "lv_tags", `String;
+  "mirror_log", `String;
+  "modules", `String;
 ]
 
 (* In some places we want the functions to be displayed sorted
@@ -250,7 +335,7 @@ let rec generate_header comment license =
   pr "\n"
 
 (* Generate the pod documentation for the C API. *)
-and generate_pod () =
+and generate_actions_pod () =
   List.iter (
     fun (shortname, style, _, flags, _, longdesc) ->
       let name = "guestfs_" ^ shortname in
@@ -269,6 +354,15 @@ I<The caller must free the returned string after use>.\n\n"
           pr "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>.\n\n"
+       | RPVList _ ->
+          pr "This function returns a C<struct guestfs_lvm_pv_list>.
+I<The caller must call C<guestfs_free_lvm_pv_list> after use.>.\n\n"
+       | RVGList _ ->
+          pr "This function returns a C<struct guestfs_lvm_vg_list>.
+I<The caller must call C<guestfs_free_lvm_vg_list> after use.>.\n\n"
+       | RLVList _ ->
+          pr "This function returns a C<struct guestfs_lvm_lv_list>.
+I<The caller must call C<guestfs_free_lvm_lv_list> after use.>.\n\n"
       );
       if List.mem ProtocolLimitWarning flags then
        pr "Because of the message protocol, there is a transfer limit 
@@ -276,7 +370,43 @@ of somewhere between 2MB and 4MB.  To transfer large files you should use
 FTP.\n\n";
   ) sorted_functions
 
-(* Generate the protocol (XDR) file. *)
+and generate_structs_pod () =
+  (* LVM structs documentation. *)
+  List.iter (
+    fun (typ, cols) ->
+      pr "=head2 guestfs_lvm_%s\n" typ;
+      pr "\n";
+      pr " struct guestfs_lvm_%s {\n" typ;
+      List.iter (
+       function
+       | name, `String -> pr "  char *%s;\n" name
+       | name, `UUID ->
+           pr "  /* The next field is NOT nul-terminated, be careful when printing it: */\n";
+           pr "  char %s[32];\n" name
+       | name, `Bytes -> pr "  uint64_t %s;\n" name
+       | name, `Int -> pr "  int64_t %s;\n" name
+       | name, `OptPercent ->
+           pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
+           pr "  float %s;\n" name
+      ) cols;
+      pr " \n";
+      pr " struct guestfs_lvm_%s_list {\n" typ;
+      pr "   uint32_t len; /* Number of elements in list. */\n";
+      pr "   struct guestfs_lvm_%s *val; /* Elements. */\n" typ;
+      pr " };\n";
+      pr " \n";
+      pr " void guestfs_free_lvm_%s_list (struct guestfs_free_lvm_%s_list *);\n"
+       typ typ;
+      pr "\n"
+  ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
+
+(* Generate the protocol (XDR) file, 'guestfs_protocol.x' and
+ * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'.  We
+ * have to use an underscore instead of a dash because otherwise
+ * rpcgen generates incorrect code.
+ *
+ * This header is NOT exported to clients, but see also generate_structs_h.
+ *)
 and generate_xdr () =
   generate_header CStyle LGPLv2;
 
@@ -284,6 +414,24 @@ and generate_xdr () =
   pr "typedef string str<>;\n";
   pr "\n";
 
+  (* LVM internal structures. *)
+  List.iter (
+    function
+    | typ, cols ->
+       pr "struct guestfs_lvm_int_%s {\n" typ;
+       List.iter (function
+                  | name, `String -> pr "  string %s<>;\n" name
+                  | name, `UUID -> pr "  opaque %s[32];\n" name
+                  | name, `Bytes -> pr "  hyper %s;\n" name
+                  | name, `Int -> pr "  hyper %s;\n" name
+                  | name, `OptPercent -> pr "  float %s;\n" name
+                 ) cols;
+       pr "};\n";
+       pr "\n";
+       pr "typedef struct guestfs_lvm_int_%s guestfs_lvm_int_%s_list<>;\n" typ typ;
+       pr "\n";
+  ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
+
   List.iter (
     fun (shortname, style, _, _, _, _) ->
       let name = "guestfs_" ^ shortname in
@@ -308,6 +456,18 @@ and generate_xdr () =
           pr "struct %s_ret {\n" name;
           pr "  str %s<>;\n" n;
           pr "};\n\n"
+       | RPVList n ->
+          pr "struct %s_ret {\n" name;
+          pr "  guestfs_lvm_int_pv_list %s;\n" n;
+          pr "};\n\n"
+       | RVGList n ->
+          pr "struct %s_ret {\n" name;
+          pr "  guestfs_lvm_int_vg_list %s;\n" n;
+          pr "};\n\n"
+       | RLVList n ->
+          pr "struct %s_ret {\n" name;
+          pr "  guestfs_lvm_int_lv_list %s;\n" n;
+          pr "};\n\n"
       );
   ) functions;
 
@@ -361,6 +521,46 @@ struct guestfs_message_header {
 };
 "
 
+(* Generate the guestfs-structs.h file. *)
+and generate_structs_h () =
+  generate_header CStyle LGPLv2;
+
+  (* This is a public exported header file containing various
+   * structures.  The structures are carefully written to have
+   * exactly the same in-memory format as the XDR structures that
+   * we use on the wire to the daemon.  The reason for creating
+   * copies of these structures here is just so we don't have to
+   * export the whole of guestfs_protocol.h (which includes much
+   * unrelated and XDR-dependent stuff that we don't want to be
+   * public, or required by clients).
+   *
+   * To reiterate, we will pass these structures to and from the
+   * client with a simple assignment or memcpy, so the format
+   * must be identical to what rpcgen / the RFC defines.
+   *)
+
+  (* LVM public structures. *)
+  List.iter (
+    function
+    | typ, cols ->
+       pr "struct guestfs_lvm_%s {\n" typ;
+       List.iter (
+         function
+         | name, `String -> pr "  char *%s;\n" name
+         | name, `UUID -> pr "  char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name
+         | name, `Bytes -> pr "  uint64_t %s;\n" name
+         | name, `Int -> pr "  int64_t %s;\n" name
+         | name, `OptPercent -> pr "  float %s; /* [0..100] or -1 */\n" name
+       ) cols;
+       pr "};\n";
+       pr "\n";
+       pr "struct guestfs_lvm_%s_list {\n" typ;
+       pr "  uint32_t len;\n";
+       pr "  struct guestfs_lvm_%s *val;\n" typ;
+       pr "};\n";
+       pr "\n"
+  ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
+
 (* Generate the guestfs-actions.h file. *)
 and generate_actions_h () =
   generate_header CStyle LGPLv2;
@@ -374,6 +574,8 @@ and generate_actions_h () =
 (* Generate the client-side dispatch stubs. *)
 and generate_client_actions () =
   generate_header CStyle LGPLv2;
+
+  (* Client-side stubs for each function. *)
   List.iter (
     fun (shortname, style, _, _, _, _) ->
       let name = "guestfs_" ^ shortname in
@@ -385,7 +587,8 @@ and generate_client_actions () =
       pr "  struct guestfs_message_error err;\n";
       (match fst style with
        | Err -> ()
-       | RString _ | RStringList _ -> pr "  struct %s_ret ret;\n" name;
+       | RString _ | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
+          pr "  struct %s_ret ret;\n" name
       );
       pr "};\n\n";
 
@@ -408,7 +611,7 @@ and generate_client_actions () =
 
       (match fst style with
        | Err -> ()
-       |  RString _ | RStringList _ ->
+       | RString _ | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
            pr "  if (!xdr_%s_ret (xdr, &rv->ret)) {\n" name;
            pr "    error (g, \"%s: failed to parse reply\");\n" name;
            pr "    return;\n";
@@ -427,7 +630,8 @@ and generate_client_actions () =
       let error_code =
        match fst style with
        | Err -> "-1"
-       | RString _ | RStringList _ -> "NULL" in
+       | RString _ | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
+           "NULL" in
 
       pr "{\n";
 
@@ -499,6 +703,15 @@ and generate_client_actions () =
           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
+       | RPVList n ->
+          pr "  /* caller will free this */\n";
+          pr "  return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
+       | RVGList n ->
+          pr "  /* caller will free this */\n";
+          pr "  return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
+       | RLVList n ->
+          pr "  /* caller will free this */\n";
+          pr "  return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
       );
 
       pr "}\n\n"
@@ -507,17 +720,30 @@ and generate_client_actions () =
 (* Generate daemon/actions.h. *)
 and generate_daemon_actions_h () =
   generate_header CStyle GPLv2;
+
+  pr "#include \"../src/guestfs_protocol.h\"\n";
+  pr "\n";
+
   List.iter (
     fun (name, style, _, _, _, _) ->
-      generate_prototype ~single_line:true ~newline:true ("do_" ^ name) style;
+      generate_prototype
+       ~single_line:true ~newline:true ~in_daemon:true ("do_" ^ name) style;
   ) functions
 
 (* Generate the server-side stubs. *)
 and generate_daemon_actions () =
   generate_header CStyle GPLv2;
 
+  pr "#define _GNU_SOURCE // for strchrnul\n";
+  pr "\n";
+  pr "#include <stdio.h>\n";
+  pr "#include <stdlib.h>\n";
+  pr "#include <string.h>\n";
+  pr "#include <inttypes.h>\n";
+  pr "#include <ctype.h>\n";
   pr "#include <rpc/types.h>\n";
   pr "#include <rpc/xdr.h>\n";
+  pr "\n";
   pr "#include \"daemon.h\"\n";
   pr "#include \"../src/guestfs_protocol.h\"\n";
   pr "#include \"actions.h\"\n";
@@ -532,7 +758,11 @@ and generate_daemon_actions () =
        match fst style with
        | Err -> pr "  int r;\n"; "-1"
        | RString _ -> pr "  char *r;\n"; "NULL"
-       | RStringList _ -> pr "  char **r;\n"; "NULL" in
+       | RStringList _ -> pr "  char **r;\n"; "NULL"
+       | RPVList _ -> pr "  guestfs_lvm_int_pv_list *r;\n"; "NULL"
+       | RVGList _ -> pr "  guestfs_lvm_int_vg_list *r;\n"; "NULL"
+       | RLVList _ -> pr "  guestfs_lvm_int_lv_list *r;\n"; "NULL" in
+
       (match snd style with
        | P0 -> ()
        | args ->
@@ -582,6 +812,21 @@ and generate_daemon_actions () =
           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"
+       | RPVList 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 "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
+       | RVGList 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 "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
+       | RLVList 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 "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
       );
 
       pr "}\n\n";
@@ -602,7 +847,173 @@ and generate_daemon_actions () =
   pr "    default:\n";
   pr "      reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
   pr "  }\n";
-  pr "}\n"
+  pr "}\n";
+  pr "\n";
+
+  (* LVM columns and tokenization functions. *)
+  (* XXX This generates crap code.  We should rethink how we
+   * do this parsing.
+   *)
+  List.iter (
+    function
+    | typ, cols ->
+       pr "static const char *lvm_%s_cols = \"%s\";\n"
+         typ (String.concat "," (List.map fst cols));
+       pr "\n";
+
+       pr "static int lvm_tokenize_%s (char *str, struct guestfs_lvm_int_%s *r)\n" typ typ;
+       pr "{\n";
+       pr "  char *tok, *p, *next;\n";
+       pr "  int i, j;\n";
+       pr "\n";
+       (*
+       pr "  fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
+       pr "\n";
+       *)
+       pr "  if (!str) {\n";
+       pr "    fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
+       pr "    return -1;\n";
+       pr "  }\n";
+       pr "  if (!*str || isspace (*str)) {\n";
+       pr "    fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
+       pr "    return -1;\n";
+       pr "  }\n";
+       pr "  tok = str;\n";
+       List.iter (
+         fun (name, coltype) ->
+           pr "  if (!tok) {\n";
+           pr "    fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
+           pr "    return -1;\n";
+           pr "  }\n";
+           pr "  p = strchrnul (tok, ',');\n";
+           pr "  if (*p) next = p+1; else next = NULL;\n";
+           pr "  *p = '\\0';\n";
+           (match coltype with
+            | `String ->
+                pr "  r->%s = strdup (tok);\n" name;
+                pr "  if (r->%s == NULL) {\n" name;
+                pr "    perror (\"strdup\");\n";
+                pr "    return -1;\n";
+                pr "  }\n"
+            | `UUID ->
+                pr "  for (i = j = 0; i < 32; ++j) {\n";
+                pr "    if (tok[j] == '\\0') {\n";
+                pr "      fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
+                pr "      return -1;\n";
+                pr "    } else if (tok[j] != '-')\n";
+                pr "      r->%s[i++] = tok[j];\n" name;
+                pr "  }\n";
+            | `Bytes ->
+                pr "  if (sscanf (tok, \"%%\"SCNu64, &r->%s) != 1) {\n" name;
+                pr "    fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
+                pr "    return -1;\n";
+                pr "  }\n";
+            | `Int ->
+                pr "  if (sscanf (tok, \"%%\"SCNi64, &r->%s) != 1) {\n" name;
+                pr "    fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
+                pr "    return -1;\n";
+                pr "  }\n";
+            | `OptPercent ->
+                pr "  if (tok[0] == '\\0')\n";
+                pr "    r->%s = -1;\n" name;
+                pr "  else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
+                pr "    fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
+                pr "    return -1;\n";
+                pr "  }\n";
+           );
+           pr "  tok = next;\n";
+       ) cols;
+
+       pr "  if (tok != NULL) {\n";
+       pr "    fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
+       pr "    return -1;\n";
+       pr "  }\n";
+       pr "  return 0;\n";
+       pr "}\n";
+       pr "\n";
+
+       pr "guestfs_lvm_int_%s_list *\n" typ;
+       pr "parse_command_line_%ss (void)\n" typ;
+       pr "{\n";
+       pr "  char *out, *err;\n";
+       pr "  char *p, *pend;\n";
+       pr "  int r, i;\n";
+       pr "  guestfs_lvm_int_%s_list *ret;\n" typ;
+       pr "  void *newp;\n";
+       pr "\n";
+       pr "  ret = malloc (sizeof *ret);\n";
+       pr "  if (!ret) {\n";
+       pr "    reply_with_perror (\"malloc\");\n";
+       pr "    return NULL;\n";
+       pr "  }\n";
+       pr "\n";
+       pr "  ret->guestfs_lvm_int_%s_list_len = 0;\n" typ;
+       pr "  ret->guestfs_lvm_int_%s_list_val = NULL;\n" typ;
+       pr "\n";
+       pr "  r = command (&out, &err,\n";
+       pr "           \"/sbin/lvm\", \"%ss\",\n" typ;
+       pr "           \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
+       pr "           \"--nosuffix\", \"--separator\", \",\", \"--units\", \"b\", NULL);\n";
+       pr "  if (r == -1) {\n";
+       pr "    reply_with_error (\"%%s\", err);\n";
+       pr "    free (out);\n";
+       pr "    free (err);\n";
+       pr "    return NULL;\n";
+       pr "  }\n";
+       pr "\n";
+       pr "  free (err);\n";
+       pr "\n";
+       pr "  /* Tokenize each line of the output. */\n";
+       pr "  p = out;\n";
+       pr "  i = 0;\n";
+       pr "  while (p) {\n";
+       pr "    pend = strchr (p, '\\n');       /* Get the next line of output. */\n";
+       pr "    if (pend) {\n";
+       pr "      *pend = '\\0';\n";
+       pr "      pend++;\n";
+       pr "    }\n";
+       pr "\n";
+       pr "    while (*p && isspace (*p))      /* Skip any leading whitespace. */\n";
+       pr "      p++;\n";
+       pr "\n";
+       pr "    if (!*p) {                      /* Empty line?  Skip it. */\n";
+       pr "      p = pend;\n";
+       pr "      continue;\n";
+       pr "    }\n";
+       pr "\n";
+       pr "    /* Allocate some space to store this next entry. */\n";
+       pr "    newp = realloc (ret->guestfs_lvm_int_%s_list_val,\n" typ;
+       pr "                sizeof (guestfs_lvm_int_%s) * (i+1));\n" typ;
+       pr "    if (newp == NULL) {\n";
+       pr "      reply_with_perror (\"realloc\");\n";
+       pr "      free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
+       pr "      free (ret);\n";
+       pr "      free (out);\n";
+       pr "      return NULL;\n";
+       pr "    }\n";
+       pr "    ret->guestfs_lvm_int_%s_list_val = newp;\n" typ;
+       pr "\n";
+       pr "    /* Tokenize the next entry. */\n";
+       pr "    r = lvm_tokenize_%s (p, &ret->guestfs_lvm_int_%s_list_val[i]);\n" typ typ;
+       pr "    if (r == -1) {\n";
+       pr "      reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
+        pr "      free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
+        pr "      free (ret);\n";
+       pr "      free (out);\n";
+       pr "      return NULL;\n";
+       pr "    }\n";
+       pr "\n";
+       pr "    ++i;\n";
+       pr "    p = pend;\n";
+       pr "  }\n";
+       pr "\n";
+       pr "  ret->guestfs_lvm_int_%s_list_len = i;\n" typ;
+       pr "\n";
+       pr "  free (out);\n";
+       pr "  return ret;\n";
+       pr "}\n"
+
+  ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
 
 (* Generate a lot of different functions for guestfish. *)
 and generate_fish_cmds () =
@@ -611,7 +1022,9 @@ and generate_fish_cmds () =
   pr "#include <stdio.h>\n";
   pr "#include <stdlib.h>\n";
   pr "#include <string.h>\n";
+  pr "#include <inttypes.h>\n";
   pr "\n";
+  pr "#include <guestfs.h>\n";
   pr "#include \"fish.h\"\n";
   pr "\n";
 
@@ -669,6 +1082,45 @@ FTP."
   pr "}\n";
   pr "\n";
 
+  (* print_{pv,vg,lv}_list functions *)
+  List.iter (
+    function
+    | typ, cols ->
+       pr "static void print_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
+       pr "{\n";
+       pr "  int i;\n";
+       pr "\n";
+       List.iter (
+         function
+         | name, `String ->
+             pr "  printf (\"%s: %%s\\n\", %s->%s);\n" name typ name
+         | name, `UUID ->
+             pr "  printf (\"%s: \");\n" name;
+             pr "  for (i = 0; i < 32; ++i)\n";
+             pr "    printf (\"%%c\", %s->%s[i]);\n" typ name;
+             pr "  printf (\"\\n\");\n"
+         | name, `Bytes ->
+             pr "  printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name
+         | name, `Int ->
+             pr "  printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
+         | name, `OptPercent ->
+             pr "  if (%s->%s >= 0) printf (\"%s: %%g %%%%\\n\", %s->%s);\n"
+               typ name name typ name;
+             pr "  else printf (\"%s: \\n\");\n" name
+       ) cols;
+       pr "}\n";
+       pr "\n";
+       pr "static void print_%s_list (struct guestfs_lvm_%s_list *%ss)\n"
+         typ typ typ;
+       pr "{\n";
+       pr "  int i;\n";
+       pr "\n";
+       pr "  for (i = 0; i < %ss->len; ++i)\n" typ;
+       pr "    print_%s (&%ss->val[i]);\n" typ typ;
+       pr "}\n";
+       pr "\n";
+  ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
+
   (* run_<action> actions *)
   List.iter (
     fun (name, style, _, _, _, _) ->
@@ -678,6 +1130,9 @@ FTP."
        | Err -> pr "  int r;\n"
        | RString _ -> pr "  char *r;\n"
        | RStringList _ -> pr "  char **r;\n"
+       | RPVList _ -> pr "  struct guestfs_lvm_pv_list *r;\n"
+       | RVGList _ -> pr "  struct guestfs_lvm_vg_list *r;\n"
+       | RLVList _ -> pr "  struct guestfs_lvm_lv_list *r;\n"
       );
       iter_args (
        function
@@ -716,6 +1171,21 @@ FTP."
           pr "  print_strings (r);\n";
           pr "  free_strings (r);\n";
           pr "  return 0;\n"
+       | RPVList _ ->
+          pr "  if (r == NULL) return -1;\n";
+          pr "  print_pv_list (r);\n";
+          pr "  guestfs_free_lvm_pv_list (r);\n";
+          pr "  return 0;\n"
+       | RVGList _ ->
+          pr "  if (r == NULL) return -1;\n";
+          pr "  print_vg_list (r);\n";
+          pr "  guestfs_free_lvm_vg_list (r);\n";
+          pr "  return 0;\n"
+       | RLVList _ ->
+          pr "  if (r == NULL) return -1;\n";
+          pr "  print_lv_list (r);\n";
+          pr "  guestfs_free_lvm_lv_list (r);\n";
+          pr "  return 0;\n"
       );
       pr "}\n";
       pr "\n"
@@ -745,7 +1215,7 @@ FTP."
 
 (* Generate a C function prototype. *)
 and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
-    ?(single_line = false) ?(newline = false)
+    ?(single_line = false) ?(newline = false) ?(in_daemon = false)
     ?handle name style =
   if extern then pr "extern ";
   if static then pr "static ";
@@ -753,6 +1223,15 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
    | Err -> pr "int "
    | RString _ -> pr "char *"
    | RStringList _ -> pr "char **"
+   | RPVList _ ->
+       if not in_daemon then pr "struct guestfs_lvm_pv_list *"
+       else pr "guestfs_lvm_int_pv_list *"
+   | RVGList _ ->
+       if not in_daemon then pr "struct guestfs_lvm_vg_list *"
+       else pr "guestfs_lvm_int_vg_list *"
+   | RLVList _ ->
+       if not in_daemon then pr "struct guestfs_lvm_lv_list *"
+       else pr "guestfs_lvm_int_lv_list *"
   );
   pr "%s (" name;
   let comma = ref false in
@@ -810,6 +1289,10 @@ let () =
   generate_xdr ();
   close ();
 
+  let close = output_to "src/guestfs-structs.h" in
+  generate_structs_h ();
+  close ();
+
   let close = output_to "src/guestfs-actions.h" in
   generate_actions_h ();
   close ();
@@ -830,6 +1313,10 @@ let () =
   generate_fish_cmds ();
   close ();
 
+  let close = output_to "guestfs-structs.pod" in
+  generate_structs_pod ();
+  close ();
+
   let close = output_to "guestfs-actions.pod" in
-  generate_pod ();
+  generate_actions_pod ();
   close ()
index d2ec0c7..83f5ec1 100644 (file)
@@ -595,3 +595,216 @@ char **guestfs_list_partitions (guestfs_h *g)
   return rv.ret.partitions.partitions_val;
 }
 
+struct pvs_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_pvs_ret ret;
+};
+
+static void pvs_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct pvs_rv *rv = (struct pvs_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_pvs: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_pvs: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_pvs_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_pvs: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+struct guestfs_lvm_pv_list *guestfs_pvs (guestfs_h *g)
+{
+  struct pvs_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_pvs called from the wrong state, %d != READY",
+      g->state);
+    return NULL;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  serial = dispatch (g, GUESTFS_PROC_PVS, NULL, NULL);
+  if (serial == -1)
+    return NULL;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = pvs_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_pvs failed, see earlier error messages");
+    return NULL;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_PVS, serial) == -1)
+    return NULL;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return NULL;
+  }
+
+  /* caller will free this */
+  return safe_memdup (g, &rv.ret.physvols, sizeof (rv.ret.physvols));
+}
+
+struct vgs_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_vgs_ret ret;
+};
+
+static void vgs_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct vgs_rv *rv = (struct vgs_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_vgs: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_vgs: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_vgs_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_vgs: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+struct guestfs_lvm_vg_list *guestfs_vgs (guestfs_h *g)
+{
+  struct vgs_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_vgs called from the wrong state, %d != READY",
+      g->state);
+    return NULL;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  serial = dispatch (g, GUESTFS_PROC_VGS, NULL, NULL);
+  if (serial == -1)
+    return NULL;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = vgs_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_vgs failed, see earlier error messages");
+    return NULL;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_VGS, serial) == -1)
+    return NULL;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return NULL;
+  }
+
+  /* caller will free this */
+  return safe_memdup (g, &rv.ret.volgroups, sizeof (rv.ret.volgroups));
+}
+
+struct lvs_rv {
+  int cb_done;  /* flag to indicate callback was called */
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_lvs_ret ret;
+};
+
+static void lvs_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  struct lvs_rv *rv = (struct lvs_rv *) data;
+
+  if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+    error (g, "guestfs_lvs: failed to parse reply header");
+    return;
+  }
+  if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+    if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+      error (g, "guestfs_lvs: failed to parse reply error");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_lvs_ret (xdr, &rv->ret)) {
+    error (g, "guestfs_lvs: failed to parse reply");
+    return;
+  }
+ done:
+  rv->cb_done = 1;
+  main_loop.main_loop_quit (g);
+}
+
+struct guestfs_lvm_lv_list *guestfs_lvs (guestfs_h *g)
+{
+  struct lvs_rv rv;
+  int serial;
+
+  if (g->state != READY) {
+    error (g, "guestfs_lvs called from the wrong state, %d != READY",
+      g->state);
+    return NULL;
+  }
+
+  memset (&rv, 0, sizeof rv);
+
+  serial = dispatch (g, GUESTFS_PROC_LVS, NULL, NULL);
+  if (serial == -1)
+    return NULL;
+
+  rv.cb_done = 0;
+  g->reply_cb_internal = lvs_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_lvs failed, see earlier error messages");
+    return NULL;
+  }
+
+  if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LVS, serial) == -1)
+    return NULL;
+
+  if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", rv.err.error);
+    return NULL;
+  }
+
+  /* caller will free this */
+  return safe_memdup (g, &rv.ret.logvols, sizeof (rv.ret.logvols));
+}
+
index 7019d98..d48cd9b 100644 (file)
@@ -27,3 +27,6 @@ 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);
+extern struct guestfs_lvm_pv_list *guestfs_pvs (guestfs_h *handle);
+extern struct guestfs_lvm_vg_list *guestfs_vgs (guestfs_h *handle);
+extern struct guestfs_lvm_lv_list *guestfs_lvs (guestfs_h *handle);
diff --git a/src/guestfs-structs.h b/src/guestfs-structs.h
new file mode 100644 (file)
index 0000000..45ce131
--- /dev/null
@@ -0,0 +1,94 @@
+/* libguestfs generated file
+ * WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'.
+ * ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+struct guestfs_lvm_pv {
+  char *pv_name;
+  char pv_uuid[32]; /* this is NOT nul-terminated, be careful when printing */
+  char *pv_fmt;
+  uint64_t pv_size;
+  uint64_t dev_size;
+  uint64_t pv_free;
+  uint64_t pv_used;
+  char *pv_attr;
+  int64_t pv_pe_count;
+  int64_t pv_pe_alloc_count;
+  char *pv_tags;
+  uint64_t pe_start;
+  int64_t pv_mda_count;
+  uint64_t pv_mda_free;
+};
+
+struct guestfs_lvm_pv_list {
+  uint32_t len;
+  struct guestfs_lvm_pv *val;
+};
+
+struct guestfs_lvm_vg {
+  char *vg_name;
+  char vg_uuid[32]; /* this is NOT nul-terminated, be careful when printing */
+  char *vg_fmt;
+  char *vg_attr;
+  uint64_t vg_size;
+  uint64_t vg_free;
+  char *vg_sysid;
+  uint64_t vg_extent_size;
+  int64_t vg_extent_count;
+  int64_t vg_free_count;
+  int64_t max_lv;
+  int64_t max_pv;
+  int64_t pv_count;
+  int64_t lv_count;
+  int64_t snap_count;
+  int64_t vg_seqno;
+  char *vg_tags;
+  int64_t vg_mda_count;
+  uint64_t vg_mda_free;
+};
+
+struct guestfs_lvm_vg_list {
+  uint32_t len;
+  struct guestfs_lvm_vg *val;
+};
+
+struct guestfs_lvm_lv {
+  char *lv_name;
+  char lv_uuid[32]; /* this is NOT nul-terminated, be careful when printing */
+  char *lv_attr;
+  int64_t lv_major;
+  int64_t lv_minor;
+  int64_t lv_kernel_major;
+  int64_t lv_kernel_minor;
+  uint64_t lv_size;
+  int64_t seg_count;
+  char *origin;
+  float snap_percent; /* [0..100] or -1 */
+  float copy_percent; /* [0..100] or -1 */
+  char *move_pv;
+  char *lv_tags;
+  char *mirror_log;
+  char *modules;
+};
+
+struct guestfs_lvm_lv_list {
+  uint32_t len;
+  struct guestfs_lvm_lv *val;
+};
+
index 3492c62..092e405 100644 (file)
 
 static void error (guestfs_h *g, const char *fs, ...);
 static void perrorf (guestfs_h *g, const char *fs, ...);
-static void *safe_malloc (guestfs_h *g, int nbytes);
+static void *safe_malloc (guestfs_h *g, size_t nbytes);
 static void *safe_realloc (guestfs_h *g, void *ptr, int nbytes);
 static char *safe_strdup (guestfs_h *g, const char *str);
+static void *safe_memdup (guestfs_h *g, void *ptr, size_t size);
 
 static void default_error_cb (guestfs_h *g, void *data, const char *msg);
 static void stdout_event (void *data, int watch, int fd, int events);
@@ -328,7 +329,7 @@ perrorf (guestfs_h *g, const char *fs, ...)
 }
 
 static void *
-safe_malloc (guestfs_h *g, int nbytes)
+safe_malloc (guestfs_h *g, size_t nbytes)
 {
   void *ptr = malloc (nbytes);
   if (!ptr) g->abort_cb ();
@@ -351,6 +352,15 @@ safe_strdup (guestfs_h *g, const char *str)
   return s;
 }
 
+static void *
+safe_memdup (guestfs_h *g, void *ptr, size_t size)
+{
+  void *p = malloc (size);
+  if (!p) g->abort_cb ();
+  memcpy (p, ptr, size);
+  return p;
+}
+
 void
 guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_cb cb)
 {
@@ -1019,6 +1029,28 @@ sock_read_event (void *data, int watch, int fd, int events)
     goto cleanup;
   }
 
+  /* Got the full message, begin processing it. */
+  if (g->verbose) {
+    int i, j;
+
+    for (i = 0; i < g->msg_in_size; i += 16) {
+      printf ("%04x: ", i);
+      for (j = i; j < MIN (i+16, g->msg_in_size); ++j)
+       printf ("%02x ", (unsigned char) g->msg_in[j]);
+      for (; j < i+16; ++j)
+       printf ("   ");
+      printf ("|");
+      for (j = i; j < MIN (i+16, g->msg_in_size); ++j)
+       if (isprint (g->msg_in[j]))
+         printf ("%c", g->msg_in[j]);
+       else
+         printf (".");
+      for (; j < i+16; ++j)
+       printf (" ");
+      printf ("|\n");
+    }
+  }
+
   /* Not in the expected state. */
   if (g->state != BUSY)
     error (g, "state %d != BUSY", g->state);
@@ -1231,6 +1263,31 @@ check_reply_header (guestfs_h *g,
  */
 #include "guestfs-actions.c"
 
+/* Structure-freeing functions.  These rely on the fact that the
+ * structure format is identical to the XDR format.  See note in
+ * generator.ml.
+ */
+void
+guestfs_free_lvm_pv_list (struct guestfs_lvm_pv_list *x)
+{
+  xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_pv_list, (char *) x);
+  free (x);
+}
+
+void
+guestfs_free_lvm_vg_list (struct guestfs_lvm_vg_list *x)
+{
+  xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_vg_list, (char *) x);
+  free (x);
+}
+
+void
+guestfs_free_lvm_lv_list (struct guestfs_lvm_lv_list *x)
+{
+  xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_lv_list, (char *) x);
+  free (x);
+}
+
 /* This is the default main loop implementation, using select(2). */
 
 struct handle_cb_data {
index 575e0c7..cbe2c82 100644 (file)
@@ -59,8 +59,13 @@ extern int guestfs_get_autosync (guestfs_h *g);
 extern void guestfs_set_path (guestfs_h *g, const char *path);
 extern const char *guestfs_get_path (guestfs_h *g);
 
+#include <guestfs-structs.h>
 #include <guestfs-actions.h>
 
+extern void guestfs_free_lvm_pv_list (struct guestfs_lvm_pv_list *);
+extern void guestfs_free_lvm_vg_list (struct guestfs_lvm_vg_list *);
+extern void guestfs_free_lvm_lv_list (struct guestfs_lvm_lv_list *);
+
 /* Low-level event API. */
 typedef void (*guestfs_reply_cb) (guestfs_h *g, void *data, XDR *xdr);
 typedef void (*guestfs_log_message_cb) (guestfs_h *g, void *data, char *buf, int len);
index 80f618b..dfdd0d8 100644 (file)
@@ -16,6 +16,164 @@ xdr_str (XDR *xdrs, str *objp)
 }
 
 bool_t
+xdr_guestfs_lvm_int_pv (XDR *xdrs, guestfs_lvm_int_pv *objp)
+{
+       register int32_t *buf;
+
+       int i;
+        if (!xdr_string (xdrs, &objp->pv_name, ~0))
+                return FALSE;
+        if (!xdr_opaque (xdrs, objp->pv_uuid, 32))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->pv_fmt, ~0))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->pv_size))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->dev_size))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->pv_free))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->pv_used))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->pv_attr, ~0))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->pv_pe_count))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->pv_pe_alloc_count))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->pv_tags, ~0))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->pe_start))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->pv_mda_count))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->pv_mda_free))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_lvm_int_pv_list (XDR *xdrs, guestfs_lvm_int_pv_list *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->guestfs_lvm_int_pv_list_val, (u_int *) &objp->guestfs_lvm_int_pv_list_len, ~0,
+               sizeof (guestfs_lvm_int_pv), (xdrproc_t) xdr_guestfs_lvm_int_pv))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_lvm_int_vg (XDR *xdrs, guestfs_lvm_int_vg *objp)
+{
+       register int32_t *buf;
+
+       int i;
+        if (!xdr_string (xdrs, &objp->vg_name, ~0))
+                return FALSE;
+        if (!xdr_opaque (xdrs, objp->vg_uuid, 32))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->vg_fmt, ~0))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->vg_attr, ~0))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->vg_size))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->vg_free))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->vg_sysid, ~0))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->vg_extent_size))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->vg_extent_count))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->vg_free_count))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->max_lv))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->max_pv))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->pv_count))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->lv_count))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->snap_count))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->vg_seqno))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->vg_tags, ~0))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->vg_mda_count))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->vg_mda_free))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_lvm_int_vg_list (XDR *xdrs, guestfs_lvm_int_vg_list *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->guestfs_lvm_int_vg_list_val, (u_int *) &objp->guestfs_lvm_int_vg_list_len, ~0,
+               sizeof (guestfs_lvm_int_vg), (xdrproc_t) xdr_guestfs_lvm_int_vg))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_lvm_int_lv (XDR *xdrs, guestfs_lvm_int_lv *objp)
+{
+       register int32_t *buf;
+
+       int i;
+        if (!xdr_string (xdrs, &objp->lv_name, ~0))
+                return FALSE;
+        if (!xdr_opaque (xdrs, objp->lv_uuid, 32))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->lv_attr, ~0))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->lv_major))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->lv_minor))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->lv_kernel_major))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->lv_kernel_minor))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->lv_size))
+                return FALSE;
+        if (!xdr_quad_t (xdrs, &objp->seg_count))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->origin, ~0))
+                return FALSE;
+        if (!xdr_float (xdrs, &objp->snap_percent))
+                return FALSE;
+        if (!xdr_float (xdrs, &objp->copy_percent))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->move_pv, ~0))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->lv_tags, ~0))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->mirror_log, ~0))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->modules, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_lvm_int_lv_list (XDR *xdrs, guestfs_lvm_int_lv_list *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->guestfs_lvm_int_lv_list_val, (u_int *) &objp->guestfs_lvm_int_lv_list_len, ~0,
+               sizeof (guestfs_lvm_int_lv), (xdrproc_t) xdr_guestfs_lvm_int_lv))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
 xdr_guestfs_mount_args (XDR *xdrs, guestfs_mount_args *objp)
 {
        register int32_t *buf;
@@ -121,6 +279,36 @@ xdr_guestfs_list_partitions_ret (XDR *xdrs, guestfs_list_partitions_ret *objp)
 }
 
 bool_t
+xdr_guestfs_pvs_ret (XDR *xdrs, guestfs_pvs_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_guestfs_lvm_int_pv_list (xdrs, &objp->physvols))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_vgs_ret (XDR *xdrs, guestfs_vgs_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_guestfs_lvm_int_vg_list (xdrs, &objp->volgroups))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_lvs_ret (XDR *xdrs, guestfs_lvs_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_guestfs_lvm_int_lv_list (xdrs, &objp->logvols))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
 xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp)
 {
        register int32_t *buf;
index 3e19212..a168e43 100644 (file)
@@ -16,6 +16,82 @@ extern "C" {
 
 typedef char *str;
 
+struct guestfs_lvm_int_pv {
+       char *pv_name;
+       char pv_uuid[32];
+       char *pv_fmt;
+       quad_t pv_size;
+       quad_t dev_size;
+       quad_t pv_free;
+       quad_t pv_used;
+       char *pv_attr;
+       quad_t pv_pe_count;
+       quad_t pv_pe_alloc_count;
+       char *pv_tags;
+       quad_t pe_start;
+       quad_t pv_mda_count;
+       quad_t pv_mda_free;
+};
+typedef struct guestfs_lvm_int_pv guestfs_lvm_int_pv;
+
+typedef struct {
+       u_int guestfs_lvm_int_pv_list_len;
+       guestfs_lvm_int_pv *guestfs_lvm_int_pv_list_val;
+} guestfs_lvm_int_pv_list;
+
+struct guestfs_lvm_int_vg {
+       char *vg_name;
+       char vg_uuid[32];
+       char *vg_fmt;
+       char *vg_attr;
+       quad_t vg_size;
+       quad_t vg_free;
+       char *vg_sysid;
+       quad_t vg_extent_size;
+       quad_t vg_extent_count;
+       quad_t vg_free_count;
+       quad_t max_lv;
+       quad_t max_pv;
+       quad_t pv_count;
+       quad_t lv_count;
+       quad_t snap_count;
+       quad_t vg_seqno;
+       char *vg_tags;
+       quad_t vg_mda_count;
+       quad_t vg_mda_free;
+};
+typedef struct guestfs_lvm_int_vg guestfs_lvm_int_vg;
+
+typedef struct {
+       u_int guestfs_lvm_int_vg_list_len;
+       guestfs_lvm_int_vg *guestfs_lvm_int_vg_list_val;
+} guestfs_lvm_int_vg_list;
+
+struct guestfs_lvm_int_lv {
+       char *lv_name;
+       char lv_uuid[32];
+       char *lv_attr;
+       quad_t lv_major;
+       quad_t lv_minor;
+       quad_t lv_kernel_major;
+       quad_t lv_kernel_minor;
+       quad_t lv_size;
+       quad_t seg_count;
+       char *origin;
+       float snap_percent;
+       float copy_percent;
+       char *move_pv;
+       char *lv_tags;
+       char *mirror_log;
+       char *modules;
+};
+typedef struct guestfs_lvm_int_lv guestfs_lvm_int_lv;
+
+typedef struct {
+       u_int guestfs_lvm_int_lv_list_len;
+       guestfs_lvm_int_lv *guestfs_lvm_int_lv_list_val;
+} guestfs_lvm_int_lv_list;
+
 struct guestfs_mount_args {
        char *device;
        char *mountpoint;
@@ -76,6 +152,21 @@ struct guestfs_list_partitions_ret {
 };
 typedef struct guestfs_list_partitions_ret guestfs_list_partitions_ret;
 
+struct guestfs_pvs_ret {
+       guestfs_lvm_int_pv_list physvols;
+};
+typedef struct guestfs_pvs_ret guestfs_pvs_ret;
+
+struct guestfs_vgs_ret {
+       guestfs_lvm_int_vg_list volgroups;
+};
+typedef struct guestfs_vgs_ret guestfs_vgs_ret;
+
+struct guestfs_lvs_ret {
+       guestfs_lvm_int_lv_list logvols;
+};
+typedef struct guestfs_lvs_ret guestfs_lvs_ret;
+
 enum guestfs_procedure {
        GUESTFS_PROC_MOUNT = 1,
        GUESTFS_PROC_SYNC = 2,
@@ -85,7 +176,10 @@ enum guestfs_procedure {
        GUESTFS_PROC_LS = 6,
        GUESTFS_PROC_LIST_DEVICES = 7,
        GUESTFS_PROC_LIST_PARTITIONS = 8,
-       GUESTFS_PROC_dummy = 8 + 1,
+       GUESTFS_PROC_PVS = 9,
+       GUESTFS_PROC_VGS = 10,
+       GUESTFS_PROC_LVS = 11,
+       GUESTFS_PROC_dummy = 11 + 1,
 };
 typedef enum guestfs_procedure guestfs_procedure;
 #define GUESTFS_MESSAGE_MAX 4194304
@@ -124,6 +218,12 @@ 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_lvm_int_pv (XDR *, guestfs_lvm_int_pv*);
+extern  bool_t xdr_guestfs_lvm_int_pv_list (XDR *, guestfs_lvm_int_pv_list*);
+extern  bool_t xdr_guestfs_lvm_int_vg (XDR *, guestfs_lvm_int_vg*);
+extern  bool_t xdr_guestfs_lvm_int_vg_list (XDR *, guestfs_lvm_int_vg_list*);
+extern  bool_t xdr_guestfs_lvm_int_lv (XDR *, guestfs_lvm_int_lv*);
+extern  bool_t xdr_guestfs_lvm_int_lv_list (XDR *, guestfs_lvm_int_lv_list*);
 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*);
@@ -134,6 +234,9 @@ 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_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_pvs_ret (XDR *, guestfs_pvs_ret*);
+extern  bool_t xdr_guestfs_vgs_ret (XDR *, guestfs_vgs_ret*);
+extern  bool_t xdr_guestfs_lvs_ret (XDR *, guestfs_lvs_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*);
@@ -142,6 +245,12 @@ 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_lvm_int_pv ();
+extern bool_t xdr_guestfs_lvm_int_pv_list ();
+extern bool_t xdr_guestfs_lvm_int_vg ();
+extern bool_t xdr_guestfs_lvm_int_vg_list ();
+extern bool_t xdr_guestfs_lvm_int_lv ();
+extern bool_t xdr_guestfs_lvm_int_lv_list ();
 extern bool_t xdr_guestfs_mount_args ();
 extern bool_t xdr_guestfs_touch_args ();
 extern bool_t xdr_guestfs_cat_args ();
@@ -152,6 +261,9 @@ extern bool_t xdr_guestfs_ls_args ();
 extern bool_t xdr_guestfs_ls_ret ();
 extern bool_t xdr_guestfs_list_devices_ret ();
 extern bool_t xdr_guestfs_list_partitions_ret ();
+extern bool_t xdr_guestfs_pvs_ret ();
+extern bool_t xdr_guestfs_vgs_ret ();
+extern bool_t xdr_guestfs_lvs_ret ();
 extern bool_t xdr_guestfs_procedure ();
 extern bool_t xdr_guestfs_message_direction ();
 extern bool_t xdr_guestfs_message_status ();
index be0efdb..fedc6f2 100644 (file)
 
 typedef string str<>;
 
+struct guestfs_lvm_int_pv {
+  string pv_name<>;
+  opaque pv_uuid[32];
+  string pv_fmt<>;
+  hyper pv_size;
+  hyper dev_size;
+  hyper pv_free;
+  hyper pv_used;
+  string pv_attr<>;
+  hyper pv_pe_count;
+  hyper pv_pe_alloc_count;
+  string pv_tags<>;
+  hyper pe_start;
+  hyper pv_mda_count;
+  hyper pv_mda_free;
+};
+
+typedef struct guestfs_lvm_int_pv guestfs_lvm_int_pv_list<>;
+
+struct guestfs_lvm_int_vg {
+  string vg_name<>;
+  opaque vg_uuid[32];
+  string vg_fmt<>;
+  string vg_attr<>;
+  hyper vg_size;
+  hyper vg_free;
+  string vg_sysid<>;
+  hyper vg_extent_size;
+  hyper vg_extent_count;
+  hyper vg_free_count;
+  hyper max_lv;
+  hyper max_pv;
+  hyper pv_count;
+  hyper lv_count;
+  hyper snap_count;
+  hyper vg_seqno;
+  string vg_tags<>;
+  hyper vg_mda_count;
+  hyper vg_mda_free;
+};
+
+typedef struct guestfs_lvm_int_vg guestfs_lvm_int_vg_list<>;
+
+struct guestfs_lvm_int_lv {
+  string lv_name<>;
+  opaque lv_uuid[32];
+  string lv_attr<>;
+  hyper lv_major;
+  hyper lv_minor;
+  hyper lv_kernel_major;
+  hyper lv_kernel_minor;
+  hyper lv_size;
+  hyper seg_count;
+  string origin<>;
+  float snap_percent;
+  float copy_percent;
+  string move_pv<>;
+  string lv_tags<>;
+  string mirror_log<>;
+  string modules<>;
+};
+
+typedef struct guestfs_lvm_int_lv guestfs_lvm_int_lv_list<>;
+
 /* guestfs_mount */
 
 struct guestfs_mount_args {
@@ -78,6 +142,24 @@ struct guestfs_list_partitions_ret {
   str partitions<>;
 };
 
+/* guestfs_pvs */
+
+struct guestfs_pvs_ret {
+  guestfs_lvm_int_pv_list physvols;
+};
+
+/* guestfs_vgs */
+
+struct guestfs_vgs_ret {
+  guestfs_lvm_int_vg_list volgroups;
+};
+
+/* guestfs_lvs */
+
+struct guestfs_lvs_ret {
+  guestfs_lvm_int_lv_list logvols;
+};
+
 enum guestfs_procedure {
   GUESTFS_PROC_MOUNT = 1,
   GUESTFS_PROC_SYNC = 2,
@@ -87,6 +169,9 @@ enum guestfs_procedure {
   GUESTFS_PROC_LS = 6,
   GUESTFS_PROC_LIST_DEVICES = 7,
   GUESTFS_PROC_LIST_PARTITIONS = 8,
+  GUESTFS_PROC_PVS = 9,
+  GUESTFS_PROC_VGS = 10,
+  GUESTFS_PROC_LVS = 11,
   GUESTFS_PROC_dummy
 };