78316fd0a13fbc5072a15d2618bee0839163c4de
[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 $format;
124
125 =item B<--format> raw
126
127 Specify the format of disk images given on the command line.  If this
128 is omitted then the format is autodetected from the content of the
129 disk image.
130
131 If disk images are requested from libvirt, then this program asks
132 libvirt for this information.  In this case, the value of the format
133 parameter is ignored.
134
135 If working with untrusted raw-format guest disk images, you should
136 ensure the format is always specified.
137
138 =cut
139
140 my $expr;
141
142 =item B<--expr EXPR> | B<-e EXPR>
143
144 Instead of launching the external editor, non-interactively
145 apply the Perl expression C<EXPR> to each line in the file.
146 See L</NON-INTERACTIVE EDITING> below.
147
148 Be careful to properly quote the expression to prevent it from
149 being altered by the shell.
150
151 =back
152
153 =cut
154
155 GetOptions ("help|?" => \$help,
156             "version" => \$version,
157             "connect|c=s" => \$uri,
158             "format=s" => \$format,
159             "expr|e=s" => \$expr,
160             "backup|b=s" => \$backup,
161     ) or pod2usage (2);
162 pod2usage (1) if $help;
163 if ($version) {
164     my $g = Sys::Guestfs->new ();
165     my %h = $g->version ();
166     print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
167     exit
168 }
169
170 pod2usage (__"virt-edit: no image, VM names or filenames to edit given")
171     if @ARGV <= 1;
172
173 my $filename = pop @ARGV;
174
175 my $g;
176 if ($uri) {
177     $g = open_guest (\@ARGV, address => $uri, rw => 1, format => $format);
178 } else {
179     $g = open_guest (\@ARGV, rw => 1, format => $format);
180 }
181
182 $g->launch ();
183
184 # List of possible filesystems.
185 my @partitions = get_partitions ($g);
186
187 # Now query each one to build up a picture of what's in it.
188 my %fses =
189     inspect_all_partitions ($g, \@partitions,
190       use_windows_registry => 0);
191
192 my $oses = inspect_operating_systems ($g, \%fses);
193
194 my @roots = keys %$oses;
195 die __"multiboot operating systems are not supported by virt-edit" if @roots > 1;
196 my $root_dev = $roots[0];
197
198 my $os = $oses->{$root_dev};
199 mount_operating_system ($g, $os, 0);
200
201 my ($fh, $tempname) = tempfile (UNLINK => 1);
202 my $fddev = "/dev/fd/" . fileno ($fh);
203
204 # Allow this to fail in case eg. the file does not exist.
205 $g->download ($filename, $fddev);
206
207 close $fh or die "close: $!";
208
209 my $do_upload = $tempname;
210
211 if (!defined $expr) {
212     # Interactively edit the file.
213     my $oldctime = (stat ($tempname))[10];
214
215     my $editor = $ENV{EDITOR};
216     $editor ||= "vi";
217     system ("$editor $tempname") == 0
218         or die "edit failed: $editor: $?";
219
220     my $newctime = (stat ($tempname))[10];
221
222     if ($oldctime == $newctime) {
223         $do_upload = undef;
224         print __"File not changed.\n";
225     }
226 } else {
227     my ($fh, $tempout) = tempfile (UNLINK => 1);
228
229     # Apply a Perl expression to the lines of the file.
230     open IFILE, $tempname or die "$tempname: $!";
231     my $lineno = 0;
232     while (<IFILE>) {
233         $lineno++;
234         eval $expr;
235         die if $@;
236         print $fh $_ or die "print: $!";
237     }
238     close $fh or die "close: $!";
239
240     $do_upload = $tempout;
241 }
242
243 if (defined $do_upload) {
244     # Upload to a new file, so if it fails we don't end up with
245     # a partially written file.  Give the new file a completely
246     # random name so we have only a tiny chance of overwriting
247     # some existing file.
248     my $dirname = $filename;
249     $dirname =~ s{/[^/]+$}{/};
250
251     my @chars = ('a'..'z', 'A'..'Z', '0'..'9');
252     my $newname = $dirname;
253     foreach (0..7) {
254         $newname .= $chars[rand @chars];
255     }
256
257     $g->upload ($do_upload, $newname);
258
259     # Backup or overwrite?
260     $g->mv ($filename, "$filename$backup") if defined $backup;
261     $g->mv ($newname, $filename);
262
263     $g->umount_all ();
264     $g->sync ();
265 }
266
267 undef $g;
268
269 exit 0;
270
271 =head1 NON-INTERACTIVE EDITING
272
273 C<virt-edit> normally calls out to C<$EDITOR> (or vi) so
274 the system administrator can interactively edit the file.
275
276 There are two ways also to use C<virt-edit> from scripts in order to
277 make automated edits to files.  (Note that although you I<can> use
278 C<virt-edit> like this, it's less error-prone to write scripts
279 directly using the libguestfs API and Augeas for configuration file
280 editing.)
281
282 The first method is to temporarily set C<$EDITOR> to any script or
283 program you want to run.  The script is invoked as C<$EDITOR tmpfile>
284 and it should update C<tmpfile> in place however it likes.
285
286 The second method is to use the C<-e> parameter of C<virt-edit> to run
287 a short Perl snippet in the style of L<sed(1)>.  For example to
288 replace all instances of C<foo> with C<bar> in a file:
289
290  virt-edit domname filename -e 's/foo/bar/'
291
292 The full power of Perl regular expressions can be used (see
293 L<perlre(1)>).  For example to delete root's password you could do:
294
295  virt-edit domname /etc/passwd -e 's/^root:.*?:/root::/'
296
297 What really happens is that the snippet is evaluated as a Perl
298 expression for each line of the file.  The line, including the final
299 C<\n>, is passed in C<$_> and the expression should update C<$_> or
300 leave it unchanged.
301
302 To delete a line, set C<$_> to the empty string.  For example, to
303 delete the C<apache> user account from the password file you can do:
304
305  virt-edit mydomain /etc/passwd -e '$_ = "" if /^apache:/'
306
307 To insert a line, prepend or append it to C<$_>.  However appending
308 lines to the end of the file is rather difficult this way since there
309 is no concept of "last line of the file" - your expression just
310 doesn't get called again.  You might want to use the first method
311 (setting C<$EDITOR>) if you want to do this.
312
313 The variable C<$lineno> contains the current line number.
314 As is traditional, the first line in the file is number C<1>.
315
316 The return value from the expression is ignored, but the expression
317 may call C<die> in order to abort the whole program, leaving the
318 original file untouched.
319
320 Remember when matching the end of a line that C<$_> may contain the
321 final C<\n>, or (for DOS files) C<\r\n>, or if the file does not end
322 with a newline then neither of these.  Thus to match or substitute
323 some text at the end of a line, use this regular expression:
324
325  /some text(\r?\n)?$/
326
327 Alternately, use the perl C<chomp> function, being careful not to
328 chomp C<$_> itself (since that would remove all newlines from the
329 file):
330
331  my $m = $_; chomp $m; $m =~ /some text$/
332
333 =head1 ENVIRONMENT VARIABLES
334
335 =over 4
336
337 =item C<EDITOR>
338
339 If set, this string is used as the editor.  It may contain arguments,
340 eg. C<"emacs -nw">
341
342 If not set, C<vi> is used.
343
344 =back
345
346 =head1 SHELL QUOTING
347
348 Libvirt guest names can contain arbitrary characters, some of which
349 have meaning to the shell such as C<#> and space.  You may need to
350 quote or escape these characters on the command line.  See the shell
351 manual page L<sh(1)> for details.
352
353 =head1 SEE ALSO
354
355 L<guestfs(3)>,
356 L<guestfish(1)>,
357 L<virt-cat(1)>,
358 L<Sys::Guestfs(3)>,
359 L<Sys::Guestfs::Lib(3)>,
360 L<Sys::Virt(3)>,
361 L<http://libguestfs.org/>,
362 L<perl(1)>,
363 L<perlre(1)>.
364
365 =head1 AUTHOR
366
367 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
368
369 =head1 COPYRIGHT
370
371 Copyright (C) 2009-2010 Red Hat Inc.
372
373 This program is free software; you can redistribute it and/or modify
374 it under the terms of the GNU General Public License as published by
375 the Free Software Foundation; either version 2 of the License, or
376 (at your option) any later version.
377
378 This program is distributed in the hope that it will be useful,
379 but WITHOUT ANY WARRANTY; without even the implied warranty of
380 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
381 GNU General Public License for more details.
382
383 You should have received a copy of the GNU General Public License
384 along with this program; if not, write to the Free Software
385 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.