7f474c96a9796ee155115ae9ddcc25d6cad7b9f7
[libguestfs.git] / tools / virt-edit
1 #!/usr/bin/perl -w
2 # virt-edit
3 # Copyright (C) 2009-2011 Red Hat Inc.
4 #
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.
9 #
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.
14 #
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.
18
19 use warnings;
20 use strict;
21
22 use Sys::Guestfs;
23 use Sys::Guestfs::Lib qw(open_guest);
24 use Pod::Usage;
25 use Getopt::Long;
26 use File::Temp qw/tempfile/;
27 use File::Basename;
28 use Locale::TextDomain 'libguestfs';
29
30 =encoding utf8
31
32 =head1 NAME
33
34 virt-edit - Edit a file in a virtual machine
35
36 =head1 SYNOPSIS
37
38  virt-edit [--options] domname file
39
40  virt-edit [--options] disk.img [disk.img ...] file
41
42  virt-edit [domname|disk.img] file -e 'expr'
43
44 =head1 WARNING
45
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.
49
50 =head1 DESCRIPTION
51
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).
54
55 If you want to just view a file, use L<virt-cat(1)>.
56
57 For more complex cases you should look at the L<guestfish(1)> tool
58 (see L</USING GUESTFISH> below).
59
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.
62
63 =head1 EXAMPLES
64
65 Edit the named files interactively:
66
67  virt-edit mydomain /boot/grub/grub.conf
68
69  virt-edit mydomain /etc/passwd
70
71 You can also edit files non-interactively (see
72 L</NON-INTERACTIVE EDITING> below).
73 To change the init default level to 5:
74
75  virt-edit mydomain /etc/inittab -e 's/^id:.*/id:5:initdefault:/'
76
77 =head1 OPTIONS
78
79 =over 4
80
81 =cut
82
83 my $help;
84
85 =item B<--help>
86
87 Display brief help.
88
89 =cut
90
91 my $version;
92
93 =item B<--version>
94
95 Display version number and exit.
96
97 =cut
98
99 my $backup;
100
101 =item B<--backup extension> | B<-b extension>
102
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.
105
106 Usually the first character of C<extension> would be a dot C<.>
107 so you would write:
108
109  virt-edit -b .orig [etc]
110
111 By default, no backup file is made.
112
113 =cut
114
115 my $uri;
116
117 =item B<--connect URI> | B<-c URI>
118
119 If using libvirt, connect to the given I<URI>.  If omitted, then we
120 connect to the default libvirt hypervisor.
121
122 If you specify guest block devices directly, then libvirt is not used
123 at all.
124
125 =cut
126
127 my $format;
128
129 =item B<--format> raw
130
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
133 disk image.
134
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.
138
139 If working with untrusted raw-format guest disk images, you should
140 ensure the format is always specified.
141
142 =cut
143
144 my $expr;
145
146 =item B<--expr EXPR> | B<-e EXPR>
147
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.
151
152 Be careful to properly quote the expression to prevent it from
153 being altered by the shell.
154
155 =back
156
157 =cut
158
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,
165     ) or pod2usage (2);
166 pod2usage (1) if $help;
167 if ($version) {
168     my $g = Sys::Guestfs->new ();
169     my %h = $g->version ();
170     print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
171     exit
172 }
173
174 pod2usage (__"virt-edit: no image, VM names or filenames to edit given")
175     if @ARGV <= 1;
176
177 my $filename = pop @ARGV;
178
179 my $g;
180 if ($uri) {
181     $g = open_guest (\@ARGV, address => $uri, rw => 1, format => $format);
182 } else {
183     $g = open_guest (\@ARGV, rw => 1, format => $format);
184 }
185
186 $g->launch ();
187
188 my @roots = $g->inspect_os ();
189 if (@roots == 0) {
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));
192 }
193 if (@roots > 1) {
194     die __x("{prog}: multiboot operating systems are not supported.\n",
195             prog => basename ($0))
196 }
197 my $root = $roots[0];
198 my %fses = $g->inspect_get_mountpoints ($root);
199 my @fses = sort { length $a <=> length $b } keys %fses;
200 foreach (@fses) {
201     $g->mount_options ("", $fses{$_}, $_);
202 }
203
204 my ($fh, $tempname) = tempfile (UNLINK => 1);
205 my $fddev = "/dev/fd/" . fileno ($fh);
206
207 # Allow this to fail in case eg. the file does not exist.
208 $g->download ($filename, $fddev);
209
210 close $fh or die "close: $!";
211
212 my $do_upload = $tempname;
213
214 if (!defined $expr) {
215     # Interactively edit the file.
216     my $oldctime = (stat ($tempname))[10];
217
218     my $editor = $ENV{EDITOR};
219     $editor ||= "vi";
220     system ("$editor $tempname") == 0
221         or die "edit failed: $editor: $?";
222
223     my $newctime = (stat ($tempname))[10];
224
225     if ($oldctime == $newctime) {
226         $do_upload = undef;
227         print __"File not changed.\n";
228     }
229 } else {
230     my ($fh, $tempout) = tempfile (UNLINK => 1);
231
232     # Apply a Perl expression to the lines of the file.
233     open IFILE, $tempname or die "$tempname: $!";
234     my $lineno = 0;
235     while (<IFILE>) {
236         $lineno++;
237         eval $expr;
238         die if $@;
239         print $fh $_ or die "print: $!";
240     }
241     close $fh or die "close: $!";
242
243     $do_upload = $tempout;
244 }
245
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{/[^/]+$}{/};
253
254     my @chars = ('a'..'z', 'A'..'Z', '0'..'9');
255     my $newname = $dirname;
256     foreach (0..7) {
257         $newname .= $chars[rand @chars];
258     }
259
260     $g->upload ($do_upload, $newname);
261
262     # Backup or overwrite?
263     $g->mv ($filename, "$filename$backup") if defined $backup;
264     $g->mv ($newname, $filename);
265
266     $g->umount_all ();
267     $g->sync ();
268 }
269
270 undef $g;
271
272 =head1 NON-INTERACTIVE EDITING
273
274 C<virt-edit> normally calls out to C<$EDITOR> (or vi) so
275 the system administrator can interactively edit the file.
276
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
281 editing.)
282
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.
286
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:
290
291  virt-edit domname filename -e 's/foo/bar/'
292
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:
295
296  virt-edit domname /etc/passwd -e 's/^root:.*?:/root::/'
297
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
301 leave it unchanged.
302
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:
305
306  virt-edit mydomain /etc/passwd -e '$_ = "" if /^apache:/'
307
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.
313
314 The variable C<$lineno> contains the current line number.
315 As is traditional, the first line in the file is number C<1>.
316
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.
320
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:
325
326  /some text(\r?\n)?$/
327
328 Alternately, use the perl C<chomp> function, being careful not to
329 chomp C<$_> itself (since that would remove all newlines from the
330 file):
331
332  my $m = $_; chomp $m; $m =~ /some text$/
333
334 =head1 USING GUESTFISH
335
336 L<guestfish(1)> is a more powerful, lower level tool which you can use
337 when C<virt-edit> doesn't work.
338
339 Using C<virt-edit> is approximately equivalent to doing:
340
341  guestfish --rw -i -d domname edit /file
342
343 where C<domname> is the name of the libvirt guest, and C</file> is the
344 full path to the file.
345
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:
350
351  guestfish --rw -a disk.img -m /dev/sda1 edit /file
352
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
355 file.
356
357 C<virt-edit> cannot create new files.  Use the guestfish commands
358 C<touch>, C<write> or C<upload> instead:
359
360  guestfish --rw -i -d domname touch /newfile
361
362  guestfish --rw -i -d domname write /newfile "new content"
363
364  guestfish --rw -i -d domname upload localfile /newfile
365
366 C<virt-edit> cannot edit multiple files, but guestfish can
367 do it like this:
368
369  guestfish --rw -i -d domname edit /file1 : edit /file2
370
371 =cut
372
373 exit 0;
374
375 =head1 ENVIRONMENT VARIABLES
376
377 =over 4
378
379 =item C<EDITOR>
380
381 If set, this string is used as the editor.  It may contain arguments,
382 eg. C<"emacs -nw">
383
384 If not set, C<vi> is used.
385
386 =back
387
388 =head1 SHELL QUOTING
389
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.
394
395 =head1 SEE ALSO
396
397 L<guestfs(3)>,
398 L<guestfish(1)>,
399 L<virt-cat(1)>,
400 L<virt-copy-in(1)>,
401 L<virt-tar-in(1)>,
402 L<Sys::Guestfs(3)>,
403 L<Sys::Guestfs::Lib(3)>,
404 L<Sys::Virt(3)>,
405 L<http://libguestfs.org/>,
406 L<perl(1)>,
407 L<perlre(1)>.
408
409 =head1 AUTHOR
410
411 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
412
413 =head1 COPYRIGHT
414
415 Copyright (C) 2009-2011 Red Hat Inc.
416
417 This program is free software; you can redistribute it and/or modify
418 it under the terms of the GNU General Public License as published by
419 the Free Software Foundation; either version 2 of the License, or
420 (at your option) any later version.
421
422 This program is distributed in the hope that it will be useful,
423 but WITHOUT ANY WARRANTY; without even the implied warranty of
424 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
425 GNU General Public License for more details.
426
427 You should have received a copy of the GNU General Public License
428 along with this program; if not, write to the Free Software
429 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.