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);
272 =head1 NON-INTERACTIVE EDITING
274 C<virt-edit> normally calls out to C<$EDITOR> (or vi) so
275 the system administrator can interactively edit the file.
277 There are two ways also to use C<virt-edit> from scripts in order to
278 make automated edits to files. (Note that although you I<can> use
279 C<virt-edit> like this, it's less error-prone to write scripts
280 directly using the libguestfs API and Augeas for configuration file
283 The first method is to temporarily set C<$EDITOR> to any script or
284 program you want to run. The script is invoked as C<$EDITOR tmpfile>
285 and it should update C<tmpfile> in place however it likes.
287 The second method is to use the C<-e> parameter of C<virt-edit> to run
288 a short Perl snippet in the style of L<sed(1)>. For example to
289 replace all instances of C<foo> with C<bar> in a file:
291 virt-edit domname filename -e 's/foo/bar/'
293 The full power of Perl regular expressions can be used (see
294 L<perlre(1)>). For example to delete root's password you could do:
296 virt-edit domname /etc/passwd -e 's/^root:.*?:/root::/'
298 What really happens is that the snippet is evaluated as a Perl
299 expression for each line of the file. The line, including the final
300 C<\n>, is passed in C<$_> and the expression should update C<$_> or
303 To delete a line, set C<$_> to the empty string. For example, to
304 delete the C<apache> user account from the password file you can do:
306 virt-edit mydomain /etc/passwd -e '$_ = "" if /^apache:/'
308 To insert a line, prepend or append it to C<$_>. However appending
309 lines to the end of the file is rather difficult this way since there
310 is no concept of "last line of the file" - your expression just
311 doesn't get called again. You might want to use the first method
312 (setting C<$EDITOR>) if you want to do this.
314 The variable C<$lineno> contains the current line number.
315 As is traditional, the first line in the file is number C<1>.
317 The return value from the expression is ignored, but the expression
318 may call C<die> in order to abort the whole program, leaving the
319 original file untouched.
321 Remember when matching the end of a line that C<$_> may contain the
322 final C<\n>, or (for DOS files) C<\r\n>, or if the file does not end
323 with a newline then neither of these. Thus to match or substitute
324 some text at the end of a line, use this regular expression:
328 Alternately, use the perl C<chomp> function, being careful not to
329 chomp C<$_> itself (since that would remove all newlines from the
332 my $m = $_; chomp $m; $m =~ /some text$/
334 =head1 USING GUESTFISH
336 L<guestfish(1)> is a more powerful, lower level tool which you can use
337 when C<virt-edit> doesn't work.
339 Using C<virt-edit> is approximately equivalent to doing:
341 guestfish --rw -i -d domname edit /file
343 where C<domname> is the name of the libvirt guest, and C</file> is the
344 full path to the file.
346 The command above uses libguestfs's guest inspection feature and so
347 does not work on guests that libguestfs cannot inspect, or on things
348 like arbitrary disk images that don't contain guests. To edit a file
349 on a disk image directly, use:
351 guestfish --rw -a disk.img -m /dev/sda1 edit /file
353 where C<disk.img> is the disk image, C</dev/sda1> is the filesystem
354 within the disk image to edit, and C</file> is the full path to the
357 C<virt-edit> cannot create new files. Use the guestfish commands
358 C<touch>, C<write> or C<upload> instead:
360 guestfish --rw -i -d domname touch /newfile
362 guestfish --rw -i -d domname write /newfile "new content"
364 guestfish --rw -i -d domname upload localfile /newfile
366 C<virt-edit> cannot edit multiple files, but guestfish can
369 guestfish --rw -i -d domname edit /file1 : edit /file2
375 =head1 ENVIRONMENT VARIABLES
381 If set, this string is used as the editor. It may contain arguments,
384 If not set, C<vi> is used.
390 Libvirt guest names can contain arbitrary characters, some of which
391 have meaning to the shell such as C<#> and space. You may need to
392 quote or escape these characters on the command line. See the shell
393 manual page L<sh(1)> for details.
401 L<Sys::Guestfs::Lib(3)>,
403 L<http://libguestfs.org/>,
409 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
413 Copyright (C) 2009-2011 Red Hat Inc.
415 This program is free software; you can redistribute it and/or modify
416 it under the terms of the GNU General Public License as published by
417 the Free Software Foundation; either version 2 of the License, or
418 (at your option) any later version.
420 This program is distributed in the hope that it will be useful,
421 but WITHOUT ANY WARRANTY; without even the implied warranty of
422 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
423 GNU General Public License for more details.
425 You should have received a copy of the GNU General Public License
426 along with this program; if not, write to the Free Software
427 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.