diff options
-rw-r--r-- | Makefile.am | 13 | ||||
-rw-r--r-- | daemon/Makefile.am | 1 | ||||
-rw-r--r-- | daemon/actions.h | 5 | ||||
-rw-r--r-- | daemon/daemon.h | 5 | ||||
-rw-r--r-- | daemon/lvm.c | 50 | ||||
-rw-r--r-- | daemon/proto.c | 2 | ||||
-rw-r--r-- | daemon/stubs.c | 989 | ||||
-rw-r--r-- | fish/cmds.c | 170 | ||||
-rw-r--r-- | fish/fish.c | 2 | ||||
-rw-r--r-- | guestfs-actions.pod | 30 | ||||
-rw-r--r-- | guestfs-structs.pod | 87 | ||||
-rw-r--r-- | guestfs.pod | 4 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rwxr-xr-x | src/generator.ml | 509 | ||||
-rw-r--r-- | src/guestfs-actions.c | 213 | ||||
-rw-r--r-- | src/guestfs-actions.h | 3 | ||||
-rw-r--r-- | src/guestfs-structs.h | 94 | ||||
-rw-r--r-- | src/guestfs.c | 61 | ||||
-rw-r--r-- | src/guestfs.h | 5 | ||||
-rw-r--r-- | src/guestfs_protocol.c | 188 | ||||
-rw-r--r-- | src/guestfs_protocol.h | 114 | ||||
-rw-r--r-- | src/guestfs_protocol.x | 85 |
22 files changed, 2610 insertions, 22 deletions
diff --git a/Makefile.am b/Makefile.am index 8e2b49d6..39cf86cf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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" \ diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 5faa6523..4d56034d 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -25,6 +25,7 @@ guestfsd_SOURCES = \ file.c \ guestfsd.c \ ls.c \ + lvm.c \ mount.c \ proto.c \ stubs.c \ diff --git a/daemon/actions.h b/daemon/actions.h index 58b8a24c..6527e0c2 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -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 (); diff --git a/daemon/daemon.h b/daemon/daemon.h index 60e1982b..e2b6a2a3 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -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 index 00000000..1b888f24 --- /dev/null +++ b/daemon/lvm.c @@ -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 (); +} diff --git a/daemon/proto.c b/daemon/proto.c index 62d8da5a..93d33c2c 100644 --- a/daemon/proto.c +++ b/daemon/proto.c @@ -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; diff --git a/daemon/stubs.c b/daemon/stubs.c index faece82d..24f60427 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -19,8 +19,16 @@ * 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; +} diff --git a/fish/cmds.c b/fish/cmds.c index 28dd9563..6d8e4543 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -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; diff --git a/fish/fish.c b/fish/fish.c index 374416e7..e0d04706 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -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': diff --git a/guestfs-actions.pod b/guestfs-actions.pod index f89ae26b..a2a19ba4 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -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 index 00000000..31b9ff28 --- /dev/null +++ b/guestfs-structs.pod @@ -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 *); + diff --git a/guestfs.pod b/guestfs.pod index 3c28b04d..7396644d 100644 --- a/guestfs.pod +++ b/guestfs.pod @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 566fa410..7a4e360a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/generator.ml b/src/generator.ml index 12c51fc8..8d1dc046 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -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 () diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index d2ec0c76..83f5ec15 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -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)); +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index 7019d988..d48cd9bf 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -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 index 00000000..45ce1316 --- /dev/null +++ b/src/guestfs-structs.h @@ -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; +}; + diff --git a/src/guestfs.c b/src/guestfs.c index 3492c624..092e405e 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -58,9 +58,10 @@ 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 { diff --git a/src/guestfs.h b/src/guestfs.h index 575e0c72..cbe2c82c 100644 --- a/src/guestfs.h +++ b/src/guestfs.h @@ -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); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index 80f618b1..dfdd0d8b 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -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; diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index 3e192127..a168e43a 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -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 (); diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index be0efdbb..fedc6f2d 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -21,6 +21,70 @@ 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 }; |