Ignore launch() error in virt-rescue. (RHBZ#618556)
[libguestfs.git] / tools / virt-edit
1 #!/usr/bin/perl -w
2 # virt-edit
3 # Copyright (C) 2009-2010 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 get_partitions resolve_windows_path
24   inspect_all_partitions inspect_partition
25   inspect_operating_systems mount_operating_system);
26 use Pod::Usage;
27 use Getopt::Long;
28 use File::Temp qw/tempfile/;
29 use Locale::TextDomain 'libguestfs';
30
31 =encoding utf8
32
33 =head1 NAME
34
35 virt-edit - Edit a file in a virtual machine
36
37 =head1 SYNOPSIS
38
39  virt-edit [--options] domname file
40
41  virt-edit [--options] disk.img [disk.img ...] file
42
43  virt-edit [domname|disk.img] file -e 'expr'
44
45 =head1 WARNING
46
47 You must I<not> use C<virt-edit> on live virtual machines.  If you do
48 this, you risk disk corruption in the VM.  C<virt-edit> tries to stop
49 you from doing this, but doesn't catch all cases.
50
51 =head1 DESCRIPTION
52
53 C<virt-edit> is a command line tool to edit C<file> where C<file>
54 exists in the named virtual machine (or disk image).
55
56 If you want to just view a file, use L<virt-cat(1)>.  For more complex
57 cases you should look at the L<guestfish(1)> tool.
58
59 =head1 EXAMPLES
60
61 Edit the named files interactively:
62
63  virt-edit mydomain /boot/grub/grub.conf
64
65  virt-edit mydomain /etc/passwd
66
67 You can also edit files non-interactively (see
68 L</NON-INTERACTIVE EDITING> below).
69 To change the init default level to 5:
70
71  virt-edit mydomain /etc/inittab -e 's/^id:.*/id:5:initdefault:/'
72
73 =head1 OPTIONS
74
75 =over 4
76
77 =cut
78
79 my $help;
80
81 =item B<--help>
82
83 Display brief help.
84
85 =cut
86
87 my $version;
88
89 =item B<--version>
90
91 Display version number and exit.
92
93 =cut
94
95 my $backup;
96
97 =item B<--backup extension> | B<-b extension>
98
99 Create a backup of the original file I<in the guest disk image>.
100 The backup has the original filename with C<extension> added.
101
102 Usually the first character of C<extension> would be a dot C<.>
103 so you would write:
104
105  virt-edit -b .orig [etc]
106
107 By default, no backup file is made.
108
109 =cut
110
111 my $uri;
112
113 =item B<--connect URI> | B<-c URI>
114
115 If using libvirt, connect to the given I<URI>.  If omitted, then we
116 connect to the default libvirt hypervisor.
117
118 If you specify guest block devices directly, then libvirt is not used
119 at all.
120
121 =cut
122
123 my $expr;
124
125 =item B<--expr EXPR> | B<-e EXPR>
126
127 Instead of launching the external editor, non-interactively
128 apply the Perl expression C<EXPR> to each line in the file.
129 See L</NON-INTERACTIVE EDITING> below.
130
131 Be careful to properly quote the expression to prevent it from
132 being altered by the shell.
133
134 =back
135
136 =cut
137
138 GetOptions ("help|?" => \$help,
139             "version" => \$version,
140             "connect|c=s" => \$uri,
141             "expr|e=s" => \$expr,
142             "backup|b=s" => \$backup,
143     ) or pod2usage (2);
144 pod2usage (1) if $help;
145 if ($version) {
146     my $g = Sys::Guestfs->new ();
147     my %h = $g->version ();
148     print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
149     exit
150 }
151
152 pod2usage (__"virt-edit: no image, VM names or filenames to edit given")
153     if @ARGV <= 1;
154
155 my $filename = pop @ARGV;
156
157 my $g;
158 if ($uri) {
159     $g = open_guest (\@ARGV, address => $uri, rw => 1);
160 } else {
161     $g = open_guest (\@ARGV, rw => 1);
162 }
163
164 $g->launch ();
165
166 # List of possible filesystems.
167 my @partitions = get_partitions ($g);
168
169 # Now query each one to build up a picture of what's in it.
170 my %fses =
171     inspect_all_partitions ($g, \@partitions,
172       use_windows_registry => 0);
173
174 my $oses = inspect_operating_systems ($g, \%fses);
175
176 my @roots = keys %$oses;
177 die __"multiboot operating systems are not supported by virt-edit" if @roots > 1;
178 my $root_dev = $roots[0];
179
180 my $os = $oses->{$root_dev};
181 mount_operating_system ($g, $os, 0);
182
183 my ($fh_not_used, $tempname) = tempfile (UNLINK => 1);
184
185 # Allow this to fail in case eg. the file does not exist.
186 $g->download($filename, $tempname);
187
188 my $do_upload = $tempname;
189
190 if (!defined $expr) {
191     # Interactively edit the file.
192     my $oldctime = (stat ($tempname))[10];
193
194     my $editor = $ENV{EDITOR};
195     $editor ||= "vi";
196     system ("$editor $tempname") == 0
197         or die "edit failed: $editor: $?";
198
199     my $newctime = (stat ($tempname))[10];
200
201     if ($oldctime == $newctime) {
202         $do_upload = undef;
203         print __"File not changed.\n";
204     }
205 } else {
206     my ($fh, $tempout) = tempfile (UNLINK => 1);
207
208     # Apply a Perl expression to the lines of the file.
209     open IFILE, $tempname or die "$tempname: $!";
210     my $lineno = 0;
211     while (<IFILE>) {
212         $lineno++;
213         eval $expr;
214         die if $@;
215         print $fh $_ or die "print: $!";
216     }
217     close $fh;
218
219     $do_upload = $tempout;
220 }
221
222 if (defined $do_upload) {
223     # Upload to a new file, so if it fails we don't end up with
224     # a partially written file.  Give the new file a completely
225     # random name so we have only a tiny chance of overwriting
226     # some existing file.
227     my $dirname = $filename;
228     $dirname =~ s{/[^/]+$}{/};
229
230     my @chars = ('a'..'z', 'A'..'Z', '0'..'9');
231     my $newname = $dirname;
232     foreach (0..7) {
233         $newname .= $chars[rand @chars];
234     }
235
236     $g->upload ($do_upload, $newname);
237
238     # Backup or overwrite?
239     $g->mv ($filename, "$filename$backup") if defined $backup;
240     $g->mv ($newname, $filename);
241
242     $g->umount_all ();
243     $g->sync ();
244 }
245
246 undef $g;
247
248 exit 0;
249
250 =head1 NON-INTERACTIVE EDITING
251
252 C<virt-edit> normally calls out to C<$EDITOR> (or vi) so
253 the system administrator can interactively edit the file.
254
255 There are two ways also to use C<virt-edit> from scripts in order to
256 make automated edits to files.  (Note that although you I<can> use
257 C<virt-edit> like this, it's less error-prone to write scripts
258 directly using the libguestfs API and Augeas for configuration file
259 editing.)
260
261 The first method is to temporarily set C<$EDITOR> to any script or
262 program you want to run.  The script is invoked as C<$EDITOR tmpfile>
263 and it should update C<tmpfile> in place however it likes.
264
265 The second method is to use the C<-e> parameter of C<virt-edit> to run
266 a short Perl snippet in the style of L<sed(1)>.  For example to
267 replace all instances of C<foo> with C<bar> in a file:
268
269  virt-edit domname filename -e 's/foo/bar/'
270
271 The full power of Perl regular expressions can be used (see
272 L<perlre(1)>).  For example to delete root's password you could do:
273
274  virt-edit domname /etc/passwd -e 's/^root:.*?:/root::/'
275
276 What really happens is that the snippet is evaluated as a Perl
277 expression for each line of the file.  The line, including the final
278 C<\n>, is passed in C<$_> and the expression should update C<$_> or
279 leave it unchanged.
280
281 To delete a line, set C<$_> to the empty string.  For example, to
282 delete the C<apache> user account from the password file you can do:
283
284  virt-edit mydomain /etc/passwd -e '$_ = "" if /^apache:/'
285
286 To insert a line, prepend or append it to C<$_>.  However appending
287 lines to the end of the file is rather difficult this way since there
288 is no concept of "last line of the file" - your expression just
289 doesn't get called again.  You might want to use the first method
290 (setting C<$EDITOR>) if you want to do this.
291
292 The variable C<$lineno> contains the current line number.
293 As is traditional, the first line in the file is number C<1>.
294
295 The return value from the expression is ignored, but the expression
296 may call C<die> in order to abort the whole program, leaving the
297 original file untouched.
298
299 Remember when matching the end of a line that C<$_> may contain the
300 final C<\n>, or (for DOS files) C<\r\n>, or if the file does not end
301 with a newline then neither of these.  Thus to match or substitute
302 some text at the end of a line, use this regular expression:
303
304  /some text(\r?\n)?$/
305
306 Alternately, use the perl C<chomp> function, being careful not to
307 chomp C<$_> itself (since that would remove all newlines from the
308 file):
309
310  my $m = $_; chomp $m; $m =~ /some text$/
311
312 =head1 ENVIRONMENT VARIABLES
313
314 =over 4
315
316 =item C<EDITOR>
317
318 If set, this string is used as the editor.  It may contain arguments,
319 eg. C<"emacs -nw">
320
321 If not set, C<vi> is used.
322
323 =back
324
325 =head1 SEE ALSO
326
327 L<guestfs(3)>,
328 L<guestfish(1)>,
329 L<virt-cat(1)>,
330 L<Sys::Guestfs(3)>,
331 L<Sys::Guestfs::Lib(3)>,
332 L<Sys::Virt(3)>,
333 L<http://libguestfs.org/>,
334 L<perl(1)>,
335 L<perlre(1)>.
336
337 =head1 AUTHOR
338
339 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
340
341 =head1 COPYRIGHT
342
343 Copyright (C) 2009-2010 Red Hat Inc.
344
345 This program is free software; you can redistribute it and/or modify
346 it under the terms of the GNU General Public License as published by
347 the Free Software Foundation; either version 2 of the License, or
348 (at your option) any later version.
349
350 This program is distributed in the hope that it will be useful,
351 but WITHOUT ANY WARRANTY; without even the implied warranty of
352 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
353 GNU General Public License for more details.
354
355 You should have received a copy of the GNU General Public License
356 along with this program; if not, write to the Free Software
357 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.