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.
This commit is contained in:
Richard W.M. Jones
2010-11-22 17:33:35 +00:00
parent f6d3d56771
commit fbc2555903
17 changed files with 1390 additions and 25 deletions

4
.gitignore vendored
View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -57,6 +57,7 @@ gnu-make
gnumakefile
hash
hash-pjw
human
ignore-value
lock
maintainer-makefile

View File

@@ -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

52
cat/run-filesystems-locally Executable file
View File

@@ -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);

33
cat/test-virt-filesystems.sh Executable file
View File

@@ -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

870
cat/virt-filesystems.c Normal file
View File

@@ -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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <unistd.h>
#include <getopt.h>
#include <assert.h>
#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);
}

349
cat/virt-filesystems.pod Executable file
View File

@@ -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<virt-list-filesystems(1)> and
L<virt-list-partitions(1)>.
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<virt-resize(1)>). If you are curious
about what an unknown disk image contains, use this tool along with
L<virt-inspector(1)>.
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</dev/sda1>
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<file> 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<URI>. 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</NOTE ABOUT CSV FORMAT> 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<disk.img>.
virt-filesystems --format=raw -a disk.img --format -a another.img
forces raw format (no auto-detection) for C<disk.img> and reverts to
auto-detection for C<another.img>.
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</add-drive-opts>.
=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</dev/tty>.
=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<csvtool(1)> 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<bytes>.
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<seems> 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<not> work
reliably. This example has two columns:
"foo,bar",baz
Myth: Read the file one line at a time. Reality: This does I<not>
work reliably. This example has one row:
"foo
bar",baz
For shell scripts, use C<csvtool> (L<http://merjis.com/developers/csv>
also packaged in major Linux distributions).
For other languages, use a CSV processing library (eg. C<Text::CSV>
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<sh(1)> for details.
=head1 SEE ALSO
L<guestfs(3)>,
L<guestfish(1)>,
L<virt-cat(1)>,
L<virt-df(1)>,
L<virt-list-filesystems(1)>,
L<virt-list-partitions(1)>,
L<csvtool(1)>,
L<http://libguestfs.org/>.
=head1 AUTHOR
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
=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.

View File

@@ -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</list-partitions> and L</lvs> commands),
or you can use the L<virt-list-filesystems(1)> 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</list-partitions>,
L</list-filesystems> and L</lvs> commands), or you can use the
L<virt-filesystems(1)> program.
=item B<-n> | B<--no-sync>
@@ -1042,6 +1043,7 @@ L<http://libguestfs.org/>,
L<virt-cat(1)>,
L<virt-df(1)>,
L<virt-edit(1)>,
L<virt-filesystems(1)>,
L<virt-list-filesystems(1)>,
L<virt-list-partitions(1)>,
L<virt-ls(1)>,

View File

@@ -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<virt-list-filesystems(1)> first:
disk image, use L<virt-filesystems(1)> first:
virt-list-filesystems MyGuest
virt-filesystems MyGuest
If you want to trace the libguestfs calls but without excessive
debugging information, we recommend:

5
m4/.gitignore vendored
View File

@@ -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

View File

@@ -1,4 +1,5 @@
cat/virt-cat.c
cat/virt-filesystems.c
cat/virt-ls.c
daemon/augeas.c
daemon/available.c

View File

@@ -2088,6 +2088,7 @@ L<guestmount(1)>,
L<virt-cat(1)>,
L<virt-df(1)>,
L<virt-edit(1)>,
L<virt-filesystems(1)>,
L<virt-inspector(1)>,
L<virt-list-filesystems(1)>,
L<virt-list-partitions(1)>,

View File

@@ -39,6 +39,9 @@ virt-list-filesystems - List filesystems in a virtual machine or disk image
=head1 DESCRIPTION
This tool is obsolete. Use L<virt-filesystems(1)> as a more
flexible replacement.
C<virt-list-filesystems> is a command line tool to list
the filesystems that are contained in a virtual machine or
disk image.
@@ -189,6 +192,7 @@ L<guestfs(3)>,
L<guestfish(1)>,
L<virt-cat(1)>,
L<virt-tar(1)>,
L<virt-filesystems(1)>,
L<virt-list-partitions(1)>,
L<Sys::Guestfs(3)>,
L<Sys::Guestfs::Lib(3)>,

View File

@@ -39,6 +39,9 @@ virt-list-partitions - List partitions in a virtual machine or disk image
=head1 DESCRIPTION
This tool is obsolete. Use L<virt-filesystems(1)> as a more
flexible replacement.
C<virt-list-partitions> 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<sh(1)> for details.
L<guestfs(3)>,
L<guestfish(1)>,
L<virt-filesystems(1)>,
L<virt-list-filesystems(1)>,
L<virt-resize(1)>,
L<Sys::Guestfs(3)>,

View File

@@ -58,10 +58,8 @@ B<should not> 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<virt-list-partitions(1)>,
L<virt-list-filesystems(1)> and
L<virt-df(1)>,
we recommend you go and read those manual pages first.
L<virt-filesystems(1)> and L<virt-df(1)>, we recommend you go and read
those manual pages first.
=head1 EXAMPLES
@@ -69,7 +67,7 @@ Copy C<olddisk> to C<newdisk>, 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<virsh dumpxml> like this to find the disk image name:
=item 3. Look at current sizing
Use L<virt-list-partitions(1)> to display the current partitions and
Use L<virt-filesystems(1)> 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<virt-list-filesystems(1)> to list the filesystems in
Use L<virt-filesystems(1)> to list the filesystems in
the guest.
You can give this option multiple times, I<but> it doesn't
@@ -1485,8 +1484,7 @@ manual page L<sh(1)> for details.
=head1 SEE ALSO
L<virt-list-partitions(1)>,
L<virt-list-filesystems(1)>,
L<virt-filesystems(1)>,
L<virt-df(1)>,
L<guestfs(3)>,
L<guestfish(1)>,