X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=tools%2Fvirt-df;h=61fcfb8e76000dd600c412e65cdf3f023e9092a5;hb=f48cd1f262312ad278a293a20ab442dcfc076a69;hp=15512216e77dccedd5fe722d662bd9f86a86688f;hpb=a80177cc847102d5a6e43a8ff87769e86f30bd6c;p=libguestfs.git diff --git a/tools/virt-df b/tools/virt-df index 1551221..61fcfb8 100755 --- a/tools/virt-df +++ b/tools/virt-df @@ -112,6 +112,23 @@ read L below. =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 $human; =item B<--human-readable> | B<-h> @@ -164,6 +181,7 @@ GetOptions ("help|?" => \$help, "version" => \$version, "connect|c=s" => \$uri, "csv" => \$csv, + "format=s" => \$format, "human-readable|human|h" => \$human, "inodes|i" => \$inodes, "one-per-guest" => \$one_per_guest, @@ -229,7 +247,7 @@ if (@ARGV == 0) { # No params, use libvirt. } elsif (@ARGV == 1) { # One param, could be disk image or domname. if (-e $ARGV[0]) { push @domains, { name => basename ($ARGV[0]), - disks => [ $ARGV[0] ] } + disks => [ [ $ARGV[0], $format ] ] } } else { my $conn; @@ -248,8 +266,9 @@ if (@ARGV == 0) { # No params, use libvirt. disks => \@disks } } } else { # >= 2 params, all disk images. + my @disks = map { [ $_, $format ] } @ARGV; push @domains, { name => basename ($ARGV[0]), - disks => \@ARGV } + disks => \@disks } } sub get_disks_from_libvirt @@ -258,12 +277,29 @@ sub get_disks_from_libvirt my $xml = $dom->get_xml_description (); my $p = XML::XPath->new (xml => $xml); - my @disks = $p->findnodes ('//devices/disk/source/@dev'); - push (@disks, $p->findnodes ('//devices/disk/source/@file')); + my $nodes = $p->find ('//devices/disk'); + + my @disks; + my $node; + foreach $node ($nodes->get_nodelist) { + # The filename can be in dev or file attribute, hence: + my $filename = $p->find ('./source/@dev', $node); + unless ($filename) { + $filename = $p->find ('./source/@file', $node); + next unless $filename; + } + $filename = $filename->to_literal; + + # Get the disk format (may not be set). + my $format = $p->find ('./driver/@type', $node); + $format = $format->to_literal if $format; + + push @disks, [ $filename, $format ]; + } # Code in Sys::Guestfs::Lib dies here if there are no disks at all. - return map { $_->getData } @disks; + return @disks; } # Sort the domains by name for display. @@ -312,7 +348,12 @@ sub multi_df foreach $d (@_) { foreach $disk (@{$d->{disks}}) { - $g->add_drive_ro ($disk); + my $filename = $disk->[0]; + my $format = $disk->[1]; + my @args = ($filename); + push @args, readonly => 1; + push @args, format => $format if defined $format; + $g->add_drive_opts (@args); } } @@ -473,6 +514,7 @@ sub print_title printf "%-36s%10s %10s %10s %5s\n", $cols[1], $cols[2], $cols[3], $cols[4], $cols[5]; } else { + # Columns don't need special CSV quoting. print (join (",", @cols), "\n"); } } @@ -491,7 +533,11 @@ sub print_cols printf ("%10s %10s %10s %5s\n", $_[2], $_[3], $_[4], $percent); } else { - printf ("\"%s\",\"%s\",%d,%d,%d,%.1f%%\n", @_); + # Need to quote libvirt domain and filesystem. + my $dom = shift; + my $fs = shift; + print csv_quote($dom), ",", csv_quote($fs), ","; + printf ("%d,%d,%d,%.1f%%\n", @_); } } @@ -509,6 +555,31 @@ sub human_size } } +# Quote field for CSV without using an external module. +sub csv_quote +{ + local $_ = shift; + + my $needs_quoting = /[ ",\n\0]/; + return $_ unless $needs_quoting; + + my $i; + my $out = '"'; + for ($i = 0; $i < length; ++$i) { + my $c = substr $_, $i, 1; + if ($c eq '"') { + $out .= '""'; + } elsif ($c eq '\0') { + $out .= '"0'; + } else { + $out .= $c; + } + } + $out .= '"'; + + return $out; +} + =head1 NOTE ABOUT CSV FORMAT Comma-separated values (CSV) is a deceptive format. It I like