d4e11dbfa3a58f7c6167247eb780fdf457a19a58
[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 $uri;
96
97 =item B<--connect URI> | B<-c URI>
98
99 If using libvirt, connect to the given I<URI>.  If omitted, then we
100 connect to the default libvirt hypervisor.
101
102 If you specify guest block devices directly, then libvirt is not used
103 at all.
104
105 =cut
106
107 my $expr;
108
109 =item B<--expr EXPR> | B<-e EXPR>
110
111 Instead of launching the external editor, non-interactively
112 apply the Perl expression C<EXPR> to each line in the file.
113 See L</NON-INTERACTIVE EDITING> below.
114
115 Be careful to properly quote the expression to prevent it from
116 being altered by the shell.
117
118 =back
119
120 =cut
121
122 GetOptions ("help|?" => \$help,
123             "version" => \$version,
124             "connect|c=s" => \$uri,
125             "expr|e=s" => \$expr,
126     ) or pod2usage (2);
127 pod2usage (1) if $help;
128 if ($version) {
129     my $g = Sys::Guestfs->new ();
130     my %h = $g->version ();
131     print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
132     exit
133 }
134
135 pod2usage (__"virt-edit: no image, VM names or filenames to edit given")
136     if @ARGV <= 1;
137
138 my $filename = pop @ARGV;
139
140 my $g;
141 if ($uri) {
142     $g = open_guest (\@ARGV, address => $uri, rw => 1);
143 } else {
144     $g = open_guest (\@ARGV, rw => 1);
145 }
146
147 $g->launch ();
148
149 # List of possible filesystems.
150 my @partitions = get_partitions ($g);
151
152 # Now query each one to build up a picture of what's in it.
153 my %fses =
154     inspect_all_partitions ($g, \@partitions,
155       use_windows_registry => 0);
156
157 my $oses = inspect_operating_systems ($g, \%fses);
158
159 my @roots = keys %$oses;
160 die __"multiboot operating systems are not supported by virt-edit" if @roots > 1;
161 my $root_dev = $roots[0];
162
163 my $os = $oses->{$root_dev};
164 mount_operating_system ($g, $os, 0);
165
166 my ($fh_not_used, $tempname) = tempfile ();
167
168 # Allow this to fail in case eg. the file does not exist.
169 $g->download($filename, $tempname);
170
171 my $do_upload = $tempname;
172
173 if (!defined $expr) {
174     # Interactively edit the file.
175     my $oldctime = (stat ($tempname))[10];
176
177     my $editor = $ENV{EDITOR};
178     $editor ||= "vi";
179     system ("$editor $tempname") == 0
180         or die "edit failed: $editor: $?";
181
182     my $newctime = (stat ($tempname))[10];
183
184     if ($oldctime == $newctime) {
185         $do_upload = undef;
186         print __"File not changed.\n";
187     }
188 } else {
189     my ($fh, $tempout) = tempfile ();
190
191     # Apply a Perl expression to the lines of the file.
192     open IFILE, $tempname or die "$tempname: $!";
193     my $lineno = 0;
194     while (<IFILE>) {
195         $lineno++;
196         eval $expr;
197         die if $@;
198         print $fh $_ or die "print: $!";
199     }
200     close $fh;
201
202     $do_upload = $tempout;
203 }
204
205 if (defined $do_upload) {
206     $g->upload ($do_upload, $filename);
207     $g->umount_all ();
208     $g->sync ();
209 }
210
211 undef $g;
212
213 exit 0;
214
215 =head1 NON-INTERACTIVE EDITING
216
217 C<virt-edit> normally calls out to C<$EDITOR> (or vi) so
218 the system administrator can interactively edit the file.
219
220 There are two ways also to use C<virt-edit> from scripts in order to
221 make automated edits to files.  (Note that although you I<can> use
222 C<virt-edit> like this, it's less error-prone to write scripts
223 directly using the libguestfs API and Augeas for configuration file
224 editing.)
225
226 The first method is to temporarily set C<$EDITOR> to any script or
227 program you want to run.  The script is invoked as C<$EDITOR tmpfile>
228 and it should update C<tmpfile> in place however it likes.
229
230 The second method is to use the C<-e> parameter of C<virt-edit> to run
231 a short Perl snippet in the style of L<sed(1)>.  For example to
232 replace all instances of C<foo> with C<bar> in a file:
233
234  virt-edit domname filename -e 's/foo/bar/'
235
236 The full power of Perl regular expressions can be used (see
237 L<perlre(1)>).  For example to delete root's password you could do:
238
239  virt-edit domname /etc/passwd -e 's/^root:.*?:/root::/'
240
241 What really happens is that the snippet is evaluated as a Perl
242 expression for each line of the file.  The line, including the final
243 C<\n>, is passed in C<$_> and the expression should update C<$_> or
244 leave it unchanged.
245
246 To delete a line, set C<$_> to the empty string.  For example, to
247 delete the C<apache> user account from the password file you can do:
248
249  virt-edit mydomain /etc/passwd -e '$_ = "" if /^apache:/'
250
251 To insert a line, prepend or append it to C<$_>.  However appending
252 lines to the end of the file is rather difficult this way since there
253 is no concept of "last line of the file" - your expression just
254 doesn't get called again.  You might want to use the first method
255 (setting C<$EDITOR>) if you want to do this.
256
257 The variable C<$lineno> contains the current line number.
258 As is traditional, the first line in the file is number C<1>.
259
260 The return value from the expression is ignored, but the expression
261 may call C<die> in order to abort the whole program, leaving the
262 original file untouched.
263
264 Remember when matching the end of a line that C<$_> may contain the
265 final C<\n>, or (for DOS files) C<\r\n>, or if the file does not end
266 with a newline then neither of these.  Thus to match or substitute
267 some text at the end of a line, use this regular expression:
268
269  /some text(\r?\n)?$/
270
271 Alternately, use the perl C<chomp> function, being careful not to
272 chomp C<$_> itself (since that would remove all newlines from the
273 file):
274
275  my $m = $_; chomp $m; $m =~ /some text$/
276
277 =head1 ENVIRONMENT VARIABLES
278
279 =over 4
280
281 =item C<EDITOR>
282
283 If set, this string is used as the editor.  It may contain arguments,
284 eg. C<"emacs -nw">
285
286 If not set, C<vi> is used.
287
288 =back
289
290 =head1 SEE ALSO
291
292 L<guestfs(3)>,
293 L<guestfish(1)>,
294 L<virt-cat(1)>,
295 L<Sys::Guestfs(3)>,
296 L<Sys::Guestfs::Lib(3)>,
297 L<Sys::Virt(3)>,
298 L<http://libguestfs.org/>,
299 L<perl(1)>,
300 L<perlre(1)>.
301
302 =head1 AUTHOR
303
304 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
305
306 =head1 COPYRIGHT
307
308 Copyright (C) 2009-2010 Red Hat Inc.
309
310 This program is free software; you can redistribute it and/or modify
311 it under the terms of the GNU General Public License as published by
312 the Free Software Foundation; either version 2 of the License, or
313 (at your option) any later version.
314
315 This program is distributed in the hope that it will be useful,
316 but WITHOUT ANY WARRANTY; without even the implied warranty of
317 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
318 GNU General Public License for more details.
319
320 You should have received a copy of the GNU General Public License
321 along with this program; if not, write to the Free Software
322 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.