"Finish off" this program, add manpage.
authorRichard W.M. Jones <rjones@redhat.com>
Tue, 4 Mar 2008 13:43:04 +0000 (13:43 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Tue, 4 Mar 2008 13:43:04 +0000 (13:43 +0000)
virt-df/.depend
virt-df/Makefile.in [changed mode: 0755->0644]
virt-df/README [changed mode: 0755->0644]
virt-df/virt-df.1 [new file with mode: 0644]
virt-df/virt-df.pod [new file with mode: 0644]
virt-df/virt-df.txt [new file with mode: 0644]
virt-df/virt_df.ml [changed mode: 0755->0644]

index 8cb0e7e..1a7750e 100644 (file)
@@ -6,5 +6,5 @@ virt_df_lvm2.cmo: virt_df.cmo
 virt_df_lvm2.cmx: virt_df.cmx 
 virt_df_main.cmo: virt_df.cmo 
 virt_df_main.cmx: virt_df.cmx 
-virt_df.cmo: ../libvirt/libvirt.cmi 
-virt_df.cmx: ../libvirt/libvirt.cmx 
+virt_df.cmo: ../libvirt/libvirt_version.cmi ../libvirt/libvirt.cmi 
+virt_df.cmx: ../libvirt/libvirt_version.cmx ../libvirt/libvirt.cmx 
old mode 100755 (executable)
new mode 100644 (file)
index cc25c9c..13d386e
@@ -50,9 +50,9 @@ export LD_LIBRARY_PATH=../libvirt
 BYTE_TARGETS   := virt-df
 OPT_TARGETS    := virt-df.opt
 
-#ifeq ($(HAVE_PERLDOC),perldoc)
-#BYTE_TARGETS  += virt-df.1 virt-df.txt
-#endif
+ifeq ($(HAVE_PERLDOC),perldoc)
+BYTE_TARGETS   += virt-df.1 virt-df.txt
+endif
 
 all: $(BYTE_TARGETS)
 
@@ -68,14 +68,14 @@ virt-df.opt: $(XOBJS)
          ../libvirt/mllibvirt.cmxa -cclib -lncurses -o $@ $^
 
 # Manual page.
-#ifeq ($(HAVE_PERLDOC),perldoc)
-#virt-df.1: virt-df.pod
-#      pod2man -c "Virtualization Support" --release "$(PACKAGE)-$(VERSION)" \
-#              $< > $@
-#
-#virt-df.txt: virt-df.pod
-#      pod2text $< > $@
-#endif
+ifeq ($(HAVE_PERLDOC),perldoc)
+virt-df.1: virt-df.pod
+       pod2man -c "Virtualization Support" --release "$(PACKAGE)-$(VERSION)" \
+               $< > $@
+
+virt-df.txt: virt-df.pod
+       pod2text $< > $@
+endif
 
 install:
        if [ -x virt-df.opt ]; then \
old mode 100755 (executable)
new mode 100644 (file)
index c6dfcc7..0623030
@@ -1,33 +1,2 @@
-virt-df is a 'df' tool for printing out the used and available disk
-space in all active and inactive domains.  Without this tool you would
-need to log in to each domain individually or set up monitoring.
-
-It is only a proof-of-concept.  Please bare in mind the following
-limitations when using this tool:
-
-(1) It does not work over remote connections.  Part of the reason why
-I wrote virt-df was to get an idea of how the remote storage API for
-libvirt might look.
-
-(2) It only understands a limited set of partition types.  Assuming
-that the files and partitions that we get back from libvirt / Xen
-correspond to block devices in the guests, we can go some way towards
-manually parsing those partitions to find out what they contain.  We
-can read the MBR, LVM, superblocks and so on.  However that's a lot of
-parsing work, and currently there is no library which understands a
-wide range of partition schemes and filesystem types (not even
-libparted which doesn't support LVM yet).  The Linux kernel does
-support that, but there's not really any good way to access that work.
-
-The current implementation uses a hand-coded parser which understands
-some simple formats (MBR, LVM2, ext2/3).  In future we should use
-something like libparted.
-
-(3) The statistics you get are delayed.  The real state of, for
-example, an ext2 filesystem is only stored in the memory of the
-guest's kernel.  The ext2 superblock contains some meta-information
-about blocks used and free, but this superblock is not up to date.  In
-fact the guest kernel may not update it even on a 'sync', not until
-the filesystem is unmounted.  Some operations do appear to write the
-superblock, for example fsync(2) [that is my reading of the ext2/3
-source code at least].
+Please see the manual page (virt-df.pod or virt-df.txt in this
+directory).
\ No newline at end of file
diff --git a/virt-df/virt-df.1 b/virt-df/virt-df.1
new file mode 100644 (file)
index 0000000..ff7e92d
--- /dev/null
@@ -0,0 +1,280 @@
+.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  | will give a
+.\" real vertical bar.  \*(C+ will give a nicer C++.  Capital omega is used to
+.\" do unbreakable dashes and therefore won't be available.  \*(C` and \*(C'
+.\" expand to `' in nroff, nothing in troff, for use with C<>.
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` ""
+.    ds C' ""
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" entries marked with X<> in POD.  Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.if \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.\"
+.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "VIRT-DF 1"
+.TH VIRT-DF 1 "2008-03-04" "ocaml-libvirt-0.4.0.3" "Virtualization Support"
+.SH "NAME"
+virt\-df \- 'df'\-like utility for virtualization stats
+.SH "SUMMARY"
+.IX Header "SUMMARY"
+virt-df [\-options]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+virt-df is a \fIdf\fR\|(1)\-like utility for showing the actual disk usage
+of guests.  Many command line options are the same as for ordinary
+\&\fIdf\fR.
+.PP
+It uses libvirt so it is capable of showing stats across a variety of
+different virtualization systems.
+.PP
+There are some shortcomings to the whole approach of reading disk
+state from outside the guest.  Please read \s-1SHORTCOMINGS\s0 section below
+for more details.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-a\fR, \fB\-\-all\fR" 4
+.IX Item "-a, --all"
+Show all domains.  The default is show only running (active) domains.
+.IP "\fB\-c uri\fR, \fB\-\-connect uri\fR" 4
+.IX Item "-c uri, --connect uri"
+Connect to libvirt \s-1URI\s0.  The default is to connect to the default
+libvirt \s-1URI\s0, normally Xen.
+.IP "\fB\-h\fR, \fB\-\-human\-readable\fR" 4
+.IX Item "-h, --human-readable"
+Display human-readable sizes (eg. 10GiB).
+.IP "\fB\-i\fR, \fB\-\-inodes\fR" 4
+.IX Item "-i, --inodes"
+Display inode information.
+.IP "\fB\-\-help\fR" 4
+.IX Item "--help"
+Display usage summary.
+.IP "\fB\-\-version\fR" 4
+.IX Item "--version"
+Display version and exit.
+.SH "SHORTCOMINGS"
+.IX Header "SHORTCOMINGS"
+virt-df spies on the guest's disk image to try to work out how much
+disk space it is actually using.  There are some shortcomings to this,
+described here.
+.PP
+(1) It does not work over remote connections.  The storage \s-1API\s0 does
+not support peeking into remote disks, and libvirt has rejected a
+request to add this support.
+.PP
+(2) It only understands a limited set of partition types.  Assuming
+that the files and partitions that we get back from libvirt / Xen
+correspond to block devices in the guests, we can go some way towards
+manually parsing those partitions to find out what they contain.  We
+can read the \s-1MBR\s0, \s-1LVM\s0, superblocks and so on.  However that's a lot of
+parsing work, and currently there is no library which understands a
+wide range of partition schemes and filesystem types (not even
+libparted which doesn't support \s-1LVM\s0 yet).  The Linux kernel does
+support that, but there's not really any good way to access that work.
+.PP
+The current implementation uses a hand-coded parser which understands
+some simple formats (\s-1MBR\s0, \s-1LVM2\s0, ext2/3).  In future we should use
+something like libparted.
+.PP
+(3) The statistics you get are delayed.  The real state of, for
+example, an ext2 filesystem is only stored in the memory of the
+guest's kernel.  The ext2 superblock contains some meta-information
+about blocks used and free, but this superblock is not up to date.  In
+fact the guest kernel may not update it even on a 'sync', not until
+the filesystem is unmounted.  Some operations do appear to write the
+superblock, for example \fIfsync\fR\|(2) [that is my reading of the ext2/3
+source code at least].
+.SH "SECURITY"
+.IX Header "SECURITY"
+The current code is probably not secure against malicious guests.  In
+particular a malicious guest can set up a disk in such a way that disk
+structures with loops can cause virt-df to spin forever.  We are
+preparing a parsing library which can fix these sorts of problems.
+.PP
+In the meantime, do not run virt-df on untrusted guests.
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+\&\fIdf\fR\|(1),
+\&\fIvirsh\fR\|(1),
+\&\fIxm\fR\|(1),
+<http://www.libvirt.org/ocaml/>,
+<http://www.libvirt.org/>,
+<http://et.redhat.com/~rjones/>,
+<http://caml.inria.fr/>
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+Richard W.M. Jones <rjones @ redhat . com>
+.SH "COPYRIGHT"
+.IX Header "COPYRIGHT"
+(C) Copyright 2007\-2008 Red Hat Inc., Richard W.M. Jones
+http://libvirt.org/
+.PP
+This program is free software; you can redistribute it and/or modify
+it under the terms of the \s-1GNU\s0 General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+.PP
+This program is distributed in the hope that it will be useful,
+but \s-1WITHOUT\s0 \s-1ANY\s0 \s-1WARRANTY\s0; without even the implied warranty of
+\&\s-1MERCHANTABILITY\s0 or \s-1FITNESS\s0 \s-1FOR\s0 A \s-1PARTICULAR\s0 \s-1PURPOSE\s0.  See the
+\&\s-1GNU\s0 General Public License for more details.
+.PP
+You should have received a copy of the \s-1GNU\s0 General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, \s-1MA\s0 02139, \s-1USA\s0.
+.SH "REPORTING BUGS"
+.IX Header "REPORTING BUGS"
+Bugs can be viewed on the Red Hat Bugzilla page:
+<https://bugzilla.redhat.com/>.
+.PP
+If you find a bug in virt\-df, please follow these steps to report it:
+.IP "1. Check for existing bug reports" 4
+.IX Item "1. Check for existing bug reports"
+Go to <https://bugzilla.redhat.com/> and search for similar bugs.
+Someone may already have reported the same bug, and they may even
+have fixed it.
+.IP "2. Capture debug and error messages" 4
+.IX Item "2. Capture debug and error messages"
+Run
+.Sp
+.Vb 1
+\& virt-df > virt-df.log 2>&1
+.Ve
+.Sp
+and keep \fIvirt\-df.log\fR.  It contains error messages which you should
+submit with your bug report.
+.IP "3. Get version of virt-df and version of libvirt." 4
+.IX Item "3. Get version of virt-df and version of libvirt."
+Run
+.Sp
+.Vb 1
+\& virt-df --version
+.Ve
+.IP "4. Submit a bug report." 4
+.IX Item "4. Submit a bug report."
+Go to <https://bugzilla.redhat.com/> and enter a new bug.
+Please describe the problem in as much detail as possible.
+.Sp
+Remember to include the version numbers (step 3) and the debug
+messages file (step 2).
+.IP "5. Assign the bug to rjones @ redhat.com" 4
+.IX Item "5. Assign the bug to rjones @ redhat.com"
+Assign or reassign the bug to \fBrjones @ redhat.com\fR (without the
+spaces).  You can also send me an email with the bug number if you
+want a faster response.
diff --git a/virt-df/virt-df.pod b/virt-df/virt-df.pod
new file mode 100644 (file)
index 0000000..84b1d97
--- /dev/null
@@ -0,0 +1,174 @@
+=head1 NAME
+
+virt-df - 'df'-like utility for virtualization stats
+
+=head1 SUMMARY
+
+virt-df [-options]
+
+=head1 DESCRIPTION
+
+virt-df is a L<df(1)>-like utility for showing the actual disk usage
+of guests.  Many command line options are the same as for ordinary
+I<df>.
+
+It uses libvirt so it is capable of showing stats across a variety of
+different virtualization systems.
+
+There are some shortcomings to the whole approach of reading disk
+state from outside the guest.  Please read SHORTCOMINGS section below
+for more details.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-a>, B<--all>
+
+Show all domains.  The default is show only running (active) domains.
+
+=item B<-c uri>, B<--connect uri>
+
+Connect to libvirt URI.  The default is to connect to the default
+libvirt URI, normally Xen.
+
+=item B<-h>, B<--human-readable>
+
+Display human-readable sizes (eg. 10GiB).
+
+=item B<-i>, B<--inodes>
+
+Display inode information.
+
+=item B<--help>
+
+Display usage summary.
+
+=item B<--version>
+
+Display version and exit.
+
+=back
+
+=head1 SHORTCOMINGS
+
+virt-df spies on the guest's disk image to try to work out how much
+disk space it is actually using.  There are some shortcomings to this,
+described here.
+
+(1) It does not work over remote connections.  The storage API does
+not support peeking into remote disks, and libvirt has rejected a
+request to add this support.
+
+(2) It only understands a limited set of partition types.  Assuming
+that the files and partitions that we get back from libvirt / Xen
+correspond to block devices in the guests, we can go some way towards
+manually parsing those partitions to find out what they contain.  We
+can read the MBR, LVM, superblocks and so on.  However that's a lot of
+parsing work, and currently there is no library which understands a
+wide range of partition schemes and filesystem types (not even
+libparted which doesn't support LVM yet).  The Linux kernel does
+support that, but there's not really any good way to access that work.
+
+The current implementation uses a hand-coded parser which understands
+some simple formats (MBR, LVM2, ext2/3).  In future we should use
+something like libparted.
+
+(3) The statistics you get are delayed.  The real state of, for
+example, an ext2 filesystem is only stored in the memory of the
+guest's kernel.  The ext2 superblock contains some meta-information
+about blocks used and free, but this superblock is not up to date.  In
+fact the guest kernel may not update it even on a 'sync', not until
+the filesystem is unmounted.  Some operations do appear to write the
+superblock, for example L<fsync(2)> [that is my reading of the ext2/3
+source code at least].
+
+=head1 SECURITY
+
+The current code is probably not secure against malicious guests.  In
+particular a malicious guest can set up a disk in such a way that disk
+structures with loops can cause virt-df to spin forever.  We are
+preparing a parsing library which can fix these sorts of problems.
+
+In the meantime, do not run virt-df on untrusted guests.
+
+=head1 SEE ALSO
+
+L<df(1)>,
+L<virsh(1)>,
+L<xm(1)>,
+L<http://www.libvirt.org/ocaml/>,
+L<http://www.libvirt.org/>,
+L<http://et.redhat.com/~rjones/>,
+L<http://caml.inria.fr/>
+
+=head1 AUTHORS
+
+Richard W.M. Jones <rjones @ redhat . com>
+
+=head1 COPYRIGHT
+
+(C) Copyright 2007-2008 Red Hat Inc., Richard W.M. Jones
+http://libvirt.org/
+
+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.
+
+=head1 REPORTING BUGS
+
+Bugs can be viewed on the Red Hat Bugzilla page:
+L<https://bugzilla.redhat.com/>.
+
+If you find a bug in virt-df, please follow these steps to report it:
+
+=over 4
+
+=item 1. Check for existing bug reports
+
+Go to L<https://bugzilla.redhat.com/> and search for similar bugs.
+Someone may already have reported the same bug, and they may even
+have fixed it.
+
+=item 2. Capture debug and error messages
+
+Run
+
+ virt-df > virt-df.log 2>&1
+
+and keep I<virt-df.log>.  It contains error messages which you should
+submit with your bug report.
+
+=item 3. Get version of virt-df and version of libvirt.
+
+Run
+
+ virt-df --version
+
+=item 4. Submit a bug report.
+
+Go to L<https://bugzilla.redhat.com/> and enter a new bug.
+Please describe the problem in as much detail as possible.
+
+Remember to include the version numbers (step 3) and the debug
+messages file (step 2).
+
+=item 5. Assign the bug to rjones @ redhat.com
+
+Assign or reassign the bug to B<rjones @ redhat.com> (without the
+spaces).  You can also send me an email with the bug number if you
+want a faster response.
+
+=back
+
+=end
diff --git a/virt-df/virt-df.txt b/virt-df/virt-df.txt
new file mode 100644 (file)
index 0000000..fcddafb
--- /dev/null
@@ -0,0 +1,139 @@
+NAME
+    virt-df - 'df'-like utility for virtualization stats
+
+SUMMARY
+    virt-df [-options]
+
+DESCRIPTION
+    virt-df is a df(1)-like utility for showing the actual disk usage of
+    guests. Many command line options are the same as for ordinary *df*.
+
+    It uses libvirt so it is capable of showing stats across a variety of
+    different virtualization systems.
+
+    There are some shortcomings to the whole approach of reading disk state
+    from outside the guest. Please read SHORTCOMINGS section below for more
+    details.
+
+OPTIONS
+    -a, --all
+        Show all domains. The default is show only running (active) domains.
+
+    -c uri, --connect uri
+        Connect to libvirt URI. The default is to connect to the default
+        libvirt URI, normally Xen.
+
+    -h, --human-readable
+        Display human-readable sizes (eg. 10GiB).
+
+    -i, --inodes
+        Display inode information.
+
+    --help
+        Display usage summary.
+
+    --version
+        Display version and exit.
+
+SHORTCOMINGS
+    virt-df spies on the guest's disk image to try to work out how much disk
+    space it is actually using. There are some shortcomings to this,
+    described here.
+
+    (1) It does not work over remote connections. The storage API does not
+    support peeking into remote disks, and libvirt has rejected a request to
+    add this support.
+
+    (2) It only understands a limited set of partition types. Assuming that
+    the files and partitions that we get back from libvirt / Xen correspond
+    to block devices in the guests, we can go some way towards manually
+    parsing those partitions to find out what they contain. We can read the
+    MBR, LVM, superblocks and so on. However that's a lot of parsing work,
+    and currently there is no library which understands a wide range of
+    partition schemes and filesystem types (not even libparted which doesn't
+    support LVM yet). The Linux kernel does support that, but there's not
+    really any good way to access that work.
+
+    The current implementation uses a hand-coded parser which understands
+    some simple formats (MBR, LVM2, ext2/3). In future we should use
+    something like libparted.
+
+    (3) The statistics you get are delayed. The real state of, for example,
+    an ext2 filesystem is only stored in the memory of the guest's kernel.
+    The ext2 superblock contains some meta-information about blocks used and
+    free, but this superblock is not up to date. In fact the guest kernel
+    may not update it even on a 'sync', not until the filesystem is
+    unmounted. Some operations do appear to write the superblock, for
+    example fsync(2) [that is my reading of the ext2/3 source code at
+    least].
+
+SECURITY
+    The current code is probably not secure against malicious guests. In
+    particular a malicious guest can set up a disk in such a way that disk
+    structures with loops can cause virt-df to spin forever. We are
+    preparing a parsing library which can fix these sorts of problems.
+
+    In the meantime, do not run virt-df on untrusted guests.
+
+SEE ALSO
+    df(1), virsh(1), xm(1), <http://www.libvirt.org/ocaml/>,
+    <http://www.libvirt.org/>, <http://et.redhat.com/~rjones/>,
+    <http://caml.inria.fr/>
+
+AUTHORS
+    Richard W.M. Jones <rjones @ redhat . com>
+
+COPYRIGHT
+    (C) Copyright 2007-2008 Red Hat Inc., Richard W.M. Jones
+    http://libvirt.org/
+
+    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.
+
+REPORTING BUGS
+    Bugs can be viewed on the Red Hat Bugzilla page:
+    <https://bugzilla.redhat.com/>.
+
+    If you find a bug in virt-df, please follow these steps to report it:
+
+    1. Check for existing bug reports
+        Go to <https://bugzilla.redhat.com/> and search for similar bugs.
+        Someone may already have reported the same bug, and they may even
+        have fixed it.
+
+    2. Capture debug and error messages
+        Run
+
+         virt-df > virt-df.log 2>&1
+
+        and keep *virt-df.log*. It contains error messages which you should
+        submit with your bug report.
+
+    3. Get version of virt-df and version of libvirt.
+        Run
+
+         virt-df --version
+
+    4. Submit a bug report.
+        Go to <https://bugzilla.redhat.com/> and enter a new bug. Please
+        describe the problem in as much detail as possible.
+
+        Remember to include the version numbers (step 3) and the debug
+        messages file (step 2).
+
+    5. Assign the bug to rjones @ redhat.com
+        Assign or reassign the bug to rjones @ redhat.com (without the
+        spaces). You can also send me an email with the bug number if you
+        want a faster response.
+
old mode 100755 (executable)
new mode 100644 (file)
index bbaaa7d..350d535
@@ -1,5 +1,5 @@
 (* 'df' command for virtual domains.
-   (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
+   (C) Copyright 2007-2008 Richard W.M. Jones, Red Hat Inc.
    http://libvirt.org/
 
    This program is free software; you can redistribute it and/or modify
@@ -37,6 +37,7 @@ let (/^) = Int64.div
 let uri = ref None
 let inodes = ref false
 let human = ref false
+let all = ref false
 
 (* Maximum number of extended partitions possible. *)
 let max_extended_partitions = 100
@@ -104,7 +105,7 @@ let rec probe_device dom_name target source =
   let size = (LargeFile.fstat fd).LargeFile.st_size in
   let size = size /^ sector_size in    (* Size in sectors. *)
 
-  print_device dom_name target source size;
+  (*print_device dom_name target source size;*)
 
   let partitions = probe_mbr fd in
 
@@ -124,9 +125,9 @@ let rec probe_device dom_name target source =
            None
       ) partitions in
     let stats = List.filter_map (fun x -> x) stats in
-    print_stats stats
+    print_stats dom_name stats
   ) else            (* Not an MBR, assume it's a single partition. *)
-    print_stats [target, probe_partition target None fd 0L size];
+    print_stats dom_name [target, probe_partition target None fd 0L size];
 
   close fd
 
@@ -243,57 +244,59 @@ and probe_partition target part_type fd start size =
          ProbeFailed
            (sprintf "unsupported partition type %02x" part_type)
 
-and print_stats statss =
+and print_stats dom_name statss =
   List.iter (
-    function
-    (* Swap partition. *)
-    | (target, Swap { swap_name = swap_name;
-                     swap_block_size = block_size;
-                     swap_blocks_total = blocks_total }) ->
-       if not !human then
-         printf "\t%s %Ld %s\n"
-           target (block_size *^ blocks_total /^ 1024L) swap_name
-       else
-         printf "\t%s %s %s\n"
-           target (printable_size (block_size *^ blocks_total)) swap_name
-
-    (* Ordinary filesystem. *)
-    | (target, Filesystem stats) ->
-       printf "\t%s " target;
-
-       if not !inodes then (           (* Block display. *)
-         (* 'df' doesn't count the restricted blocks. *)
-         let blocks_total =
-           stats.fs_blocks_total -^ stats.fs_blocks_reserved in
-         let blocks_avail =
-           stats.fs_blocks_avail -^ stats.fs_blocks_reserved in
-         let blocks_avail =
-           if blocks_avail < 0L then 0L else blocks_avail in
-
-         if not !human then (          (* Display 1K blocks. *)
-           printf "%Ld %Ld %Ld %s\n"
-             (blocks_total *^ stats.fs_block_size /^ 1024L)
-             (stats.fs_blocks_used *^ stats.fs_block_size /^ 1024L)
-             (blocks_avail *^ stats.fs_block_size /^ 1024L)
-             stats.fs_name
-         ) else (                      (* Human-readable blocks. *)
-           printf "%s %s %s %s\n"
-             (printable_size (blocks_total *^ stats.fs_block_size))
-             (printable_size (stats.fs_blocks_used *^ stats.fs_block_size))
-             (printable_size (blocks_avail *^ stats.fs_block_size))
+    fun (target, fs_probe_t) ->
+      let dom_target = dom_name ^ ":" ^ target in
+      printf "%-20s " dom_target;
+
+      match fs_probe_t with
+      (* Swap partition. *)
+      | Swap { swap_name = swap_name;
+              swap_block_size = block_size;
+              swap_blocks_total = blocks_total } ->
+         if not !human then
+           printf "%10Ld                       %s\n"
+             (block_size *^ blocks_total /^ 1024L) swap_name
+         else
+           printf "%10s                       %s\n"
+             (printable_size (block_size *^ blocks_total)) swap_name
+
+      (* Ordinary filesystem. *)
+      | Filesystem stats ->
+         if not !inodes then (         (* Block display. *)
+           (* 'df' doesn't count the restricted blocks. *)
+           let blocks_total =
+             stats.fs_blocks_total -^ stats.fs_blocks_reserved in
+           let blocks_avail =
+             stats.fs_blocks_avail -^ stats.fs_blocks_reserved in
+           let blocks_avail =
+             if blocks_avail < 0L then 0L else blocks_avail in
+
+           if not !human then (        (* Display 1K blocks. *)
+             printf "%10Ld %10Ld %10Ld %s\n"
+               (blocks_total *^ stats.fs_block_size /^ 1024L)
+               (stats.fs_blocks_used *^ stats.fs_block_size /^ 1024L)
+               (blocks_avail *^ stats.fs_block_size /^ 1024L)
+               stats.fs_name
+           ) else (                    (* Human-readable blocks. *)
+             printf "%10s %10s %10s %s\n"
+               (printable_size (blocks_total *^ stats.fs_block_size))
+               (printable_size (stats.fs_blocks_used *^ stats.fs_block_size))
+               (printable_size (blocks_avail *^ stats.fs_block_size))
+               stats.fs_name
+           )
+         ) else (                      (* Inodes display. *)
+           printf "%10Ld %10Ld %10Ld %s\n"
+             stats.fs_inodes_total stats.fs_inodes_used stats.fs_inodes_avail
              stats.fs_name
          )
-       ) else (                        (* Inodes display. *)
-         printf "%Ld %Ld %Ld %s\n"
-           stats.fs_inodes_total stats.fs_inodes_used stats.fs_inodes_avail
-           stats.fs_name
-       )
 
-    (* Unsupported filesystem or other failure. *)
-    | (target, ProbeFailed reason) ->
-       printf "\t%s %s\n" target reason
+      (* Unsupported filesystem or other failure. *)
+      | ProbeFailed reason ->
+         printf "                                 %s\n" reason
 
-    | (_, ProbeIgnore) -> ()
+      | ProbeIgnore -> ()
   ) statss
 
 (* Target is something like "hda" and size is the size in sectors. *)
@@ -323,13 +326,26 @@ let main () =
   (* Command line argument parsing. *)
   let set_uri = function "" -> uri := None | u -> uri := Some u in
 
+  let version () =
+    printf "virt-df %s\n" (Libvirt_version.version);
+
+    let major, minor, release =
+      let v, _ = Libvirt.get_version () in
+      v / 1_000_000, (v / 1_000) mod 1_000, v mod 1_000 in
+    printf "libvirt %d.%d.%d\n" major minor release;
+    exit 0
+  in
+
   let argspec = Arg.align [
+    "-a", Arg.Set all, " Show all domains (default: only active domains)";
+    "--all", Arg.Set all, " Show all domains (default: only active domains)";
     "-c", Arg.String set_uri, "uri Connect to URI (default: Xen)";
     "--connect", Arg.String set_uri, "uri Connect to URI (default: Xen)";
     "-h", Arg.Set human, " Print sizes in human-readable format";
     "--human-readable", Arg.Set human, " Print sizes in human-readable format";
     "-i", Arg.Set inodes, " Show inodes instead of blocks";
     "--inodes", Arg.Set inodes, " Show inodes instead of blocks";
+    "--version", Arg.Unit version, " Display version and exit";
   ] in
 
   let anon_fun str = raise (Arg.Bad (str ^ ": unknown parameter")) in
@@ -361,11 +377,15 @@ OPTIONS" in
       let nr_active_doms = C.num_of_domains conn in
       let active_doms = Array.to_list (C.list_domains conn nr_active_doms) in
       let active_doms = List.map (D.lookup_by_id conn) active_doms in
-      let nr_inactive_doms = C.num_of_defined_domains conn in
-      let inactive_doms =
-       Array.to_list (C.list_defined_domains conn nr_inactive_doms) in
-      let inactive_doms = List.map (D.lookup_by_name conn) inactive_doms in
-      active_doms @ inactive_doms in
+      if not !all then
+       active_doms
+      else (
+       let nr_inactive_doms = C.num_of_defined_domains conn in
+       let inactive_doms =
+         Array.to_list (C.list_defined_domains conn nr_inactive_doms) in
+       let inactive_doms = List.map (D.lookup_by_name conn) inactive_doms in
+       active_doms @ inactive_doms
+      ) in
 
     (* Get their XML. *)
     let xmls = List.map D.get_xml_desc doms in
@@ -460,6 +480,16 @@ OPTIONS" in
        { dom_name = name; dom_id = domid; dom_disks = disks }
     ) xmls in
 
+  (* Print the title. *)
+  let () =
+    let total, used, avail =
+      match !inodes, !human with
+      | false, false -> "1K-blocks", "Used", "Available"
+      | false, true -> "Size", "Used", "Available"
+      | true, _ -> "Inodes", "IUse", "IFree" in
+    printf "%-20s %10s %10s %10s %s\n%!"
+      "Filesystem" total used avail "Type" in
+
   (* Probe the devices. *)
   List.iter (
     fun { dom_name = dom_name; dom_disks = dom_disks } ->