From: Richard W.M. Jones Date: Mon, 22 Nov 2010 12:37:55 +0000 (+0000) Subject: ls: Rewrite virt-ls in C. X-Git-Tag: 1.7.12~3 X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=commitdiff_plain;h=f6d3d5677194ae7aaea70d43845341d91907b5ee ls: Rewrite virt-ls in C. --- diff --git a/.gitignore b/.gitignore index 9a70868..16c23a1 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-ls +cat/virt-ls.1 +cat/virt-ls.static ChangeLog *.class *.cma diff --git a/HACKING b/HACKING index 42f0f3a..cf159d5 100644 --- a/HACKING +++ b/HACKING @@ -72,7 +72,7 @@ capitests/ Automated tests of the C API. cat/ - The 'virt-cat' command and documentation. + The 'virt-cat' and 'virt-ls' commands and documentation. contrib/ Outside contributions, experimental parts. diff --git a/Makefile.am b/Makefile.am index ec84cc5..b75397c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -249,8 +249,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 + $(MAKE) -C cat virt-cat.static virt-ls.static cp cat/virt-cat.static $(BINTMPDIR)$(bindir)/virt-cat + cp cat/virt-ls.static $(BINTMPDIR)$(bindir)/virt-ls $(MAKE) -C inspector virt-inspector.static cp inspector/virt-inspector.static $(BINTMPDIR)$(bindir)/virt-inspector (cd $(BINTMPDIR) && tar cf - .) | \ diff --git a/cat/Makefile.am b/cat/Makefile.am index 63064e6..e822ac3 100644 --- a/cat/Makefile.am +++ b/cat/Makefile.am @@ -1,4 +1,4 @@ -# libguestfs virt-cat. +# libguestfs virt-cat and virt-ls. # Copyright (C) 2010 Red Hat Inc. # # This program is free software; you can redistribute it and/or modify @@ -20,9 +20,12 @@ include $(top_srcdir)/subdir-rules.mk EXTRA_DIST = \ run-cat-locally \ test-virt-cat.sh \ - virt-cat.pod + virt-cat.pod \ + run-ls-locally \ + test-virt-ls.sh \ + virt-ls.pod -bin_PROGRAMS = virt-cat +bin_PROGRAMS = virt-cat virt-ls SHARED_SOURCE_FILES = \ ../fish/inspect.c \ @@ -46,10 +49,27 @@ virt_cat_LDADD = \ $(top_builddir)/src/libguestfs.la \ ../gnulib/lib/libgnu.la +virt_ls_SOURCES = \ + $(SHARED_SOURCE_FILES) \ + virt-ls.c + +virt_ls_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_ls_LDADD = \ + $(top_builddir)/src/libguestfs.la \ + ../gnulib/lib/libgnu.la + # Manual pages and HTML files for the website. -man_MANS = virt-cat.1 +man_MANS = virt-cat.1 virt-ls.1 -noinst_DATA = $(top_builddir)/html/virt-cat.1.html +noinst_DATA = \ + $(top_builddir)/html/virt-cat.1.html \ + $(top_builddir)/html/virt-ls.1.html virt-cat.1: virt-cat.pod $(POD2MAN) \ @@ -66,6 +86,21 @@ $(top_builddir)/html/virt-cat.1.html: virt-cat.pod --outfile html/$@ \ $(abs_srcdir)/$< +virt-ls.1: virt-ls.pod + $(POD2MAN) \ + --section 1 \ + -c "Virtualization Support" \ + --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ + $< > $@-t && mv $@-t $@ + +$(top_builddir)/html/virt-ls.1.html: virt-ls.pod + mkdir -p $(top_builddir)/html + cd $(top_builddir) && pod2html \ + --css 'pod.css' \ + --htmldir html \ + --outfile html/$@ \ + $(abs_srcdir)/$< + # Tests. random_val := $(shell awk 'BEGIN{srand(); print 1+int(255*rand())}' < /dev/null) @@ -75,10 +110,14 @@ TESTS_ENVIRONMENT = \ LD_LIBRARY_PATH=$(top_builddir)/src/.libs \ LIBGUESTFS_PATH=$(top_builddir)/appliance -TESTS = test-virt-cat.sh +TESTS = test-virt-cat.sh test-virt-ls.sh -# Build a partly-static library (for the binary distribution). +# Build a partly-static binary (for the binary distribution). 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-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-ls-locally b/cat/run-ls-locally new file mode 100755 index 0000000..adc2cb6 --- /dev/null +++ b/cat/run-ls-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-ls-locally [usual virt-ls 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-ls", @ARGV), "\n"); +exec("$path/cat/virt-ls", @ARGV); diff --git a/tools/test-virt-ls.sh b/cat/test-virt-ls.sh similarity index 79% rename from tools/test-virt-ls.sh rename to cat/test-virt-ls.sh index a0c9fc5..784fd67 100755 --- a/tools/test-virt-ls.sh +++ b/cat/test-virt-ls.sh @@ -3,9 +3,6 @@ export LANG=C set -e -# Just a random UUID. -uuid=868b1447-0ec5-41bf-a2e5-6a77a4c9b66f - # Read out the test directory using virt-ls. if [ "$(./virt-ls ../images/fedora.img /bin)" != "ls test1 diff --git a/cat/virt-ls.c b/cat/virt-ls.c new file mode 100644 index 0000000..a3f043a --- /dev/null +++ b/cat/virt-ls.c @@ -0,0 +1,348 @@ +/* virt-ls + * 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 "progname.h" + +#include "guestfs.h" +#include "options.h" + +/* Currently open libguestfs handle. */ +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 = 1; + +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 files in a virtual machine\n" + "Copyright (C) 2010 Red Hat Inc.\n" + "Usage:\n" + " %s [--options] -d domname file [dir ...]\n" + " %s [--options] -a disk.img [-a disk.img ...] dir [dir ...]\n" + "Options:\n" + " -a|--add image Add image\n" + " -c|--connect uri Specify libvirt URI for -d option\n" + " -d|--domain guest Add disks from libvirt guest\n" + " --echo-keys Don't turn off echo for passphrases\n" + " --format[=raw|..] Force disk format for -a option\n" + " --help Display brief help\n" + " --keys-from-stdin Read passphrases from stdin\n" + " -l|--long Long listing\n" + " -R|--recursive Recursive listing\n" + " -v|--verbose Verbose messages\n" + " -V|--version Display version and exit\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:lRvVx"; + static const struct option long_options[] = { + { "add", 1, 0, 'a' }, + { "connect", 1, 0, 'c' }, + { "domain", 1, 0, 'd' }, + { "echo-keys", 0, 0, 0 }, + { "format", 2, 0, 0 }, + { "help", 0, 0, HELP_OPTION }, + { "keys-from-stdin", 0, 0, 0 }, + { "long", 0, 0, 'l' }, + { "recursive", 0, 0, 'R' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { 0, 0, 0, 0 } + }; + struct drv *drvs = NULL; + struct drv *drv; + char *p, *file = NULL; + const char *format = NULL; + int c; + int option_index; + int next_prepared_drive = 1; + char mode = 0; + + 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 { + 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': + usage (EXIT_SUCCESS); + + case 'l': + mode = 'l'; + break; + + case 'R': + mode = 'R'; + 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); + } + } + + /* Old-style syntax? There were no -a or -d options in the old + * virt-ls which is how we detect this. + */ + if (drvs == NULL) { + /* argc - 1 because last parameter is the single directory name. */ + while (optind < argc - 1) { + if (strchr (argv[optind], '/') || + access (argv[optind], F_OK) == 0) { /* simulate -a option */ + drv = malloc (sizeof (struct drv)); + if (!drv) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + drv->type = drv_a; + drv->a.filename = argv[optind]; + drv->a.format = NULL; + drv->next = drvs; + drvs = drv; + } else { /* simulate -d option */ + drv = malloc (sizeof (struct drv)); + if (!drv) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + drv->type = drv_d; + drv->d.guest = argv[optind]; + drv->next = drvs; + drvs = drv; + } + + optind++; + } + } + + /* 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 == 1); + + /* User must specify at least one directory name on the command line. */ + if (optind >= argc || argc - optind < 1) + usage (EXIT_FAILURE); + + /* User must have specified some drives. */ + if (drvs == NULL) + usage (EXIT_FAILURE); + + /* Add drives, inspect and mount. Note that inspector is always true, + * and there is no -m option. + */ + add_drives (drvs, 'a'); + + if (guestfs_launch (g) == -1) + exit (EXIT_FAILURE); + + inspect_mount (); + + /* Free up data structures, no longer needed after this point. */ + free_drives (drvs); + + unsigned errors = 0; + + while (optind < argc) { + const char *dir = argv[optind]; + + if (mode == 0) { + char **lines; + size_t i; + + if ((lines = guestfs_ls (g, dir)) == NULL) + errors++; + else { + for (i = 0; lines[i] != NULL; ++i) { + printf ("%s\n", lines[i]); + free (lines[i]); + } + free (lines); + } + } + else if (mode == 'l') { + char *out; + size_t i; + + if ((out = guestfs_ll (g, dir)) == NULL) + errors++; + else { + printf ("%s", out); + free (out); + } + } + else if (mode == 'R') { + /* This is TMP_TEMPLATE_ON_STACK expanded from fish.h. */ + const char *tmpdir = guestfs_tmpdir (); + char tmpfile[strlen (tmpdir) + 32]; + sprintf (tmpfile, "%s/virtlsXXXXXX", tmpdir); + + int fd = mkstemp (tmpfile); + if (fd == -1) { + perror ("mkstemp"); + exit (EXIT_FAILURE); + } + + char buf[BUFSIZ]; /* also used below */ + snprintf (buf, sizeof buf, "/dev/fd/%d", fd); + + if (guestfs_find0 (g, dir, buf) == -1) + errors++; + else { + if (close (fd) == -1) { + perror (tmpfile); + exit (EXIT_FAILURE); + } + + /* The output of find0 is a \0-separated file. Turn each \0 into + * a \n character. + */ + fd = open (tmpfile, O_RDONLY); + if (fd == -1) { + perror (tmpfile); + exit (EXIT_FAILURE); + } + + ssize_t r; + while ((r = read (fd, buf, sizeof buf)) > 0) { + size_t i; + for (i = 0; i < (size_t) r; ++i) + if (buf[i] == '\0') + buf[i] = '\n'; + + size_t n = r; + while (n > 0) { + r = write (1, buf, n); + if (r == -1) { + perror ("write"); + exit (EXIT_FAILURE); + } + n -= r; + } + } + + if (r == -1 || close (fd) == -1) { + perror (tmpfile); + exit (EXIT_FAILURE); + } + } + + unlink (tmpfile); + } + optind++; + } + + guestfs_close (g); + + exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/cat/virt-ls.pod b/cat/virt-ls.pod new file mode 100755 index 0000000..4826092 --- /dev/null +++ b/cat/virt-ls.pod @@ -0,0 +1,222 @@ +=encoding utf8 + +=head1 NAME + +virt-ls - List files in a virtual machine + +=head1 SYNOPSIS + + virt-ls [--options] -d domname directory [directory ...] + + virt-ls [--options] -a disk.img [-a disk.img ...] directory [directory ...] + +Old style: + + virt-ls [--options] domname directory + + virt-ls [--options] disk.img [disk.img ...] directory + +=head1 DESCRIPTION + +C is a command line tool to list the names of files in a +directory inside a virtual machine or disk image. + +Multiple directory names can be given, in which case the output +from each is concatenated. + +C is just a simple wrapper around L +functionality. For more complex cases you should look at the +L tool. + +To list directories from a libvirt guest use the I<-d> option to +specify the name of the guest. For a disk image, use the I<-a> +option. + +C can be used in one of three modes: simple, long and +recursive. A simple listing is like the ordinary L command: + + $ virt-ls -d myguest / + bin + boot + [etc.] + +With the C<-l> (C<--long>) option, C shows more detail: + + $ virt-ls -l -d myguest / + total 204 + dr-xr-xr-x. 2 root root 4096 2009-08-25 19:06 bin + dr-xr-xr-x. 5 root root 3072 2009-08-25 19:06 boot + [etc.] + +With the C<-R> (C<--recursive>) option, C lists the +names of files and directories recursively: + + $ virt-ls -R -d myguest /tmp + foo + foo/bar + [etc.] + +You I combine these options. To do more complicated things, +use L. + +=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<-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<-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-ls 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<--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-ls --format=raw -a disk.img /dir + +forces raw format (no auto-detection) for C. + + virt-ls --format=raw -a disk.img --format -a another.img /dir + +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<--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> | B<--long> + +=item B<-R> | B<--recursive> + +Select the mode. With neither of these options, C +produces a simple, flat list of the files in the named directory. + +C produces a "long listing", which shows more detail (just +like the plain C command). + +C produces a recursive list of files starting at the named +directory. See the documentation for L +for precise details. + +You cannot combine these options. + +=item B<-v> + +=item B<--verbose> + +Enable verbose messages for debugging. + +=item B<-V> + +=item B<--version> + +Display version number and exit. + +=item B<-x> + +Enable tracing of libguestfs API calls. + +=back + +=head1 OLD-STYLE COMMAND LINE ARGUMENTS + +Previous versions of virt-ls allowed you to write either: + + virt-ls disk.img [disk.img ...] /dir + +or + + virt-ls guestname /dir + +whereas in this version you should use I<-a> or I<-d> respectively +to avoid the confusing case where a disk image might have the same +name as a guest. + +For compatibility the old style is still supported. + +=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) 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. diff --git a/po/POTFILES.in b/po/POTFILES.in index 0e96a2c..07dc838 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,4 +1,5 @@ cat/virt-cat.c +cat/virt-ls.c daemon/augeas.c daemon/available.c daemon/base64.c @@ -139,7 +140,6 @@ tools/virt-df.pl tools/virt-edit.pl tools/virt-list-filesystems.pl tools/virt-list-partitions.pl -tools/virt-ls.pl tools/virt-make-fs.pl tools/virt-rescue.pl tools/virt-resize.pl diff --git a/tools/Makefile.am b/tools/Makefile.am index 651daf9..c5131bd 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -22,7 +22,6 @@ tools = \ edit \ list-filesystems \ list-partitions \ - ls \ make-fs \ rescue \ resize \ @@ -76,7 +75,6 @@ TESTS_ENVIRONMENT = \ TESTS = test-virt-df.sh \ test-virt-list-filesystems.sh \ - test-virt-ls.sh \ test-virt-make-fs.sh \ test-virt-resize.sh \ test-virt-tar.sh diff --git a/tools/virt-ls b/tools/virt-ls deleted file mode 100755 index 03733f7..0000000 --- a/tools/virt-ls +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/perl -w -# virt-ls -# Copyright (C) 2009-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. - -use warnings; -use strict; - -use Sys::Guestfs; -use Sys::Guestfs::Lib qw(open_guest); -use Pod::Usage; -use Getopt::Long; -use File::Temp qw/tempdir/; -use File::Basename; -use Locale::TextDomain 'libguestfs'; - -=encoding utf8 - -=head1 NAME - -virt-ls - List files in a virtual machine - -=head1 SYNOPSIS - - virt-ls [--options] domname directory - - virt-ls [--options] disk.img [disk.img ...] directory - -=head1 DESCRIPTION - -C is a command line tool to list the names of files in a -directory inside a virtual machine or disk image. - -C is just a simple wrapper around L -functionality. For more complex cases you should look at the -L tool. - -C can be used in one of three modes: simple, long and -recursive. A simple listing is like the ordinary L command: - - $ virt-ls myguest / - bin - boot - [etc.] - -With the C<-l> (C<--long>) option, C shows more detail: - - $ virt-ls -l myguest / - total 204 - dr-xr-xr-x. 2 root root 4096 2009-08-25 19:06 bin - dr-xr-xr-x. 5 root root 3072 2009-08-25 19:06 boot - [etc.] - -With the C<-R> (C<--recursive>) option, C lists the -names of files and directories recursively: - - $ virt-ls -R myguest /tmp - foo - foo/bar - [etc.] - -You I combine these options. To do more complicated things, -use L. - -=head1 OPTIONS - -=over 4 - -=cut - -my $help; - -=item B<--help> - -Display brief help. - -=cut - -my $version; - -=item B<--version> - -Display version number and exit. - -=cut - -my $uri; - -=item B<--connect URI> | B<-c 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, then libvirt is not used -at all. - -=cut - -my $format; - -=item B<--format> raw - -Specify the format of disk images given on the command line. If this -is omitted then the format is autodetected from the content of the -disk image. - -If disk images are requested from libvirt, then this program asks -libvirt for this information. In this case, the value of the format -parameter is ignored. - -If working with untrusted raw-format guest disk images, you should -ensure the format is always specified. - -=cut - -my $mode; - -=item B<-l> | B<--long> - -=item B<-R> | B<--recursive> - -Select the mode. With neither of these options, C -produces a simple, flat list of the files in the named directory. - -C produces a "long listing", which shows more detail (just -like the plain C command). - -C produces a recursive list of files starting at the named -directory. See the documentation for L -for precise details. - -You cannot combine these options. - -=back - -=cut - -sub set_mode_l -{ - die __"virt-ls: cannot combine -l and -R options\n" if $mode; - $mode = "l"; -} - -sub set_mode_R -{ - die __"virt-ls: cannot combine -l and -R options\n" if $mode; - $mode = "R"; -} - -GetOptions ("help|?" => \$help, - "version" => \$version, - "connect|c=s" => \$uri, - "format=s" => \$format, - "long|l" => \&set_mode_l, - "recursive|R" => \&set_mode_R, - ) or pod2usage (2); -pod2usage (1) if $help; -if ($version) { - my $g = Sys::Guestfs->new (); - my %h = $g->version (); - print "$h{major}.$h{minor}.$h{release}$h{extra}\n"; - exit -} - -pod2usage (__"virt-ls: no image, VM names or directory to list given") - if @ARGV <= 1; - -my $directory = pop @ARGV; - -my $g; -if ($uri) { - $g = open_guest (\@ARGV, address => $uri, format => $format); -} else { - $g = open_guest (\@ARGV, format => $format); -} - -$g->launch (); - -my @roots = $g->inspect_os (); -if (@roots == 0) { - die __x("{prog}: No operating system could be detected inside this disk image.\n\nThis may be because the file is not a disk image, or is not a virtual machine\nimage, or because the OS type is not understood by libguestfs.\n\nIf you feel this is an error, please file a bug report including as much\ninformation about the disk image as possible.\n", - prog => basename ($0)); -} -if (@roots > 1) { - die __x("{prog}: multiboot operating systems are not supported.\n", - prog => basename ($0)) -} -my %fses = $g->inspect_get_mountpoints ($roots[0]); -my @fses = sort { length $a <=> length $b } keys %fses; -foreach (@fses) { - $g->mount_ro ($fses{$_}, $_); -} - -unless ($mode) { - my @r = $g->ls ($directory); - print "$_\n" foreach @r; -} elsif ($mode eq "l") { - print ($g->ll ($directory)); -} else { # $mode eq "R" - my $dir = tempdir (CLEANUP => 1); - $g->find0 ($directory, "$dir/find0"); - open F, "$dir/find0" or die "$dir/find0: $!\n"; - my $r; - my $line; - while (($r = read (F, $line, 1024)) > 0) { - $line =~ tr{\0}{\n}; - print $line; - } - close F; -} - -=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) 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.