3 # Copyright (C) 2009-2011 Red Hat Inc.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 use Sys::Guestfs::Lib qw(open_guest);
26 use File::Temp qw/tempfile/;
28 use Locale::TextDomain 'libguestfs';
34 virt-edit - Edit a file in a virtual machine
38 virt-edit [--options] domname file
40 virt-edit [--options] disk.img [disk.img ...] file
42 virt-edit [domname|disk.img] file -e 'expr'
46 You must I<not> use C<virt-edit> on live virtual machines. If you do
47 this, you risk disk corruption in the VM. C<virt-edit> tries to stop
48 you from doing this, but doesn't catch all cases.
52 C<virt-edit> is a command line tool to edit C<file> where C<file>
53 exists in the named virtual machine (or disk image).
55 If you want to just view a file, use L<virt-cat(1)>.
57 For more complex cases you should look at the L<guestfish(1)> tool
58 (see L</USING GUESTFISH> below).
60 C<virt-edit> cannot be used to create a new file, nor to edit
61 multiple files. L<guestfish(1)> can do that and much more.
65 Edit the named files interactively:
67 virt-edit mydomain /boot/grub/grub.conf
69 virt-edit mydomain /etc/passwd
71 You can also edit files non-interactively (see
72 L</NON-INTERACTIVE EDITING> below).
73 To change the init default level to 5:
75 virt-edit mydomain /etc/inittab -e 's/^id:.*/id:5:initdefault:/'
95 Display version number and exit.
101 =item B<--backup extension> | B<-b extension>
103 Create a backup of the original file I<in the guest disk image>.
104 The backup has the original filename with C<extension> added.
106 Usually the first character of C<extension> would be a dot C<.>
109 virt-edit -b .orig [etc]
111 By default, no backup file is made.
117 =item B<--connect URI> | B<-c URI>
119 If using libvirt, connect to the given I<URI>. If omitted, then we
120 connect to the default libvirt hypervisor.
122 If you specify guest block devices directly, then libvirt is not used
129 =item B<--format> raw
131 Specify the format of disk images given on the command line. If this
132 is omitted then the format is autodetected from the content of the
135 If disk images are requested from libvirt, then this program asks
136 libvirt for this information. In this case, the value of the format
137 parameter is ignored.
139 If working with untrusted raw-format guest disk images, you should
140 ensure the format is always specified.
146 =item B<--expr EXPR> | B<-e EXPR>
148 Instead of launching the external editor, non-interactively
149 apply the Perl expression C<EXPR> to each line in the file.
150 See L</NON-INTERACTIVE EDITING> below.
152 Be careful to properly quote the expression to prevent it from
153 being altered by the shell.
159 GetOptions ("help|?" => \$help,
160 "version" => \$version,
161 "connect|c=s" => \$uri,
162 "format=s" => \$format,
163 "expr|e=s" => \$expr,
164 "backup|b=s" => \$backup,
166 pod2usage (1) if $help;
168 my $g = Sys::Guestfs->new ();
169 my %h = $g->version ();
170 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
174 pod2usage (__"virt-edit: no image, VM names or filenames to edit given")
177 my $filename = pop @ARGV;
181 $g = open_guest (\@ARGV, address => $uri, rw => 1, format => $format);
183 $g = open_guest (\@ARGV, rw => 1, format => $format);
188 my @roots = $g->inspect_os ();
190 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",
191 prog => basename ($0));
194 die __x("{prog}: multiboot operating systems are not supported.\n",
195 prog => basename ($0))
197 my $root = $roots[0];
198 my %fses = $g->inspect_get_mountpoints ($root);
199 my @fses = sort { length $a <=> length $b } keys %fses;
201 $g->mount_options ("", $fses{$_}, $_);
204 my ($fh, $tempname) = tempfile (UNLINK => 1);
205 my $fddev = "/dev/fd/" . fileno ($fh);
207 # Allow this to fail in case eg. the file does not exist.
208 $g->download ($filename, $fddev);
210 close $fh or die "close: $!";
212 my $do_upload = $tempname;
214 if (!defined $expr) {
215 # Interactively edit the file.
216 my $oldctime = (stat ($tempname))[10];
218 my $editor = $ENV{EDITOR};
220 system ("$editor $tempname") == 0
221 or die "edit failed: $editor: $?";
223 my $newctime = (stat ($tempname))[10];
225 if ($oldctime == $newctime) {
227 print __"File not changed.\n";
230 my ($fh, $tempout) = tempfile (UNLINK => 1);
232 # Apply a Perl expression to the lines of the file.
233 open IFILE, $tempname or die "$tempname: $!";
239 print $fh $_ or die "print: $!";
241 close $fh or die "close: $!";
243 $do_upload = $tempout;
246 if (defined $do_upload) {
247 # Upload to a new file, so if it fails we don't end up with
248 # a partially written file. Give the new file a completely
249 # random name so we have only a tiny chance of overwriting
250 # some existing file.
251 my $dirname = $filename;
252 $dirname =~ s{/[^/]+$}{/};
254 my @chars = ('a'..'z', 'A'..'Z', '0'..'9');
255 my $newname = $dirname;
257 $newname .= $chars[rand @chars];
260 $g->upload ($do_upload, $newname);
262 # Backup or overwrite?
263 $g->mv ($filename, "$filename$backup") if defined $backup;
264 $g->mv ($newname, $filename);
274 =head1 NON-INTERACTIVE EDITING
276 C<virt-edit> normally calls out to C<$EDITOR> (or vi) so
277 the system administrator can interactively edit the file.
279 There are two ways also to use C<virt-edit> from scripts in order to
280 make automated edits to files. (Note that although you I<can> use
281 C<virt-edit> like this, it's less error-prone to write scripts
282 directly using the libguestfs API and Augeas for configuration file
285 The first method is to temporarily set C<$EDITOR> to any script or
286 program you want to run. The script is invoked as C<$EDITOR tmpfile>
287 and it should update C<tmpfile> in place however it likes.
289 The second method is to use the C<-e> parameter of C<virt-edit> to run
290 a short Perl snippet in the style of L<sed(1)>. For example to
291 replace all instances of C<foo> with C<bar> in a file:
293 virt-edit domname filename -e 's/foo/bar/'
295 The full power of Perl regular expressions can be used (see
296 L<perlre(1)>). For example to delete root's password you could do:
298 virt-edit domname /etc/passwd -e 's/^root:.*?:/root::/'
300 What really happens is that the snippet is evaluated as a Perl
301 expression for each line of the file. The line, including the final
302 C<\n>, is passed in C<$_> and the expression should update C<$_> or
305 To delete a line, set C<$_> to the empty string. For example, to
306 delete the C<apache> user account from the password file you can do:
308 virt-edit mydomain /etc/passwd -e '$_ = "" if /^apache:/'
310 To insert a line, prepend or append it to C<$_>. However appending
311 lines to the end of the file is rather difficult this way since there
312 is no concept of "last line of the file" - your expression just
313 doesn't get called again. You might want to use the first method
314 (setting C<$EDITOR>) if you want to do this.
316 The variable C<$lineno> contains the current line number.
317 As is traditional, the first line in the file is number C<1>.
319 The return value from the expression is ignored, but the expression
320 may call C<die> in order to abort the whole program, leaving the
321 original file untouched.
323 Remember when matching the end of a line that C<$_> may contain the
324 final C<\n>, or (for DOS files) C<\r\n>, or if the file does not end
325 with a newline then neither of these. Thus to match or substitute
326 some text at the end of a line, use this regular expression:
330 Alternately, use the perl C<chomp> function, being careful not to
331 chomp C<$_> itself (since that would remove all newlines from the
334 my $m = $_; chomp $m; $m =~ /some text$/
336 =head1 USING GUESTFISH
338 L<guestfish(1)> is a more powerful, lower level tool which you can use
339 when C<virt-edit> doesn't work.
341 Using C<virt-edit> is approximately equivalent to doing:
343 guestfish --rw -i -d domname edit /file
345 where C<domname> is the name of the libvirt guest, and C</file> is the
346 full path to the file.
348 The command above uses libguestfs's guest inspection feature and so
349 does not work on guests that libguestfs cannot inspect, or on things
350 like arbitrary disk images that don't contain guests. To edit a file
351 on a disk image directly, use:
353 guestfish --rw -a disk.img -m /dev/sda1 edit /file
355 where C<disk.img> is the disk image, C</dev/sda1> is the filesystem
356 within the disk image to edit, and C</file> is the full path to the
359 C<virt-edit> cannot create new files. Use the guestfish commands
360 C<touch>, C<write> or C<upload> instead:
362 guestfish --rw -i -d domname touch /newfile
364 guestfish --rw -i -d domname write /newfile "new content"
366 guestfish --rw -i -d domname upload localfile /newfile
368 C<virt-edit> cannot edit multiple files, but guestfish can
371 guestfish --rw -i -d domname edit /file1 : edit /file2
373 =head1 ENVIRONMENT VARIABLES
379 If set, this string is used as the editor. It may contain arguments,
382 If not set, C<vi> is used.
388 Libvirt guest names can contain arbitrary characters, some of which
389 have meaning to the shell such as C<#> and space. You may need to
390 quote or escape these characters on the command line. See the shell
391 manual page L<sh(1)> for details.
399 L<Sys::Guestfs::Lib(3)>,
401 L<http://libguestfs.org/>,
407 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
411 Copyright (C) 2009-2011 Red Hat Inc.
413 This program is free software; you can redistribute it and/or modify
414 it under the terms of the GNU General Public License as published by
415 the Free Software Foundation; either version 2 of the License, or
416 (at your option) any later version.
418 This program is distributed in the hope that it will be useful,
419 but WITHOUT ANY WARRANTY; without even the implied warranty of
420 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
421 GNU General Public License for more details.
423 You should have received a copy of the GNU General Public License
424 along with this program; if not, write to the Free Software
425 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.