df: Add --uuid option to print guest UUIDs instead of names (RHBZ#646821).
[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_not_used, $tempname) = tempfile (UNLINK => 1);
202
203 # Allow this to fail in case eg. the file does not exist.
204 $g->download($filename, $tempname);
205
206 my $do_upload = $tempname;
207
208 if (!defined $expr) {
209     # Interactively edit the file.
210     my $oldctime = (stat ($tempname))[10];
211
212     my $editor = $ENV{EDITOR};
213     $editor ||= "vi";
214     system ("$editor $tempname") == 0
215         or die "edit failed: $editor: $?";
216
217     my $newctime = (stat ($tempname))[10];
218
219     if ($oldctime == $newctime) {
220         $do_upload = undef;
221         print __"File not changed.\n";
222     }
223 } else {
224     my ($fh, $tempout) = tempfile (UNLINK => 1);
225
226     # Apply a Perl expression to the lines of the file.
227     open IFILE, $tempname or die "$tempname: $!";
228     my $lineno = 0;
229     while (<IFILE>) {
230         $lineno++;
231         eval $expr;
232         die if $@;
233         print $fh $_ or die "print: $!";
234     }
235     close $fh;
236
237     $do_upload = $tempout;
238 }
239
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{/[^/]+$}{/};
247
248     my @chars = ('a'..'z', 'A'..'Z', '0'..'9');
249     my $newname = $dirname;
250     foreach (0..7) {
251         $newname .= $chars[rand @chars];
252     }
253
254     $g->upload ($do_upload, $newname);
255
256     # Backup or overwrite?
257     $g->mv ($filename, "$filename$backup") if defined $backup;
258     $g->mv ($newname, $filename);
259
260     $g->umount_all ();
261     $g->sync ();
262 }
263
264 undef $g;
265
266 exit 0;
267
268 =head1 NON-INTERACTIVE EDITING
269
270 C<virt-edit> normally calls out to C<$EDITOR> (or vi) so
271 the system administrator can interactively edit the file.
272
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
277 editing.)
278
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.
282
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:
286
287  virt-edit domname filename -e 's/foo/bar/'
288
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:
291
292  virt-edit domname /etc/passwd -e 's/^root:.*?:/root::/'
293
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
297 leave it unchanged.
298
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:
301
302  virt-edit mydomain /etc/passwd -e '$_ = "" if /^apache:/'
303
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.
309
310 The variable C<$lineno> contains the current line number.
311 As is traditional, the first line in the file is number C<1>.
312
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.
316
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:
321
322  /some text(\r?\n)?$/
323
324 Alternately, use the perl C<chomp> function, being careful not to
325 chomp C<$_> itself (since that would remove all newlines from the
326 file):
327
328  my $m = $_; chomp $m; $m =~ /some text$/
329
330 =head1 ENVIRONMENT VARIABLES
331
332 =over 4
333
334 =item C<EDITOR>
335
336 If set, this string is used as the editor.  It may contain arguments,
337 eg. C<"emacs -nw">
338
339 If not set, C<vi> is used.
340
341 =back
342
343 =head1 SHELL QUOTING
344
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.
349
350 =head1 SEE ALSO
351
352 L<guestfs(3)>,
353 L<guestfish(1)>,
354 L<virt-cat(1)>,
355 L<Sys::Guestfs(3)>,
356 L<Sys::Guestfs::Lib(3)>,
357 L<Sys::Virt(3)>,
358 L<http://libguestfs.org/>,
359 L<perl(1)>,
360 L<perlre(1)>.
361
362 =head1 AUTHOR
363
364 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
365
366 =head1 COPYRIGHT
367
368 Copyright (C) 2009-2010 Red Hat Inc.
369
370 This program is free software; you can redistribute it and/or modify
371 it under the terms of the GNU General Public License as published by
372 the Free Software Foundation; either version 2 of the License, or
373 (at your option) any later version.
374
375 This program is distributed in the hope that it will be useful,
376 but WITHOUT ANY WARRANTY; without even the implied warranty of
377 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
378 GNU General Public License for more details.
379
380 You should have received a copy of the GNU General Public License
381 along with this program; if not, write to the Free Software
382 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.