New tool: virt-log: It lists log files from within the guest.

See 'TODO' file for suggested future improvements.
This commit is contained in:
Richard W.M. Jones
2014-06-14 10:50:36 +01:00
parent 026342a7ee
commit fb546eaee0
14 changed files with 666 additions and 26 deletions

4
.gitignore vendored
View File

@@ -48,6 +48,7 @@ Makefile.in
/bash/virt-filesystems
/bash/virt-format
/bash/virt-inspector
/bash/virt-log
/bash/virt-ls
/bash/virt-sysprep
/bash/virt-sparsify
@@ -72,6 +73,8 @@ Makefile.in
/cat/virt-cat.1
/cat/virt-filesystems
/cat/virt-filesystems.1
/cat/virt-log
/cat/virt-log.1
/cat/virt-ls
/cat/virt-ls.1
/ChangeLog
@@ -245,6 +248,7 @@ Makefile.in
/html/virt-inspector.1.html
/html/virt-list-filesystems.1.html
/html/virt-list-partitions.1.html
/html/virt-log.1.html
/html/virt-ls.1.html
/html/virt-make-fs.1.html
/html/virt-p2v.1.html

8
TODO
View File

@@ -590,3 +590,11 @@ Python
It seems as if we should call PyErr_Clear() somewhere in every
Python binding.
Improvements in virt-log
------------------------
- Make it faster, especially if the user wants to grep the output.
- Support Windows guests, see
http://rwmj.wordpress.com/2011/04/17/decoding-the-windows-event-log-using-guestfish/

View File

@@ -28,6 +28,7 @@ scripts = \
virt-filesystems \
virt-format \
virt-inspector \
virt-log \
virt-ls \
virt-rescue \
virt-resize \
@@ -51,6 +52,8 @@ virt-format:
ln -sf virt-alignment-scan $@
virt-inspector:
ln -sf virt-alignment-scan $@
virt-log:
ln -sf virt-alignment-scan $@
virt-ls:
ln -sf virt-alignment-scan $@
virt-sysprep:

View File

@@ -1,5 +1,5 @@
# libguestfs virt-cat, virt-filesystems and virt-ls.
# Copyright (C) 2010-2012 Red Hat Inc.
# libguestfs virt-cat, virt-filesystems, virt-log and virt-ls.
# Copyright (C) 2010-2014 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
@@ -22,12 +22,18 @@ EXTRA_DIST = \
virt-cat.pod \
test-virt-filesystems.sh \
virt-filesystems.pod \
test-virt-log.sh \
virt-log.pod \
test-virt-ls.sh \
virt-ls.pod
CLEANFILES = stamp-virt-cat.pod stamp-virt-ls.pod stamp-virt-filesystems.pod
CLEANFILES = \
stamp-virt-cat.pod \
stamp-virt-filesystems.pod \
stamp-virt-log.pod \
stamp-virt-ls.pod
bin_PROGRAMS = virt-cat virt-filesystems virt-ls
bin_PROGRAMS = virt-cat virt-filesystems virt-log virt-ls
SHARED_SOURCE_FILES = \
../fish/domain.c \
@@ -82,6 +88,28 @@ virt_filesystems_LDADD = \
$(LIBVIRT_LIBS) \
../gnulib/lib/libgnu.la
virt_log_SOURCES = \
$(SHARED_SOURCE_FILES) \
log.c
virt_log_CPPFLAGS = \
-DGUESTFS_WARN_DEPRECATED=1 \
-DLOCALEBASEDIR=\""$(datadir)/locale"\" \
-I$(top_srcdir)/src -I$(top_builddir)/src \
-I$(top_srcdir)/fish \
-I$(srcdir)/../gnulib/lib -I../gnulib/lib
virt_log_CFLAGS = \
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
$(LIBXML2_CFLAGS)
virt_log_LDADD = \
$(top_builddir)/src/libutils.la \
$(top_builddir)/src/libguestfs.la \
$(LIBXML2_LIBS) \
$(LIBVIRT_LIBS) \
../gnulib/lib/libgnu.la
virt_ls_SOURCES = \
$(SHARED_SOURCE_FILES) \
ls.c \
@@ -107,11 +135,12 @@ virt_ls_LDADD = \
../gnulib/lib/libgnu.la
# Manual pages and HTML files for the website.
man_MANS = virt-cat.1 virt-filesystems.1 virt-ls.1
man_MANS = virt-cat.1 virt-filesystems.1 virt-log.1 virt-ls.1
noinst_DATA = \
$(top_builddir)/html/virt-cat.1.html \
$(top_builddir)/html/virt-filesystems.1.html \
$(top_builddir)/html/virt-log.1.html \
$(top_builddir)/html/virt-ls.1.html
virt-cat.1 $(top_builddir)/html/virt-cat.1.html: stamp-virt-cat.pod
@@ -124,16 +153,6 @@ stamp-virt-cat.pod: virt-cat.pod
$<
touch $@
virt-ls.1 $(top_builddir)/html/virt-ls.1.html: stamp-virt-ls.pod
stamp-virt-ls.pod: virt-ls.pod
$(PODWRAPPER) \
--man virt-ls.1 \
--html $(top_builddir)/html/virt-ls.1.html \
--license GPLv2+ \
$<
touch $@
virt-filesystems.1 $(top_builddir)/html/virt-filesystems.1.html: stamp-virt-filesystems.pod
stamp-virt-filesystems.pod: virt-filesystems.pod
@@ -144,6 +163,26 @@ stamp-virt-filesystems.pod: virt-filesystems.pod
$<
touch $@
virt-log.1 $(top_builddir)/html/virt-log.1.html: stamp-virt-log.pod
stamp-virt-log.pod: virt-log.pod
$(PODWRAPPER) \
--man virt-log.1 \
--html $(top_builddir)/html/virt-log.1.html \
--license GPLv2+ \
$<
touch $@
virt-ls.1 $(top_builddir)/html/virt-ls.1.html: stamp-virt-ls.pod
stamp-virt-ls.pod: virt-ls.pod
$(PODWRAPPER) \
--man virt-ls.1 \
--html $(top_builddir)/html/virt-ls.1.html \
--license GPLv2+ \
$<
touch $@
# Tests.
TESTS_ENVIRONMENT = $(top_builddir)/run --test
@@ -152,6 +191,7 @@ if ENABLE_APPLIANCE
TESTS = \
test-virt-cat.sh \
test-virt-filesystems.sh \
test-virt-log.sh \
test-virt-ls.sh
endif ENABLE_APPLIANCE

398
cat/log.c Normal file
View File

@@ -0,0 +1,398 @@
/* virt-log
* Copyright (C) 2010-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include <locale.h>
#include <assert.h>
#include <libintl.h>
#include <syslog.h>
#include <time.h>
#include "c-ctype.h"
#include "guestfs.h"
#include "options.h"
/* Currently open libguestfs handle. */
guestfs_h *g;
int read_only = 1;
int live = 0;
int verbose = 0;
int keys_from_stdin = 0;
int echo_keys = 0;
const char *libvirt_uri = NULL;
int inspector = 1;
#define JOURNAL_DIR "/var/log/journal"
static int do_log (void);
static int do_log_journal (void);
static int do_log_text_file (const char *filename);
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: display log files in a virtual machine\n"
"Copyright (C) 2010-2014 Red Hat Inc.\n"
"Usage:\n"
" %s [--options] -d domname\n"
" %s [--options] -a disk.img [-a disk.img ...]\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"
" -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[])
{
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEBASEDIR);
textdomain (PACKAGE);
enum { HELP_OPTION = CHAR_MAX + 1 };
static const char *options = "a:c:d:vVx";
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-options", 0, 0, 0 },
{ "verbose", 0, 0, 'v' },
{ "version", 0, 0, 'V' },
{ 0, 0, 0, 0 }
};
struct drv *drvs = NULL;
const char *format = NULL;
int c;
int r;
int option_index;
g = guestfs_create ();
if (g == NULL) {
fprintf (stderr, _("guestfs_create: failed to create handle\n"));
exit (EXIT_FAILURE);
}
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, "long-options"))
display_long_options (long_options);
else 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 '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 == 1);
assert (live == 0);
/* User must not specify more arguments on the command line. */
if (optind != argc)
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);
r = do_log ();
guestfs_close (g);
exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
static int
do_log (void)
{
CLEANUP_FREE_STRING_LIST char **roots = NULL;
char *root;
CLEANUP_FREE char *type = NULL;
CLEANUP_FREE_STRING_LIST char **journal_files = NULL;
/* Get root mountpoint. fish/inspect.c guarantees the assertions
* below.
*/
roots = guestfs_inspect_get_roots (g);
assert (roots);
assert (roots[0] != NULL);
assert (roots[1] == NULL);
root = roots[0];
type = guestfs_inspect_get_type (g, root);
if (!type)
return -1;
/* systemd journal? */
guestfs_push_error_handler (g, NULL, NULL);
journal_files = guestfs_ls (g, JOURNAL_DIR);
guestfs_pop_error_handler (g);
if (STREQ (type, "linux") &&
journal_files != NULL && journal_files[0] != NULL)
return do_log_journal ();
/* Regular /var/log text files with different names. */
if (STRNEQ (type, "windows")) {
const char *logfiles[] = { "/var/log/syslog", "/var/log/messages", NULL };
size_t i;
for (i = 0; logfiles[i] != NULL; ++i) {
if (guestfs_is_file_opts (g, logfiles[i],
GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1,
-1) == 1)
return do_log_text_file (logfiles[i]);
}
}
/* Windows is not supported right now, so give an error message. */
if (STREQ (type, "windows")) {
fprintf (stderr,
_("%s: Windows guests are not supported right now.\n"
"In the meantime, try using the technique described here:\n"
"http://rwmj.wordpress.com/2011/04/17/decoding-the-windows-event-log-using-guestfish/\n"),
program_name);
return -1;
}
/* Otherwise, there are no log files. Hmm, is this right? XXX */
return 0;
}
/* Find the value of the named field from the list of attributes. If
* not found, returns NULL (not an error). If found, returns a
* pointer to the field, and the length of the field. NOTE: The field
* is NOT \0-terminated, so you have to print it using "%.*s".
*
* There may be multiple fields with the same name. In this case, the
* function returns the first entry.
*/
static const char *
get_journal_field (const struct guestfs_xattr_list *xattrs, const char *name,
size_t *len_rtn)
{
uint32_t i;
for (i = 0; i < xattrs->len; ++i) {
if (STREQ (name, xattrs->val[i].attrname)) {
*len_rtn = xattrs->val[i].attrval_len;
return xattrs->val[i].attrval;
}
}
return NULL; /* not found */
}
static const char *const log_level_table[] = {
[LOG_EMERG] = "emerg",
[LOG_ALERT] = "alert",
[LOG_CRIT] = "crit",
[LOG_ERR] = "err",
[LOG_WARNING] = "warning",
[LOG_NOTICE] = "notice",
[LOG_INFO] = "info",
[LOG_DEBUG] = "debug"
};
static int
do_log_journal (void)
{
int r;
unsigned errors = 0;
if (guestfs_journal_open (g, JOURNAL_DIR) == -1)
return -1;
while ((r = guestfs_journal_next (g)) > 0) {
CLEANUP_FREE struct guestfs_xattr_list *xattrs = NULL;
const char *priority_str, *identifier, *comm, *pid, *message;
size_t priority_len, identifier_len, comm_len, pid_len, message_len;
int priority = LOG_INFO;
int64_t ts;
/* The question is what fields to display. We should probably
* make this configurable, but for now use the "short" format from
* journalctl. (XXX)
*/
xattrs = guestfs_journal_get (g);
if (xattrs == NULL)
exit (EXIT_FAILURE);
ts = guestfs_journal_get_realtime_usec (g); /* error checked below */
priority_str = get_journal_field (xattrs, "PRIORITY", &priority_len);
//hostname = get_journal_field (xattrs, "_HOSTNAME", &hostname_len);
identifier = get_journal_field (xattrs, "SYSLOG_IDENTIFIER",
&identifier_len);
comm = get_journal_field (xattrs, "_COMM", &comm_len);
pid = get_journal_field (xattrs, "_PID", &pid_len);
message = get_journal_field (xattrs, "MESSAGE", &message_len);
/* Timestamp. */
if (ts >= 0) {
char buf[64];
struct tm tm;
ts /= 1000000;
if (strftime (buf, sizeof buf, "%b %d %H:%M:%S",
localtime_r (&ts, &tm)) <= 0) {
fprintf (stderr, _("%s: could not format journal entry timestamp\n"),
program_name);
errors++;
continue;
}
fputs (buf, stdout);
}
/* Hostname. */
/* We don't print this because it is assumed each line from the
* guest will have the same hostname. (XXX)
*/
//if (hostname)
// printf (" %.*s", (int) hostname_len, hostname);
/* Identifier. */
if (identifier)
printf (" %.*s", (int) identifier_len, identifier);
else if (comm)
printf (" %.*s", (int) comm_len, comm);
/* PID */
if (pid)
printf ("[%.*s]", (int) pid_len, pid);
/* Log level. */
if (priority_str && *priority_str >= '0' && *priority_str <= '7')
priority = *priority_str - '0';
printf (" %s:", log_level_table[priority]);
/* Message. */
if (message)
printf (" %.*s", (int) message_len, message);
printf ("\n");
}
if (r == -1) /* error from guestfs_journal_next */
return -1;
if (guestfs_journal_close (g) == -1)
return -1;
return errors > 0 ? -1 : 0;
}
static int
do_log_text_file (const char *filename)
{
return guestfs_download (g, filename, "/dev/stdout");
}

25
cat/test-virt-log.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash -
# libguestfs
# Copyright (C) 2009-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
export LANG=C
set -e
# Read out the log files from the image using virt-log.
for f in ../tests/guests/{fedora,debian,ubuntu}.img; do
if [ -s "$f" ]; then $VG ./virt-log -a "$f"; fi
done

View File

@@ -34,15 +34,6 @@ C<mydomain>:
virt-cat -d mydomain /etc/fstab
List syslog messages from a VM disk image file:
virt-cat -a disk.img /var/log/messages | tail
Find out what DHCP IP address a VM acquired:
virt-cat -d mydomain /var/log/messages | \
grep 'dhclient: bound to' | tail
Find out what packages were recently installed:
virt-cat -d mydomain /var/log/yum.log | tail
@@ -169,6 +160,12 @@ name as a guest.
For compatibility the old style is still supported.
=head1 LOG FILES
To list out the log files from guests, see the related tool
L<virt-log(1)>. It understands binary log formats such as the systemd
journal.
=head1 WINDOWS PATHS
C<virt-cat> has a limited ability to understand Windows drive letters
@@ -250,6 +247,7 @@ L<guestfs(3)>,
L<guestfish(1)>,
L<virt-copy-out(1)>,
L<virt-edit(1)>,
L<virt-log(1)>,
L<virt-tar-out(1)>,
L<http://libguestfs.org/>.

158
cat/virt-log.pod Normal file
View File

@@ -0,0 +1,158 @@
=head1 NAME
virt-log - Display log files from a virtual machine
=head1 SYNOPSIS
virt-log [--options] -d domname
virt-log [--options] -a disk.img [-a disk.img ...]
=head1 DESCRIPTION
C<virt-log> is a command line tool to display the log files from the
named virtual machine (or disk image).
This tool understands and displays both plain text log files
(eg. C</var/log/messages>) and binary formats such as the systemd
journal.
To display other types of files, use L<virt-cat(1)>. To copy files
out of a virtual machine, use L<virt-copy-out(1)>. To display the
contents of the Windows Registry, use L<virt-win-reg(1)>.
=head1 EXAMPLES
Display the complete logs from a guest:
virt-log -d mydomain | less
Find out what DHCP IP address a VM acquired:
virt-log -d mydomain | grep 'dhclient.*bound to'
=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<-a URI>
=item B<--add URI>
Add a remote disk. See L<guestfish(1)/ADDING REMOTE STORAGE>.
=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<-d> guest
=item B<--domain> guest
Add all the disks from the named libvirt guest. Domain UUIDs can be
used instead of names.
=item B<--echo-keys>
When prompting for keys and passphrases, virt-log 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-log --format=raw -a disk.img
forces raw format (no auto-detection) for C<disk.img>.
virt-log --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).
=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<-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 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 EXIT STATUS
This program returns 0 if successful, or non-zero if there was an
error.
=head1 SEE ALSO
L<guestfs(3)>,
L<guestfish(1)>,
L<virt-cat(1)>,
L<virt-copy-out(1)>,
L<virt-tar-out(1)>,
L<virt-win-reg(1)>,
L<http://libguestfs.org/>.
=head1 AUTHOR
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
=head1 COPYRIGHT
Copyright (C) 2010-2014 Red Hat Inc.

View File

@@ -1615,6 +1615,7 @@ L<virt-filesystems(1)>,
L<virt-inspector(1)>,
L<virt-list-filesystems(1)>,
L<virt-list-partitions(1)>,
L<virt-log(1)>,
L<virt-ls(1)>,
L<virt-make-fs(1)>,
L<virt-p2v(1)>,

View File

@@ -64,6 +64,7 @@ MANPAGES = \
virt-inspector.1 \
virt-list-filesystems.1 \
virt-list-partitions.1 \
virt-log.1 \
virt-ls.1 \
virt-make-fs.1 \
virt-rescue.1 \

View File

@@ -4,6 +4,7 @@
../builder/virt-index-validate.pod
../cat/virt-cat.pod
../cat/virt-filesystems.pod
../cat/virt-log.pod
../cat/virt-ls.pod
../customize/customize-options.pod
../customize/customize-synopsis.pod

View File

@@ -64,6 +64,7 @@ MANPAGES = \
virt-inspector.1 \
virt-list-filesystems.1 \
virt-list-partitions.1 \
virt-log.1 \
virt-ls.1 \
virt-make-fs.1 \
virt-rescue.1 \

View File

@@ -9,6 +9,7 @@ builder/setlocale-c.c
builder/uname-c.c
cat/cat.c
cat/filesystems.c
cat/log.c
cat/ls.c
cat/visit.c
customize/crypt-c.c

View File

@@ -4289,8 +4289,8 @@ L<virt-builder(1)> command and documentation.
=item C<cat>
The L<virt-cat(1)>, L<virt-filesystems(1)> and L<virt-ls(1)> commands
and documentation.
The L<virt-cat(1)>, L<virt-filesystems(1)>, L<virt-log(1)>
and L<virt-ls(1)> commands and documentation.
=item C<contrib>
@@ -4776,6 +4776,7 @@ L<virt-format(1)>,
L<virt-inspector(1)>,
L<virt-list-filesystems(1)>,
L<virt-list-partitions(1)>,
L<virt-log(1)>,
L<virt-ls(1)>,
L<virt-make-fs(1)>,
L<virt-p2v(1)>,