mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-22 07:03:38 +00:00
Update the following tools to use the C API for inspection: - virt-cat - virt-edit - virt-ls - virt-tar - virt-win-reg None of the tools in the tools/ directory now use the deprecated Perl inspection APIs.
383 lines
10 KiB
Perl
Executable File
383 lines
10 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
# virt-edit
|
|
# 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/tempfile/;
|
|
use File::Basename;
|
|
use Locale::TextDomain 'libguestfs';
|
|
|
|
=encoding utf8
|
|
|
|
=head1 NAME
|
|
|
|
virt-edit - Edit a file in a virtual machine
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
virt-edit [--options] domname file
|
|
|
|
virt-edit [--options] disk.img [disk.img ...] file
|
|
|
|
virt-edit [domname|disk.img] file -e 'expr'
|
|
|
|
=head1 WARNING
|
|
|
|
You must I<not> use C<virt-edit> on live virtual machines. If you do
|
|
this, you risk disk corruption in the VM. C<virt-edit> tries to stop
|
|
you from doing this, but doesn't catch all cases.
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
C<virt-edit> is a command line tool to edit C<file> where C<file>
|
|
exists in the named virtual machine (or disk image).
|
|
|
|
If you want to just view a file, use L<virt-cat(1)>. For more complex
|
|
cases you should look at the L<guestfish(1)> tool.
|
|
|
|
=head1 EXAMPLES
|
|
|
|
Edit the named files interactively:
|
|
|
|
virt-edit mydomain /boot/grub/grub.conf
|
|
|
|
virt-edit mydomain /etc/passwd
|
|
|
|
You can also edit files non-interactively (see
|
|
L</NON-INTERACTIVE EDITING> below).
|
|
To change the init default level to 5:
|
|
|
|
virt-edit mydomain /etc/inittab -e 's/^id:.*/id:5:initdefault:/'
|
|
|
|
=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 $backup;
|
|
|
|
=item B<--backup extension> | B<-b extension>
|
|
|
|
Create a backup of the original file I<in the guest disk image>.
|
|
The backup has the original filename with C<extension> added.
|
|
|
|
Usually the first character of C<extension> would be a dot C<.>
|
|
so you would write:
|
|
|
|
virt-edit -b .orig [etc]
|
|
|
|
By default, no backup file is made.
|
|
|
|
=cut
|
|
|
|
my $uri;
|
|
|
|
=item B<--connect URI> | B<-c 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, 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 $expr;
|
|
|
|
=item B<--expr EXPR> | B<-e EXPR>
|
|
|
|
Instead of launching the external editor, non-interactively
|
|
apply the Perl expression C<EXPR> to each line in the file.
|
|
See L</NON-INTERACTIVE EDITING> below.
|
|
|
|
Be careful to properly quote the expression to prevent it from
|
|
being altered by the shell.
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
GetOptions ("help|?" => \$help,
|
|
"version" => \$version,
|
|
"connect|c=s" => \$uri,
|
|
"format=s" => \$format,
|
|
"expr|e=s" => \$expr,
|
|
"backup|b=s" => \$backup,
|
|
) 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-edit: no image, VM names or filenames to edit given")
|
|
if @ARGV <= 1;
|
|
|
|
my $filename = pop @ARGV;
|
|
|
|
my $g;
|
|
if ($uri) {
|
|
$g = open_guest (\@ARGV, address => $uri, rw => 1, format => $format);
|
|
} else {
|
|
$g = open_guest (\@ARGV, rw => 1, 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_options ("", $fses{$_}, $_);
|
|
}
|
|
|
|
my ($fh, $tempname) = tempfile (UNLINK => 1);
|
|
my $fddev = "/dev/fd/" . fileno ($fh);
|
|
|
|
# Allow this to fail in case eg. the file does not exist.
|
|
$g->download ($filename, $fddev);
|
|
|
|
close $fh or die "close: $!";
|
|
|
|
my $do_upload = $tempname;
|
|
|
|
if (!defined $expr) {
|
|
# Interactively edit the file.
|
|
my $oldctime = (stat ($tempname))[10];
|
|
|
|
my $editor = $ENV{EDITOR};
|
|
$editor ||= "vi";
|
|
system ("$editor $tempname") == 0
|
|
or die "edit failed: $editor: $?";
|
|
|
|
my $newctime = (stat ($tempname))[10];
|
|
|
|
if ($oldctime == $newctime) {
|
|
$do_upload = undef;
|
|
print __"File not changed.\n";
|
|
}
|
|
} else {
|
|
my ($fh, $tempout) = tempfile (UNLINK => 1);
|
|
|
|
# Apply a Perl expression to the lines of the file.
|
|
open IFILE, $tempname or die "$tempname: $!";
|
|
my $lineno = 0;
|
|
while (<IFILE>) {
|
|
$lineno++;
|
|
eval $expr;
|
|
die if $@;
|
|
print $fh $_ or die "print: $!";
|
|
}
|
|
close $fh or die "close: $!";
|
|
|
|
$do_upload = $tempout;
|
|
}
|
|
|
|
if (defined $do_upload) {
|
|
# Upload to a new file, so if it fails we don't end up with
|
|
# a partially written file. Give the new file a completely
|
|
# random name so we have only a tiny chance of overwriting
|
|
# some existing file.
|
|
my $dirname = $filename;
|
|
$dirname =~ s{/[^/]+$}{/};
|
|
|
|
my @chars = ('a'..'z', 'A'..'Z', '0'..'9');
|
|
my $newname = $dirname;
|
|
foreach (0..7) {
|
|
$newname .= $chars[rand @chars];
|
|
}
|
|
|
|
$g->upload ($do_upload, $newname);
|
|
|
|
# Backup or overwrite?
|
|
$g->mv ($filename, "$filename$backup") if defined $backup;
|
|
$g->mv ($newname, $filename);
|
|
|
|
$g->umount_all ();
|
|
$g->sync ();
|
|
}
|
|
|
|
undef $g;
|
|
|
|
exit 0;
|
|
|
|
=head1 NON-INTERACTIVE EDITING
|
|
|
|
C<virt-edit> normally calls out to C<$EDITOR> (or vi) so
|
|
the system administrator can interactively edit the file.
|
|
|
|
There are two ways also to use C<virt-edit> from scripts in order to
|
|
make automated edits to files. (Note that although you I<can> use
|
|
C<virt-edit> like this, it's less error-prone to write scripts
|
|
directly using the libguestfs API and Augeas for configuration file
|
|
editing.)
|
|
|
|
The first method is to temporarily set C<$EDITOR> to any script or
|
|
program you want to run. The script is invoked as C<$EDITOR tmpfile>
|
|
and it should update C<tmpfile> in place however it likes.
|
|
|
|
The second method is to use the C<-e> parameter of C<virt-edit> to run
|
|
a short Perl snippet in the style of L<sed(1)>. For example to
|
|
replace all instances of C<foo> with C<bar> in a file:
|
|
|
|
virt-edit domname filename -e 's/foo/bar/'
|
|
|
|
The full power of Perl regular expressions can be used (see
|
|
L<perlre(1)>). For example to delete root's password you could do:
|
|
|
|
virt-edit domname /etc/passwd -e 's/^root:.*?:/root::/'
|
|
|
|
What really happens is that the snippet is evaluated as a Perl
|
|
expression for each line of the file. The line, including the final
|
|
C<\n>, is passed in C<$_> and the expression should update C<$_> or
|
|
leave it unchanged.
|
|
|
|
To delete a line, set C<$_> to the empty string. For example, to
|
|
delete the C<apache> user account from the password file you can do:
|
|
|
|
virt-edit mydomain /etc/passwd -e '$_ = "" if /^apache:/'
|
|
|
|
To insert a line, prepend or append it to C<$_>. However appending
|
|
lines to the end of the file is rather difficult this way since there
|
|
is no concept of "last line of the file" - your expression just
|
|
doesn't get called again. You might want to use the first method
|
|
(setting C<$EDITOR>) if you want to do this.
|
|
|
|
The variable C<$lineno> contains the current line number.
|
|
As is traditional, the first line in the file is number C<1>.
|
|
|
|
The return value from the expression is ignored, but the expression
|
|
may call C<die> in order to abort the whole program, leaving the
|
|
original file untouched.
|
|
|
|
Remember when matching the end of a line that C<$_> may contain the
|
|
final C<\n>, or (for DOS files) C<\r\n>, or if the file does not end
|
|
with a newline then neither of these. Thus to match or substitute
|
|
some text at the end of a line, use this regular expression:
|
|
|
|
/some text(\r?\n)?$/
|
|
|
|
Alternately, use the perl C<chomp> function, being careful not to
|
|
chomp C<$_> itself (since that would remove all newlines from the
|
|
file):
|
|
|
|
my $m = $_; chomp $m; $m =~ /some text$/
|
|
|
|
=head1 ENVIRONMENT VARIABLES
|
|
|
|
=over 4
|
|
|
|
=item C<EDITOR>
|
|
|
|
If set, this string is used as the editor. It may contain arguments,
|
|
eg. C<"emacs -nw">
|
|
|
|
If not set, C<vi> is used.
|
|
|
|
=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 SEE ALSO
|
|
|
|
L<guestfs(3)>,
|
|
L<guestfish(1)>,
|
|
L<virt-cat(1)>,
|
|
L<Sys::Guestfs(3)>,
|
|
L<Sys::Guestfs::Lib(3)>,
|
|
L<Sys::Virt(3)>,
|
|
L<http://libguestfs.org/>,
|
|
L<perl(1)>,
|
|
L<perlre(1)>.
|
|
|
|
=head1 AUTHOR
|
|
|
|
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
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.
|