From: Richard W.M. Jones Date: Mon, 22 Nov 2010 17:33:35 +0000 (+0000) Subject: New tool: virt-filesystems X-Git-Tag: 1.7.12~2 X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=commitdiff_plain;h=fbc2555903be8c88ad9430d871cf0d27c8fded1e New tool: virt-filesystems This tool replaces virt-list-filesystems and virt-list-partitions with a new tool written in C with a more uniform command line structure and output. This existing Perl tools are deprecated but remain indefinitely. --- diff --git a/.gitignore b/.gitignore index 16c23a1..f66fdcc 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,9 @@ capitests/test*.tmp cat/virt-cat cat/virt-cat.1 cat/virt-cat.static +cat/virt-filesystems +cat/virt-filesystems.1 +cat/virt-filesystems.static cat/virt-ls cat/virt-ls.1 cat/virt-ls.static @@ -112,6 +115,7 @@ html/recipes.html html/virt-cat.1.html html/virt-df.1.html html/virt-edit.1.html +html/virt-filesystems.1.html html/virt-inspector.1.html html/virt-list-filesystems.1.html html/virt-list-partitions.1.html diff --git a/HACKING b/HACKING index cf159d5..05fb97b 100644 --- a/HACKING +++ b/HACKING @@ -72,7 +72,8 @@ capitests/ Automated tests of the C API. cat/ - The 'virt-cat' and 'virt-ls' commands and documentation. + The 'virt-cat', 'virt-filesystems' and 'virt-ls' commands and + documentation. contrib/ Outside contributions, experimental parts. diff --git a/Makefile.am b/Makefile.am index b75397c..e6da029 100644 --- a/Makefile.am +++ b/Makefile.am @@ -142,6 +142,7 @@ HTMLFILES = \ html/virt-cat.1.html \ html/virt-df.1.html \ html/virt-edit.1.html \ + html/virt-filesystems.1.html \ html/virt-inspector.1.html \ html/virt-list-filesystems.1.html \ html/virt-list-partitions.1.html \ @@ -249,8 +250,9 @@ bindist: cp fuse/guestmount.static $(BINTMPDIR)$(bindir)/guestmount $(MAKE) -C test-tool libguestfs-test-tool.static cp test-tool/libguestfs-test-tool.static $(BINTMPDIR)$(bindir)/libguestfs-test-tool - $(MAKE) -C cat virt-cat.static virt-ls.static + $(MAKE) -C cat virt-cat.static virt-filesystems.static virt-ls.static cp cat/virt-cat.static $(BINTMPDIR)$(bindir)/virt-cat + cp cat/virt-filesystems.static $(BINTMPDIR)$(bindir)/virt-filesystems cp cat/virt-ls.static $(BINTMPDIR)$(bindir)/virt-ls $(MAKE) -C inspector virt-inspector.static cp inspector/virt-inspector.static $(BINTMPDIR)$(bindir)/virt-inspector diff --git a/bootstrap b/bootstrap index c2507fc..c1712ae 100755 --- a/bootstrap +++ b/bootstrap @@ -57,6 +57,7 @@ gnu-make gnumakefile hash hash-pjw +human ignore-value lock maintainer-makefile diff --git a/cat/Makefile.am b/cat/Makefile.am index e822ac3..4dd7dfb 100644 --- a/cat/Makefile.am +++ b/cat/Makefile.am @@ -1,4 +1,4 @@ -# libguestfs virt-cat and virt-ls. +# libguestfs virt-cat, virt-filesystems and virt-ls. # Copyright (C) 2010 Red Hat Inc. # # This program is free software; you can redistribute it and/or modify @@ -21,11 +21,14 @@ EXTRA_DIST = \ run-cat-locally \ test-virt-cat.sh \ virt-cat.pod \ + run-filesystems-locally \ + test-virt-filesystems.sh \ + virt-filesystems.pod \ run-ls-locally \ test-virt-ls.sh \ virt-ls.pod -bin_PROGRAMS = virt-cat virt-ls +bin_PROGRAMS = virt-cat virt-filesystems virt-ls SHARED_SOURCE_FILES = \ ../fish/inspect.c \ @@ -49,6 +52,21 @@ virt_cat_LDADD = \ $(top_builddir)/src/libguestfs.la \ ../gnulib/lib/libgnu.la +virt_filesystems_SOURCES = \ + $(SHARED_SOURCE_FILES) \ + virt-filesystems.c + +virt_filesystems_CFLAGS = \ + -I$(top_srcdir)/src -I$(top_builddir)/src \ + -I$(top_srcdir)/fish \ + -I$(srcdir)/../gnulib/lib -I../gnulib/lib \ + -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) + +virt_filesystems_LDADD = \ + $(top_builddir)/src/libguestfs.la \ + ../gnulib/lib/libgnu.la + virt_ls_SOURCES = \ $(SHARED_SOURCE_FILES) \ virt-ls.c @@ -65,10 +83,11 @@ virt_ls_LDADD = \ ../gnulib/lib/libgnu.la # Manual pages and HTML files for the website. -man_MANS = virt-cat.1 virt-ls.1 +man_MANS = virt-cat.1 virt-filesystems.1 virt-ls.1 noinst_DATA = \ $(top_builddir)/html/virt-cat.1.html \ + $(top_builddir)/html/virt-filesystems.1.html \ $(top_builddir)/html/virt-ls.1.html virt-cat.1: virt-cat.pod @@ -86,6 +105,21 @@ $(top_builddir)/html/virt-cat.1.html: virt-cat.pod --outfile html/$@ \ $(abs_srcdir)/$< +virt-filesystems.1: virt-filesystems.pod + $(POD2MAN) \ + --section 1 \ + -c "Virtualization Support" \ + --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ + $< > $@-t && mv $@-t $@ + +$(top_builddir)/html/virt-filesystems.1.html: virt-filesystems.pod + mkdir -p $(top_builddir)/html + cd $(top_builddir) && pod2html \ + --css 'pod.css' \ + --htmldir html \ + --outfile html/$@ \ + $(abs_srcdir)/$< + virt-ls.1: virt-ls.pod $(POD2MAN) \ --section 1 \ @@ -110,7 +144,7 @@ TESTS_ENVIRONMENT = \ LD_LIBRARY_PATH=$(top_builddir)/src/.libs \ LIBGUESTFS_PATH=$(top_builddir)/appliance -TESTS = test-virt-cat.sh test-virt-ls.sh +TESTS = test-virt-cat.sh test-virt-filesystems.sh test-virt-ls.sh # Build a partly-static binary (for the binary distribution). @@ -118,6 +152,10 @@ virt-cat.static$(EXEEXT): $(virt_cat_OBJECTS) $(virt_cat_DEPENDENCIES) $(top_srcdir)/relink-static.sh \ $(virt_cat_LINK) $(virt_cat_OBJECTS) -static $(virt_cat_LDADD) $(virt_cat_LIBS) $(LIBVIRT_LIBS) $(LIBXML2_LIBS) -lpcre -lhivex -lmagic -lz -lm +virt-filesystems.static$(EXEEXT): $(virt_filesystems_OBJECTS) $(virt_filesystems_DEPENDENCIES) + $(top_srcdir)/relink-static.sh \ + $(virt_filesystems_LINK) $(virt_filesystems_OBJECTS) -static $(virt_filesystems_LDADD) $(virt_filesystems_LIBS) $(LIBVIRT_LIBS) $(LIBXML2_LIBS) -lpcre -lhivex -lmagic -lz -lm + virt-ls.static$(EXEEXT): $(virt_ls_OBJECTS) $(virt_ls_DEPENDENCIES) $(top_srcdir)/relink-static.sh \ $(virt_ls_LINK) $(virt_ls_OBJECTS) -static $(virt_ls_LDADD) $(virt_ls_LIBS) $(LIBVIRT_LIBS) $(LIBXML2_LIBS) -lpcre -lhivex -lmagic -lz -lm diff --git a/cat/run-filesystems-locally b/cat/run-filesystems-locally new file mode 100755 index 0000000..316cc6d --- /dev/null +++ b/cat/run-filesystems-locally @@ -0,0 +1,52 @@ +#!/usr/bin/perl +# 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. + +# This script sets up the environment so you can run virt-* tools in +# place without needing to do 'make install' first. You can also run +# the tools by creating a symlink to this script and putting it in +# your path. +# +# Use it like this: +# ./run-filesystems-locally [usual virt-filesystems args ...] + +use strict; +use warnings; + +use File::Basename qw(dirname); +use File::Spec; +use Cwd qw(abs_path); + +my $path = $0; + +# Follow symlinks until we get to the real file +while(-l $path) { + my $link = readlink($path) or die "readlink: $path: $!"; + if(File::Spec->file_name_is_absolute($link)) { + $path = $link; + } else { + $path = File::Spec->catfile(dirname($path), $link); + } +} + +# Get the absolute path of the parent directory +$path = abs_path(dirname($path).'/..'); + +$ENV{LD_LIBRARY_PATH} = $path.'/src/.libs'; +$ENV{LIBGUESTFS_PATH} = $path.'/appliance'; + +#print (join " ", ("$path/cat/virt-filesystems", @ARGV), "\n"); +exec("$path/cat/virt-filesystems", @ARGV); diff --git a/cat/test-virt-filesystems.sh b/cat/test-virt-filesystems.sh new file mode 100755 index 0000000..f3c325c --- /dev/null +++ b/cat/test-virt-filesystems.sh @@ -0,0 +1,33 @@ +#!/bin/bash - + +export LANG=C +set -e + +output="$(./virt-filesystems -a ../images/fedora.img | sort)" +expected="/dev/VG/LV1 +/dev/VG/LV2 +/dev/VG/LV3 +/dev/VG/Root +/dev/sda1" + +if [ "$output" != "$expected" ]; then + echo "$0: error: mismatch in test 1" + echo "$output" + exit 1 +fi + +output="$(./virt-filesystems -a ../images/fedora.img --all --long --uuid -h --no-title | awk '{print $1}' | sort -u)" +expected="/dev/VG +/dev/VG/LV1 +/dev/VG/LV2 +/dev/VG/LV3 +/dev/VG/Root +/dev/sda +/dev/sda1 +/dev/sda2" + +if [ "$output" != "$expected" ]; then + echo "$0: error: mismatch in test 2" + echo "$output" + exit 1 +fi diff --git a/cat/virt-filesystems.c b/cat/virt-filesystems.c new file mode 100644 index 0000000..580710d --- /dev/null +++ b/cat/virt-filesystems.c @@ -0,0 +1,870 @@ +/* virt-filesystems + * Copyright (C) 2010 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 + +#include +#include +#include +#include +#include +#include +#include + +#include "c-ctype.h" +#include "human.h" +#include "progname.h" + +#include "guestfs.h" +#include "options.h" + +/* These globals are shared with options.c. */ +guestfs_h *g; + +int read_only = 1; +int verbose = 0; +int keys_from_stdin = 0; +int echo_keys = 0; +const char *libvirt_uri = NULL; +int inspector = 0; + +static int csv = 0; /* --csv */ +static int human = 0; /* --human-readable|-h */ + +/* What is selected for output. */ +#define OUTPUT_FILESYSTEMS 1 +#define OUTPUT_FILESYSTEMS_EXTRA 2 +#define OUTPUT_PARTITIONS 4 +#define OUTPUT_BLOCKDEVS 8 +#define OUTPUT_LVS 16 +#define OUTPUT_VGS 32 +#define OUTPUT_PVS 64 +#define OUTPUT_ALL INT_MAX +static int output = 0; + +/* What columns to output. This is in display order. */ +#define COLUMN_NAME 1 /* always shown */ +#define COLUMN_TYPE 2 +#define COLUMN_VFS_TYPE 4 /* if --filesystems */ +#define COLUMN_VFS_LABEL 8 /* if --filesystems */ +#define COLUMN_SIZE 16 /* bytes, or human-readable if -h */ +#define COLUMN_PARENT_NAME 32 /* only for partitions, LVs */ +#define COLUMN_UUID 64 /* if --uuid */ +#define NR_COLUMNS 7 +static int columns; + +static char *canonical_device (const char *dev); +static void do_output_title (void); +static void do_output (void); +static void do_output_end (void); + +static inline char * +bad_cast (char const *s) +{ + return (char *) s; +} + +static void __attribute__((noreturn)) +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else { + fprintf (stdout, + _("%s: list filesystems, partitions, block devices, LVM in a VM\n" + "Copyright (C) 2010 Red Hat Inc.\n" + "Usage:\n" + " %s [--options] -d domname file\n" + " %s [--options] -a disk.img [-a disk.img ...] file\n" + "Options:\n" + " -a|--add image Add image\n" + " --all Display everything\n" + " --blkdevs|--block-devices\n" + " Display block devices\n" + " -c|--connect uri Specify libvirt URI for -d option\n" + " --csv Output as Comma-Separated Values\n" + " -d|--domain guest Add disks from libvirt guest\n" + " --echo-keys Don't turn off echo for passphrases\n" + " --extra Display swap and data filesystems\n" + " --filesystems Display mountable filesystems\n" + " --format[=raw|..] Force disk format for -a option\n" + " -h|--human-readable Human-readable sizes in --long output\n" + " --help Display brief help\n" + " --keys-from-stdin Read passphrases from stdin\n" + " -l|--long Long output\n" + " --lvs|--logvols|--logical-volumes\n" + " Display LVM logical volumes\n" + " --no-title No title in --long output\n" + " --parts|--partitions Display partitions\n" + " --pvs|--physvols|--physical-volumes\n" + " Display LVM physical volumes\n" + " --uuid|--uuids Add UUIDs to --long output\n" + " -v|--verbose Verbose messages\n" + " -V|--version Display version and exit\n" + " --vgs|--volgroups|--volume-groups\n" + " Display LVM volume groups\n" + " -x Trace libguestfs API calls\n" + "For more information, see the manpage %s(1).\n"), + program_name, program_name, program_name, + program_name); + } + exit (status); +} + +int +main (int argc, char *argv[]) +{ + /* Set global program name that is not polluted with libtool artifacts. */ + set_program_name (argv[0]); + + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEBASEDIR); + textdomain (PACKAGE); + + enum { HELP_OPTION = CHAR_MAX + 1 }; + + static const char *options = "a:c:d:hlvVx"; + static const struct option long_options[] = { + { "add", 1, 0, 'a' }, + { "all", 0, 0, 0 }, + { "blkdevs", 0, 0, 0 }, + { "block-devices", 0, 0, 0 }, + { "connect", 1, 0, 'c' }, + { "csv", 0, 0, 0 }, + { "domain", 1, 0, 'd' }, + { "echo-keys", 0, 0, 0 }, + { "extra", 0, 0, 0 }, + { "filesystems", 0, 0, 0 }, + { "format", 2, 0, 0 }, + { "help", 0, 0, HELP_OPTION }, + { "human-readable", 0, 0, 'h' }, + { "keys-from-stdin", 0, 0, 0 }, + { "long", 0, 0, 'l' }, + { "logical-volumes", 0, 0, 0 }, + { "logvols", 0, 0, 0 }, + { "lvs", 0, 0, 0 }, + { "no-title", 0, 0, 0 }, + { "parts", 0, 0, 0 }, + { "partitions", 0, 0, 0 }, + { "physical-volumes", 0, 0, 0 }, + { "physvols", 0, 0, 0 }, + { "pvs", 0, 0, 0 }, + { "uuid", 0, 0, 0 }, + { "uuids", 0, 0, 0 }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "vgs", 0, 0, 0 }, + { "volgroups", 0, 0, 0 }, + { "volume-groups", 0, 0, 0 }, + { 0, 0, 0, 0 } + }; + struct drv *drvs = NULL; + struct drv *drv; + const char *format = NULL; + int c; + int option_index; + int no_title = 0; /* --no-title */ + int long_mode = 0; /* --long|-l */ + int uuid = 0; /* --uuid */ + int title; + + g = guestfs_create (); + if (g == NULL) { + fprintf (stderr, _("guestfs_create: failed to create handle\n")); + exit (EXIT_FAILURE); + } + + argv[0] = bad_cast (program_name); + + for (;;) { + c = getopt_long (argc, argv, options, long_options, &option_index); + if (c == -1) break; + + switch (c) { + case 0: /* options which are long only */ + if (STREQ (long_options[option_index].name, "keys-from-stdin")) { + keys_from_stdin = 1; + } else if (STREQ (long_options[option_index].name, "echo-keys")) { + echo_keys = 1; + } else if (STREQ (long_options[option_index].name, "format")) { + if (!optarg || STREQ (optarg, "")) + format = NULL; + else + format = optarg; + } else if (STREQ (long_options[option_index].name, "all")) { + output = OUTPUT_ALL; + } else if (STREQ (long_options[option_index].name, "blkdevs") || + STREQ (long_options[option_index].name, "block-devices")) { + output |= OUTPUT_BLOCKDEVS; + } else if (STREQ (long_options[option_index].name, "csv")) { + csv = 1; + } else if (STREQ (long_options[option_index].name, "extra")) { + output |= OUTPUT_FILESYSTEMS; + output |= OUTPUT_FILESYSTEMS_EXTRA; + } else if (STREQ (long_options[option_index].name, "filesystems")) { + output |= OUTPUT_FILESYSTEMS; + } else if (STREQ (long_options[option_index].name, "logical-volumes") || + STREQ (long_options[option_index].name, "logvols") || + STREQ (long_options[option_index].name, "lvs")) { + output |= OUTPUT_LVS; + } else if (STREQ (long_options[option_index].name, "no-title")) { + no_title = 1; + } else if (STREQ (long_options[option_index].name, "parts") || + STREQ (long_options[option_index].name, "partitions")) { + output |= OUTPUT_PARTITIONS; + } else if (STREQ (long_options[option_index].name, "physical-volumes") || + STREQ (long_options[option_index].name, "physvols") || + STREQ (long_options[option_index].name, "pvs")) { + output |= OUTPUT_PVS; + } else if (STREQ (long_options[option_index].name, "uuid") || + STREQ (long_options[option_index].name, "uuids")) { + uuid = 1; + } else if (STREQ (long_options[option_index].name, "vgs") || + STREQ (long_options[option_index].name, "volgroups") || + STREQ (long_options[option_index].name, "volume-groups")) { + output |= OUTPUT_VGS; + } else { + fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), + program_name, long_options[option_index].name, option_index); + exit (EXIT_FAILURE); + } + break; + + case 'a': + OPTION_a; + break; + + case 'c': + OPTION_c; + break; + + case 'd': + OPTION_d; + break; + + case 'h': + human = 1; + break; + + case 'l': + long_mode = 1; + break; + + case 'v': + OPTION_v; + break; + + case 'V': + OPTION_V; + break; + + case 'x': + OPTION_x; + break; + + case HELP_OPTION: + usage (EXIT_SUCCESS); + + default: + usage (EXIT_FAILURE); + } + } + + /* These are really constants, but they have to be variables for the + * options parsing code. Assert here that they have known-good + * values. + */ + assert (read_only == 1); + assert (inspector == 0); + + /* Must be no extra arguments on the command line. */ + if (optind != argc) + usage (EXIT_FAILURE); + + /* -h and --csv doesn't make sense. Spreadsheets will corrupt these + * fields. (RHBZ#600977). + */ + if (human && csv) { + fprintf (stderr, _("%s: you cannot use -h and --csv options together.\n"), + program_name); + exit (EXIT_FAILURE); + } + + /* Nothing selected for output, means --filesystems is implied. */ + if (output == 0) + output = OUTPUT_FILESYSTEMS; + + /* What columns will be displayed? */ + columns = COLUMN_NAME; + if (long_mode) { + columns |= COLUMN_TYPE; + columns |= COLUMN_SIZE; + if ((output & OUTPUT_FILESYSTEMS)) { + columns |= COLUMN_VFS_TYPE; + columns |= COLUMN_VFS_LABEL; + } + if ((output & (OUTPUT_PARTITIONS|OUTPUT_LVS))) + columns |= COLUMN_PARENT_NAME; + if (uuid) + columns |= COLUMN_UUID; + } + + /* Display title by default only in long mode. */ + title = long_mode; + if (no_title) + title = 0; + + /* User must have specified some drives. */ + if (drvs == NULL) + usage (EXIT_FAILURE); + + /* Add drives. */ + add_drives (drvs, 'a'); + + if (guestfs_launch (g) == -1) + exit (EXIT_FAILURE); + + /* Free up data structures, no longer needed after this point. */ + free_drives (drvs); + + if (title) + do_output_title (); + do_output (); + do_output_end (); + + guestfs_close (g); + + exit (EXIT_SUCCESS); +} + +static void do_output_filesystems (void); +static void do_output_lvs (void); +static void do_output_vgs (void); +static void do_output_pvs (void); +static void do_output_partitions (void); +static void do_output_blockdevs (void); +static void write_row (const char *name, const char *type, const char *vfs_type, const char *vfs_label, int64_t size, const char *parent_name, const char *uuid); +static void write_row_strings (char **strings, size_t len); + +static void +do_output_title (void) +{ + const char *headings[NR_COLUMNS]; + size_t len = 0; + + /* NB. These strings are not localized and must not contain spaces. */ + if ((columns & COLUMN_NAME)) + headings[len++] = "Name"; + if ((columns & COLUMN_TYPE)) + headings[len++] = "Type"; + if ((columns & COLUMN_VFS_TYPE)) + headings[len++] = "VFS"; + if ((columns & COLUMN_VFS_LABEL)) + headings[len++] = "Label"; + if ((columns & COLUMN_SIZE)) + headings[len++] = "Size"; + if ((columns & COLUMN_PARENT_NAME)) + headings[len++] = "Parent"; + if ((columns & COLUMN_UUID)) + headings[len++] = "UUID"; + assert (len <= NR_COLUMNS); + + write_row_strings ((char **) headings, len); +} + +static void +do_output (void) +{ + /* The ordering here is trying to be most specific -> least specific, + * although that is not required or guaranteed. + */ + if ((output & OUTPUT_FILESYSTEMS)) + do_output_filesystems (); + + if ((output & OUTPUT_LVS)) + do_output_lvs (); + + if ((output & OUTPUT_VGS)) + do_output_vgs (); + + if ((output & OUTPUT_PVS)) + do_output_pvs (); + + if ((output & OUTPUT_PARTITIONS)) + do_output_partitions (); + + if ((output & OUTPUT_BLOCKDEVS)) + do_output_blockdevs (); +} + +static void +do_output_filesystems (void) +{ + char **fses; + size_t i; + + fses = guestfs_list_filesystems (g); + if (fses == NULL) + exit (EXIT_FAILURE); + + for (i = 0; fses[i] != NULL; i += 2) { + char *dev, *vfs_label = NULL, *vfs_uuid = NULL; + int64_t size = -1; + + /* Skip swap and unknown, unless --extra flag was given. */ + if (!(output & OUTPUT_FILESYSTEMS_EXTRA) && + (STREQ (fses[i+1], "swap") || STREQ (fses[i+1], "unknown"))) + continue; + + dev = canonical_device (fses[i]); + + /* Only bother to look these up if we will be displaying them, + * otherwise pass them as NULL. + */ + if ((columns & COLUMN_VFS_LABEL)) { + vfs_label = guestfs_vfs_label (g, fses[i]); + if (vfs_label == NULL) + exit (EXIT_FAILURE); + } + if ((columns & COLUMN_UUID)) { + vfs_uuid = guestfs_vfs_uuid (g, fses[i]); + if (vfs_uuid == NULL) + exit (EXIT_FAILURE); + } + if ((columns & COLUMN_SIZE)) { + size = guestfs_blockdev_getsize64 (g, fses[i]); + if (size == -1) + exit (EXIT_FAILURE); + } + + write_row (dev, "filesystem", + fses[i+1], vfs_label, size, NULL, vfs_uuid); + + free (dev); + free (vfs_label); + free (vfs_uuid); + free (fses[i]); + free (fses[i+1]); + } + + free (fses); +} + +static void +do_output_lvs (void) +{ + char **lvs; + size_t i; + + lvs = guestfs_lvs (g); + if (lvs == NULL) + exit (EXIT_FAILURE); + + for (i = 0; lvs[i] != NULL; ++i) { + char *uuid = NULL, *parent_name = NULL; + int64_t size = -1; + + if ((columns & COLUMN_SIZE)) { + size = guestfs_blockdev_getsize64 (g, lvs[i]); + if (size == -1) + exit (EXIT_FAILURE); + } + if ((columns & COLUMN_UUID)) { + uuid = guestfs_lvuuid (g, lvs[i]); + if (uuid == NULL) + exit (EXIT_FAILURE); + } + if ((columns & COLUMN_PARENT_NAME)) { + parent_name = strdup (lvs[i]); + if (parent_name == NULL) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + char *p = strrchr (parent_name, '/'); + if (p) + *p = '\0'; + } + + write_row (lvs[i], "lv", + NULL, NULL, size, parent_name, uuid); + + free (uuid); + free (parent_name); + free (lvs[i]); + } + + free (lvs); +} + +static void +do_output_vgs (void) +{ + struct guestfs_lvm_vg_list *vgs; + size_t i; + + vgs = guestfs_vgs_full (g); + if (vgs == NULL) + exit (EXIT_FAILURE); + + for (i = 0; i < vgs->len; ++i) { + char name[PATH_MAX]; + char uuid[33]; + + strcpy (name, "/dev/"); + strcpy (&name[5], vgs->val[i].vg_name); + memcpy (uuid, vgs->val[i].vg_uuid, 32); + uuid[32] = '\0'; + write_row (name, "vg", + NULL, NULL, (int64_t) vgs->val[i].vg_size, NULL, uuid); + + } + + guestfs_free_lvm_vg_list (vgs); +} + +static void +do_output_pvs (void) +{ + struct guestfs_lvm_pv_list *pvs; + size_t i; + + pvs = guestfs_pvs_full (g); + if (pvs == NULL) + exit (EXIT_FAILURE); + + for (i = 0; i < pvs->len; ++i) { + char *dev; + char uuid[33]; + + dev = canonical_device (pvs->val[i].pv_name); + + memcpy (uuid, pvs->val[i].pv_uuid, 32); + uuid[32] = '\0'; + write_row (dev, "pv", + NULL, NULL, (int64_t) pvs->val[i].pv_size, NULL, uuid); + + free (dev); + } + + guestfs_free_lvm_pv_list (pvs); +} + +static void +do_output_partitions (void) +{ + char **parts; + size_t i; + + parts = guestfs_list_partitions (g); + if (parts == NULL) + exit (EXIT_FAILURE); + + for (i = 0; parts[i] != NULL; ++i) { + char *dev, *parent_name = NULL; + int64_t size = -1; + + dev = canonical_device (parts[i]); + + if ((columns & COLUMN_SIZE)) { + size = guestfs_blockdev_getsize64 (g, parts[i]); + if (size == -1) + exit (EXIT_FAILURE); + } + if ((columns & COLUMN_PARENT_NAME)) { + parent_name = guestfs_part_to_dev (g, parts[i]); + if (parent_name == NULL) + exit (EXIT_FAILURE); + char *p = canonical_device (parent_name); + free (parent_name); + parent_name = p; + } + + write_row (dev, "partition", + NULL, NULL, size, parent_name, NULL); + + free (dev); + free (parent_name); + free (parts[i]); + } + + free (parts); +} + +static void +do_output_blockdevs (void) +{ + char **devices; + size_t i; + + devices = guestfs_list_devices (g); + if (devices == NULL) + exit (EXIT_FAILURE); + + for (i = 0; devices[i] != NULL; ++i) { + int64_t size = -1; + char *dev; + + dev = canonical_device (devices[i]); + + if ((columns & COLUMN_SIZE)) { + size = guestfs_blockdev_getsize64 (g, devices[i]); + if (size == -1) + exit (EXIT_FAILURE); + } + + write_row (dev, "device", + NULL, NULL, size, NULL, NULL); + + free (dev); + free (devices[i]); + } + + free (devices); +} + +/* /dev/vda1 -> /dev/sda. Returns a string which the caller must free. */ +static char * +canonical_device (const char *dev) +{ + char *ret = strdup (dev); + if (ret == NULL) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + + if (STRPREFIX (ret, "/dev/") && + (ret[5] == 'h' || ret[5] == 'v') && + ret[6] == 'd' && + c_isalpha (ret[7]) && + (c_isdigit (ret[8]) || ret[8] == '\0')) + ret[5] = 's'; + + return ret; +} + +static void +write_row (const char *name, const char *type, + const char *vfs_type, const char *vfs_label, + int64_t size, const char *parent_name, const char *uuid) +{ + const char *strings[NR_COLUMNS]; + size_t len = 0; + char hum[LONGEST_HUMAN_READABLE]; + char num[256]; + + if ((columns & COLUMN_NAME)) + strings[len++] = name; + if ((columns & COLUMN_TYPE)) + strings[len++] = type; + if ((columns & COLUMN_VFS_TYPE)) + strings[len++] = vfs_type; + if ((columns & COLUMN_VFS_LABEL)) + strings[len++] = vfs_label; + if ((columns & COLUMN_SIZE)) { + if (size >= 0) { + if (human) { + strings[len++] = + human_readable ((uintmax_t) size, hum, + human_round_to_nearest|human_autoscale| + human_base_1024|human_SI, + 1, 1); + } + else { + snprintf (num, sizeof num, "%" PRIi64, size); + strings[len++] = num; + } + } + else + strings[len++] = NULL; + } + if ((columns & COLUMN_PARENT_NAME)) + strings[len++] = parent_name; + if ((columns & COLUMN_UUID)) + strings[len++] = uuid; + assert (len <= NR_COLUMNS); + + write_row_strings ((char **) strings, len); +} + +static void add_row (char **strings, size_t len); +static void write_csv_field (const char *field); + +static void +write_row_strings (char **strings, size_t len) +{ + if (!csv) { + /* Text mode. Because we want the columns to line up, we can't + * output directly, but instead need to save up the rows and + * output them at the end. + */ + add_row (strings, len); + } + else { /* CSV mode: output it directly, quoted */ + size_t i; + + for (i = 0; i < len; ++i) { + if (i > 0) + putchar (','); + if (strings[i] != NULL) + write_csv_field (strings[i]); + } + putchar ('\n'); + } +} + +/* Function to quote CSV fields on output without requiring an + * external module. + */ +static void +write_csv_field (const char *field) +{ + size_t i, len; + int needs_quoting = 0; + + len = strlen (field); + + for (i = 0; i < len; ++i) { + if (field[i] == ' ' || field[i] == '"' || + field[i] == '\n' || field[i] == ',') { + needs_quoting = 1; + break; + } + } + + if (!needs_quoting) { + printf ("%s", field); + return; + } + + /* Quoting for CSV fields. */ + putchar ('"'); + for (i = 0; i < len; ++i) { + if (field[i] == '"') { + putchar ('"'); + putchar ('"'); + } else + putchar (field[i]); + } + putchar ('"'); +} + +/* This code is only used in text mode (non-CSV output). */ +static char ***rows = NULL; +static size_t nr_rows = 0; +static size_t max_width[NR_COLUMNS]; + +static void +add_row (char **strings, size_t len) +{ + size_t i, slen; + char **row; + + assert (len <= NR_COLUMNS); + + row = malloc (sizeof (char *) * len); + if (row == NULL) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + + for (i = 0; i < len; ++i) { + if (strings[i]) { + row[i] = strdup (strings[i]); + if (row[i] == NULL) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + + /* Keep a running total of the max width of each column. */ + slen = strlen (strings[i]); + if (slen == 0) + slen = 1; /* because "" is printed as "-" */ + if (slen > max_width[i]) + max_width[i] = slen; + } + else + row[i] = NULL; + } + + rows = realloc (rows, sizeof (char **) * (nr_rows + 1)); + if (rows == NULL) { + perror ("realloc"); + exit (EXIT_FAILURE); + } + rows[nr_rows] = row; + nr_rows++; +} + +/* In text mode we saved up all the output so that we can print the + * columns aligned. + */ +static void +do_output_end (void) +{ + size_t i, j, k, len, space_btwn; + + if (csv) + return; + + /* How much space between columns? Try 2 spaces between columns, but + * if that just pushes us over 72 columns, use 1 space. + */ + space_btwn = 2; + i = 0; + for (j = 0; j < NR_COLUMNS; ++j) + i += max_width[j] + space_btwn; + if (i > 72) + space_btwn = 1; + + for (i = 0; i < nr_rows; ++i) { + char **row = rows[i]; + + k = 0; + + for (j = 0; j < NR_COLUMNS; ++j) { + /* Ignore columns which are completely empty. This also deals + * with the fact that we didn't remember the length of each row + * in add_row above. + */ + if (max_width[j] == 0) + continue; + + while (k) { + putchar (' '); + k--; + } + + if (row[j] == NULL || STREQ (row[j], "")) { + printf ("-"); + len = 1; + } else { + printf ("%s", row[j]); + len = strlen (row[j]); + } + free (row[j]); + + assert (len <= max_width[j]); + k = max_width[j] - len + space_btwn; + } + + putchar ('\n'); + free (row); + } + free (rows); +} diff --git a/cat/virt-filesystems.pod b/cat/virt-filesystems.pod new file mode 100755 index 0000000..12f0b63 --- /dev/null +++ b/cat/virt-filesystems.pod @@ -0,0 +1,349 @@ +=encoding utf8 + +=head1 NAME + +virt-filesystems - List filesystems, partitions, block devices, LVM in a virtual machine or disk image + +=head1 SYNOPSIS + + virt-filesystems [--options] -d domname + + virt-filesystems [--options] -a disk.img [-a disk.img ...] + +=head1 DESCRIPTION + +This tool allows you to discover filesystems, partitions, logical +volumes, and their sizes in a disk image or virtual machine. It is a +replacement for L and +L. + +One use for this tool is from shell scripts to iterate over all +filesystems from a disk image: + + for fs in $(virt-filesystems -a disk.img); do + # ... + done + +Another use is to list partitions before using another tool to modify +those partitions (such as L). If you are curious +about what an unknown disk image contains, use this tool along with +L. + +Various command line options control what this program displays. You +need to give either I<-a> or I<-d> options to specify the disk image +or libvirt guest respectively. If you just specify that then the +program shows filesystems found, one per line, like this: + + $ virt-filesystems -a disk.img + /dev/sda1 + /dev/vg_guest/lv_root + +If you add I<-l> or I<--long> then the output includes extra +information: + + $ virt-filesystems -a disk.img -l + Name Type VFS Label Size + /dev/sda1 filesystem ext4 boot 524288000 + /dev/vg_guest/lv_root filesystem ext4 root 10212081664 + +If you add I<--extra> then non-mountable (swap, unknown) filesystems +are shown as well: + + $ virt-filesystems -a disk.img --extra + /dev/sda1 + /dev/vg_guest/lv_root + /dev/vg_guest/lv_swap + /dev/vg_guest/lv_data + +If you add I<--partitions> then partitions are shown instead of filesystems: + + $ virt-filesystems -a disk.img --partitions + /dev/sda1 + /dev/sda2 + +Similarly you can use I<--logical-volumes>, I<--volume-groups>, +I<--physical-volumes>, I<--block-devices> to list those items. + +You can use these options in combination as well (if you want a +combination including filesystems, you have to add I<--filesystems>). +Notice that some items fall into several categories (eg. C +might be both a partition and a filesystem). These items are listed +several times. To get a list which includes absolutely everything +that virt-filesystems knows about, use the I<--all> option. + +UUIDs (because they are quite long) are not shown by default. Add the +I<--uuid> option to display device and filesystem UUIDs in the long +output. + +I<--all --long --uuid> is a useful combination to display all possible +information about everything. + + $ virt-filesystems -a win.img --all --long --uuid -h + Name Type VFS Label Size Parent UUID + /dev/sda1 filesystem ntfs System Reserved 100M - F81C92571C92112C + /dev/sda2 filesystem ntfs - 20G - F2E8996AE8992E3B + /dev/sda1 partition - - 100M /dev/sda - + /dev/sda2 partition - - 20G /dev/sda - + /dev/sda device - - 20G - - + +For machine-readable output, use I<--csv> to get Comma-Separated Values. + +=head1 OPTIONS + +=over 4 + +=item B<--help> + +Display brief help. + +=item B<-a> file + +=item B<--add> file + +Add I which should be a disk image from a virtual machine. If +the virtual machine has multiple block devices, you must supply all of +them with separate I<-a> options. + +The format of the disk image is auto-detected. To override this and +force a particular format use the I<--format=..> option. + +=item B<--all> + +Display everything. This is currently the same as specifying these +options: I<--filesystems>, I<--extra>, I<--partitions>, +I<--block-devices>, I<--logical-volumes>, I<--volume-groups>, +I<--physical-volumes>. (More may be added to this list in future). + +See also I<--long>. + +=item B<--blkdevs> + +=item B<--block-devices> + +Display block devices. + +=item B<-c> URI + +=item B<--connect> URI + +If using libvirt, connect to the given I. If omitted, then we +connect to the default libvirt hypervisor. + +If you specify guest block devices directly (I<-a>), then libvirt is +not used at all. + +=item B<--csv> + +Write out the results in CSV format (comma-separated values). This +format can be imported easily into databases and spreadsheets, but +read L below. + +=item B<-d> guest + +=item B<--domain> guest + +Add all the disks from the named libvirt guest. + +=item B<--echo-keys> + +When prompting for keys and passphrases, virt-filesystems normally +turns echoing off so you cannot see what you are typing. If you are +not worried about Tempest attacks and there is no one else in the room +you can specify this flag to see what you are typing. + +=item B<--extra> + +This causes filesystems that are not normally, mountable filesystems +to be displayed. This category includes swapspace, and filesystems +that are empty or contain unknown data. + +This option implies I<--filesystems>. + +=item B<--filesystems> + +Display mountable filesystems. If no display option was selected then +this option is implied. + +With I<--extra>, non-mountable filesystems are shown too. + +=item B<--format=raw|qcow2|..> + +=item B<--format> + +The default for the I<-a> option is to auto-detect the format of the +disk image. Using this forces the disk format for I<-a> options which +follow on the command line. Using I<--format> with no argument +switches back to auto-detection for subsequent I<-a> options. + +For example: + + virt-filesystems --format=raw -a disk.img + +forces raw format (no auto-detection) for C. + + virt-filesystems --format=raw -a disk.img --format -a another.img + +forces raw format (no auto-detection) for C and reverts to +auto-detection for C. + +If you have untrusted raw-format guest disk images, you should use +this option to specify the disk format. This avoids a possible +security problem with malicious guests (CVE-2010-3851). See also +L. + +=item B<-h> + +=item B<--human-readable> + +In I<--long> mode, display sizes in human-readable format. + +=item B<--keys-from-stdin> + +Read key or passphrase parameters from stdin. The default is +to try to read passphrases from the user by opening C. + +=item B<-l> + +=item B<--long> + +Display extra columns of data ("long format"). + +A title row is added unless you also specify I<--no-title>. + +The extra columns displayed depend on what output you select, and the +ordering of columns may change in future versions. Use the title row, +I<--csv> output and/or L to match columns to data in +external programs. + +Use I<-h> if you want sizes to be displayed in human-readable format. +The default is to show raw numbers of I. + +Use I<--uuid> to display UUIDs too. + +=item B<--lvs> + +=item B<--logvols> + +=item B<--logical-volumes> + +Display LVM logical volumes. In this mode, these are displayed +irrespective of whether the LVs contain filesystems. + +=item B<--no-title> + +In I<--long> mode, don't add a title row. + +Note that the order of the columns is not fixed, and may change in +future versions of virt-filesystems, so using this option may give you +unexpected surprises. + +=item B<--parts> + +=item B<--partitions> + +Display partitions. In this mode, these are displayed +irrespective of whether the partitions contain filesystems. + +=item B<--pvs> + +=item B<--physvols> + +=item B<--physical-volumes> + +Display LVM physical volumes. + +=item B<--uuid> + +=item B<--uuids> + +In I<--long> mode, display UUIDs as well. + +=item B<-v> + +=item B<--verbose> + +Enable verbose messages for debugging. + +=item B<-V> + +=item B<--version> + +Display version number and exit. + +=item B<--vgs> + +=item B<--volgroups> + +=item B<--volume-groups> + +Display LVM volume groups. + +=item B<-x> + +Enable tracing of libguestfs API calls. + +=back + +=head1 NOTE ABOUT CSV FORMAT + +Comma-separated values (CSV) is a deceptive format. It I like +it should be easy to parse, but it is definitely not easy to parse. + +Myth: Just split fields at commas. Reality: This does I work +reliably. This example has two columns: + + "foo,bar",baz + +Myth: Read the file one line at a time. Reality: This does I +work reliably. This example has one row: + + "foo + bar",baz + +For shell scripts, use C (L +also packaged in major Linux distributions). + +For other languages, use a CSV processing library (eg. C +for Perl or Python's built-in csv library). + +Most spreadsheets and databases can import CSV directly. + +=head1 SHELL QUOTING + +Libvirt guest names can contain arbitrary characters, some of which +have meaning to the shell such as C<#> and space. You may need to +quote or escape these characters on the command line. See the shell +manual page L for details. + +=head1 SEE ALSO + +L, +L, +L, +L, +L, +L, +L, +L. + +=head1 AUTHOR + +Richard W.M. Jones L + +=head1 COPYRIGHT + +Copyright (C) 2010 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. diff --git a/fish/guestfish.pod b/fish/guestfish.pod index c52b773..07b9899 100644 --- a/fish/guestfish.pod +++ b/fish/guestfish.pod @@ -275,10 +275,11 @@ You have to mount something on C before most commands will work. If any I<-m> or I<--mount> options are given, the guest is automatically launched. -If you don't know what filesystems a disk image contains, you -can either run guestfish without this option, then list the partitions -and LVs available (see L and L commands), -or you can use the L program. +If you don't know what filesystems a disk image contains, you can +either run guestfish without this option, then list the partitions, +filesystems and LVs available (see L, +L and L commands), or you can use the +L program. =item B<-n> | B<--no-sync> @@ -1042,6 +1043,7 @@ L, L, L, L, +L, L, L, L, diff --git a/fuse/guestmount.pod b/fuse/guestmount.pod index e86d76c..dc4005b 100644 --- a/fuse/guestmount.pod +++ b/fuse/guestmount.pod @@ -57,9 +57,9 @@ For a libvirt guest called "Guest" you could do: guestmount -d Guest -i --ro /mnt If you don't know what filesystems are contained in a guest or -disk image, use L first: +disk image, use L first: - virt-list-filesystems MyGuest + virt-filesystems MyGuest If you want to trace the libguestfs calls but without excessive debugging information, we recommend: diff --git a/m4/.gitignore b/m4/.gitignore index 369a950..2ab011d 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -173,3 +173,8 @@ xsize.m4 /wchar_h.m4 /wctype_h.m4 /asm-underscore.m4 +/argmatch.m4 +/human.m4 +/quote.m4 +/strtoimax.m4 +/strtoumax.m4 diff --git a/po/POTFILES.in b/po/POTFILES.in index 07dc838..61bf435 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,4 +1,5 @@ cat/virt-cat.c +cat/virt-filesystems.c cat/virt-ls.c daemon/augeas.c daemon/available.c diff --git a/src/guestfs.pod b/src/guestfs.pod index fc58f4f..7cf4e0b 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -2088,6 +2088,7 @@ L, L, L, L, +L, L, L, L, diff --git a/tools/virt-list-filesystems b/tools/virt-list-filesystems index 945a861..6b818b0 100755 --- a/tools/virt-list-filesystems +++ b/tools/virt-list-filesystems @@ -39,6 +39,9 @@ virt-list-filesystems - List filesystems in a virtual machine or disk image =head1 DESCRIPTION +This tool is obsolete. Use L as a more +flexible replacement. + C is a command line tool to list the filesystems that are contained in a virtual machine or disk image. @@ -189,6 +192,7 @@ L, L, L, L, +L, L, L, L, diff --git a/tools/virt-list-partitions b/tools/virt-list-partitions index 53059b4..0baa292 100755 --- a/tools/virt-list-partitions +++ b/tools/virt-list-partitions @@ -39,6 +39,9 @@ virt-list-partitions - List partitions in a virtual machine or disk image =head1 DESCRIPTION +This tool is obsolete. Use L as a more +flexible replacement. + C is a command line tool to list the partitions that are contained in a virtual machine or disk image. It is mainly useful as a first step to using @@ -255,6 +258,7 @@ manual page L for details. L, L, +L, L, L, L, diff --git a/tools/virt-resize b/tools/virt-resize index 2d8e0f1..1e8a6c7 100755 --- a/tools/virt-resize +++ b/tools/virt-resize @@ -58,10 +58,8 @@ B be used on live virtual machines - for consistent results, shut the virtual machine down before resizing it. If you are not familiar with the associated tools: -L, -L and -L, -we recommend you go and read those manual pages first. +L and L, we recommend you go and read +those manual pages first. =head1 EXAMPLES @@ -69,7 +67,7 @@ Copy C to C, extending one of the guest's partitions to fill the extra 5GB of space. truncate -r olddisk newdisk; truncate -s +5G newdisk - virt-list-partitions -lht olddisk + virt-filesystems --long --h --all -a olddisk # Note "/dev/sda2" is a partition inside the "olddisk" file. virt-resize --expand /dev/sda2 olddisk newdisk @@ -104,13 +102,14 @@ can use C like this to find the disk image name: =item 3. Look at current sizing -Use L to display the current partitions and +Use L to display the current partitions and sizes: - # virt-list-partitions -lht /dev/vg/lv_guest - /dev/sda1 ext3 101.9M - /dev/sda2 pv 7.9G - /dev/sda device 8.0G + # virt-filesystems --long --parts --blkdevs -h -a /dev/vg/lv_guest + Name Type Size Parent + /dev/sda1 partition 101M /dev/sda + /dev/sda2 partition 7.9G /dev/sda + /dev/sda device 8.0G - (This example is a virtual machine with an 8 GB disk which we would like to expand up to 10 GB). @@ -443,7 +442,7 @@ The contents of the LV are also resized if virt-resize knows how to do that. You can stop virt-resize from trying to expand the content by using the option C<--no-expand-content>. -Use L to list the filesystems in +Use L to list the filesystems in the guest. You can give this option multiple times, I it doesn't @@ -1485,8 +1484,7 @@ manual page L for details. =head1 SEE ALSO -L, -L, +L, L, L, L,