3 # Copyright (C) 2009-2010 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)>. For more complex
56 cases you should look at the L<guestfish(1)> tool.
60 Edit the named files interactively:
62 virt-edit mydomain /boot/grub/grub.conf
64 virt-edit mydomain /etc/passwd
66 You can also edit files non-interactively (see
67 L</NON-INTERACTIVE EDITING> below).
68 To change the init default level to 5:
70 virt-edit mydomain /etc/inittab -e 's/^id:.*/id:5:initdefault:/'
90 Display version number and exit.
96 =item B<--backup extension> | B<-b extension>
98 Create a backup of the original file I<in the guest disk image>.
99 The backup has the original filename with C<extension> added.
101 Usually the first character of C<extension> would be a dot C<.>
104 virt-edit -b .orig [etc]
106 By default, no backup file is made.
112 =item B<--connect URI> | B<-c URI>
114 If using libvirt, connect to the given I<URI>. If omitted, then we
115 connect to the default libvirt hypervisor.
117 If you specify guest block devices directly, then libvirt is not used
124 =item B<--format> raw
126 Specify the format of disk images given on the command line. If this
127 is omitted then the format is autodetected from the content of the
130 If disk images are requested from libvirt, then this program asks
131 libvirt for this information. In this case, the value of the format
132 parameter is ignored.
134 If working with untrusted raw-format guest disk images, you should
135 ensure the format is always specified.
141 =item B<--expr EXPR> | B<-e EXPR>
143 Instead of launching the external editor, non-interactively
144 apply the Perl expression C<EXPR> to each line in the file.
145 See L</NON-INTERACTIVE EDITING> below.
147 Be careful to properly quote the expression to prevent it from
148 being altered by the shell.
154 GetOptions ("help|?" => \$help,
155 "version" => \$version,
156 "connect|c=s" => \$uri,
157 "format=s" => \$format,
158 "expr|e=s" => \$expr,
159 "backup|b=s" => \$backup,
161 pod2usage (1) if $help;
163 my $g = Sys::Guestfs->new ();
164 my %h = $g->version ();
165 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
169 pod2usage (__"virt-edit: no image, VM names or filenames to edit given")
172 my $filename = pop @ARGV;
176 $g = open_guest (\@ARGV, address => $uri, rw => 1, format => $format);
178 $g = open_guest (\@ARGV, rw => 1, format => $format);
183 my @roots = $g->inspect_os ();
185 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",
186 prog => basename ($0));
189 die __x("{prog}: multiboot operating systems are not supported.\n",
190 prog => basename ($0))
192 my %fses = $g->inspect_get_mountpoints ($roots[0]);
193 my @fses = sort { length $a <=> length $b } keys %fses;
195 $g->mount_options ("", $fses{$_}, $_);
198 my ($fh, $tempname) = tempfile (UNLINK => 1);
199 my $fddev = "/dev/fd/" . fileno ($fh);
201 # Allow this to fail in case eg. the file does not exist.
202 $g->download ($filename, $fddev);
204 close $fh or die "close: $!";
206 my $do_upload = $tempname;
208 if (!defined $expr) {
209 # Interactively edit the file.
210 my $oldctime = (stat ($tempname))[10];
212 my $editor = $ENV{EDITOR};
214 system ("$editor $tempname") == 0
215 or die "edit failed: $editor: $?";
217 my $newctime = (stat ($tempname))[10];
219 if ($oldctime == $newctime) {
221 print __"File not changed.\n";
224 my ($fh, $tempout) = tempfile (UNLINK => 1);
226 # Apply a Perl expression to the lines of the file.
227 open IFILE, $tempname or die "$tempname: $!";
233 print $fh $_ or die "print: $!";
235 close $fh or die "close: $!";
237 $do_upload = $tempout;
240 if (defined $do_upload) {
241 # Upload to a new file, so if it fails we don't end up with
242 # a partially written file. Give the new file a completely
243 # random name so we have only a tiny chance of overwriting
244 # some existing file.
245 my $dirname = $filename;
246 $dirname =~ s{/[^/]+$}{/};
248 my @chars = ('a'..'z', 'A'..'Z', '0'..'9');
249 my $newname = $dirname;
251 $newname .= $chars[rand @chars];
254 $g->upload ($do_upload, $newname);
256 # Backup or overwrite?
257 $g->mv ($filename, "$filename$backup") if defined $backup;
258 $g->mv ($newname, $filename);
268 =head1 NON-INTERACTIVE EDITING
270 C<virt-edit> normally calls out to C<$EDITOR> (or vi) so
271 the system administrator can interactively edit the file.
273 There are two ways also to use C<virt-edit> from scripts in order to
274 make automated edits to files. (Note that although you I<can> use
275 C<virt-edit> like this, it's less error-prone to write scripts
276 directly using the libguestfs API and Augeas for configuration file
279 The first method is to temporarily set C<$EDITOR> to any script or
280 program you want to run. The script is invoked as C<$EDITOR tmpfile>
281 and it should update C<tmpfile> in place however it likes.
283 The second method is to use the C<-e> parameter of C<virt-edit> to run
284 a short Perl snippet in the style of L<sed(1)>. For example to
285 replace all instances of C<foo> with C<bar> in a file:
287 virt-edit domname filename -e 's/foo/bar/'
289 The full power of Perl regular expressions can be used (see
290 L<perlre(1)>). For example to delete root's password you could do:
292 virt-edit domname /etc/passwd -e 's/^root:.*?:/root::/'
294 What really happens is that the snippet is evaluated as a Perl
295 expression for each line of the file. The line, including the final
296 C<\n>, is passed in C<$_> and the expression should update C<$_> or
299 To delete a line, set C<$_> to the empty string. For example, to
300 delete the C<apache> user account from the password file you can do:
302 virt-edit mydomain /etc/passwd -e '$_ = "" if /^apache:/'
304 To insert a line, prepend or append it to C<$_>. However appending
305 lines to the end of the file is rather difficult this way since there
306 is no concept of "last line of the file" - your expression just
307 doesn't get called again. You might want to use the first method
308 (setting C<$EDITOR>) if you want to do this.
310 The variable C<$lineno> contains the current line number.
311 As is traditional, the first line in the file is number C<1>.
313 The return value from the expression is ignored, but the expression
314 may call C<die> in order to abort the whole program, leaving the
315 original file untouched.
317 Remember when matching the end of a line that C<$_> may contain the
318 final C<\n>, or (for DOS files) C<\r\n>, or if the file does not end
319 with a newline then neither of these. Thus to match or substitute
320 some text at the end of a line, use this regular expression:
324 Alternately, use the perl C<chomp> function, being careful not to
325 chomp C<$_> itself (since that would remove all newlines from the
328 my $m = $_; chomp $m; $m =~ /some text$/
330 =head1 ENVIRONMENT VARIABLES
336 If set, this string is used as the editor. It may contain arguments,
339 If not set, C<vi> is used.
345 Libvirt guest names can contain arbitrary characters, some of which
346 have meaning to the shell such as C<#> and space. You may need to
347 quote or escape these characters on the command line. See the shell
348 manual page L<sh(1)> for details.
358 L<Sys::Guestfs::Lib(3)>,
360 L<http://libguestfs.org/>,
366 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
370 Copyright (C) 2009-2010 Red Hat Inc.
372 This program is free software; you can redistribute it and/or modify
373 it under the terms of the GNU General Public License as published by
374 the Free Software Foundation; either version 2 of the License, or
375 (at your option) any later version.
377 This program is distributed in the hope that it will be useful,
378 but WITHOUT ANY WARRANTY; without even the implied warranty of
379 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
380 GNU General Public License for more details.
382 You should have received a copy of the GNU General Public License
383 along with this program; if not, write to the Free Software
384 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.