apply: handle signals, unloading modules
[ksplice.git] / ksplice-apply.in
blob671060b3389e11f11821b3c380e70d0dc2f4597a
1 #!/usr/bin/perl
3 # Copyright (C) 2007-2009 Ksplice, Inc.
4 # Authors: Jeff Arnold, Anders Kaseorg, Tim Abbott
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License, version 2.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
17 # 02110-1301, USA.
19 use strict;
20 use warnings;
21 use sigtrap 'handler', \&handler, qw(normal-signals error-signals);
22 use lib 'KSPLICE_DATA_DIR';
23 use Ksplice;
25 my $debug;
26 my $debugon = 0;
27 my $partial = 0;
28 GetOptions(@common_options,
29 "partial" => \$partial,
30 "debug" => \$debugon,
31 "debugfile=s" => \$debug) or pod2usage(1);
33 pod2usage(1) if($help || scalar(@ARGV) != 1);
35 my $file = abs_path($ARGV[0]);
36 $debugon = 1 if(defined $debug);
37 $debug = abs_path($debug) if (defined $debug);
39 my $tmpdir = tempdir('ksplice-tmp-XXXXXX', TMPDIR => 1, CLEANUP => 1);
40 chdir($tmpdir);
41 my $ksplice = unpack_update($file);
42 chdir($ksplice);
44 die "No contents file in $file\n" if (!-e "contents");
45 open(CONTENTS, '<', "contents");
46 my $core;
47 my @changes;
48 while (<CONTENTS>) {
49 my @line = split(' ', $_);
50 if ($line[0] eq 'core') {
51 die "Multiple core modules in $file!" if (defined $core);
52 $core = {
53 'module' => $line[1],
54 'file' => $line[2],
57 if ($line[0] eq 'change') {
58 my $change = {
59 'target' => $line[1],
60 'new_code' => $line[2],
61 'new_code_file' => $line[3],
62 'old_code' => $line[4],
63 'old_code_file' => $line[5],
65 push @changes, $change;
68 close(CONTENTS);
70 die "Update was built using an old version of Ksplice" if (@changes == 0);
72 my $nounload = runstr("lsmod") =~ m/- $/m;
74 (my $kid = $ksplice) =~s|ksplice[-_]([^-_]+)|$1|;
75 my $update = "ksplice_$kid";
76 if(update_loaded($kid)) {
77 my $stage = get_stage($kid);
78 if ($stage eq "applied") {
79 print STDERR "Ksplice update $kid already applied.\n";
80 exit(0);
82 die "Reversed Ksplice module $update already loaded!" if ($stage eq "reversed");
85 runstr_err(qw(modprobe -q ksplice)) eq "" || die;
86 if (defined $core) {
87 die "Could not find Ksplice core module $core->{file}\n" if (!-e $core->{file});
88 if (runstr("lsmod") =~ m/^\Q$core->{module}\E\s+/) {
89 die "Ksplice core module $core already loaded.";
91 if (!load_module($core->{file}, "debug=$debugon")) {
92 die "Error loading Ksplice core module $core->{module} for update $kid";
96 foreach my $change (@changes) {
97 die unless (-e $change->{old_code_file} && -e $change->{new_code_file});
98 if ($change->{'target'} ne 'vmlinux' &&
99 runstr("lsmod") !~ m/^\Q$change->{target}\E\s+/m) {
100 if (!$partial) {
101 cleanup_modules();
102 print_error("target_not_loaded");
103 die "Module $change->{target} to be patched not loaded";
108 foreach my $change (@changes) {
109 if(!load_module($change->{new_code_file})) {
110 die "Error loading new code module $change->{new_code}";
112 if(!load_module($change->{old_code_file})) {
113 my $debugfile = get_debug_output("init_$kid", $debug);
114 print STDERR "Error loading old code module $change->{old_code}\n";
115 cleanup_modules();
116 if($debugon && defined $debugfile) {
117 print("Debugging output saved to $debugfile\n");
119 die;
123 set_debug_level($kid, $debugon);
124 set_partial($kid, $partial);
125 set_stage($kid, "applied");
126 my $stage = get_stage($kid);
127 if($stage ne 'applied') {
128 my $debugfile = get_debug_output($kid, $debug);
129 my $abort_cause = get_abort_cause($kid);
130 my $conflicts = get_conflicts($kid);
131 cleanup_modules();
132 print STDERR "Error applying Ksplice update $kid:\n";
133 print_error($abort_cause);
134 if ($abort_cause eq 'code_busy') {
135 print $conflicts;
137 if ($debugon && defined $debugfile) {
138 print("Debugging output saved to $debugfile\n");
140 die;
142 mkpath("/var/run/ksplice/updates/$kid");
143 copy("patch", "/var/run/ksplice/updates/$kid/patch") if (-e "patch");
144 copy("description", "/var/run/ksplice/updates/$kid/description") if (-e "description");
145 if (!$nounload) {
146 foreach my $change (@changes) {
147 runval('rmmod', $change->{old_code});
148 runval('rmmod', $change->{new_code}) if ($partial && refcount($change->{new_code}) == 0);
151 print "Done!\n";
152 exit(0);
154 my @modules_loaded = qw();
156 sub handler {
157 my ($sig) = @_;
158 die "caught SIG$sig, abort\n" if (!defined($kid));
159 die "caught SIG$sig after finished\n"
160 if (update_loaded($kid) && (get_stage($kid) eq 'applied'));
161 print STDERR "caught SIG$sig, aborting\n";
162 cleanup_modules();
163 exit(1);
166 sub load_module {
167 my ($module, @params) = @_;
168 push @modules_loaded, ($module =~ m/^(.*)\.ko$/);
169 if (runval_raw("insmod", $module, @params) != 0) {
170 pop @modules_loaded;
171 child_error();
172 return 0;
174 return 1;
177 sub refcount {
178 my ($module) = @_;
179 $module =~ s/-/_/g;
180 foreach(split(/\n/, runstr("lsmod"))) {
181 if (m/^(\S+)\s+[0-9]+\s+([0-9])+\s/) {
182 return $2 if ($1 eq $module);
185 return -1;
188 sub cleanup_modules {
189 foreach my $module (reverse(@modules_loaded)) {
190 runval("rmmod", $module) if(!$nounload);
194 sub print_error {
195 my ($error) = @_;
196 my %errors = (
197 "no_match" => <<'END',
198 Ksplice has aborted the upgrade because Ksplice has been unable to match the
199 object code produced by your current compiler and assembler against the running
200 kernel's object code. If you provided the exact kernel source to the running
201 kernel, then it appears that your current compiler and/or assembler are
202 behaving differently from the compiler and assembler used to build the running
203 kernel. If possible, please use the exact compiler and assembler that were
204 used to build the running kernel. If you are using exactly the same compiler
205 and assembler, consider reporting a bug to PACKAGE_BUGREPORT.
207 "code_busy" => <<'END',
208 Ksplice has aborted the upgrade because it appears that the code that you are
209 trying to patch is continuously in use by the system. More specifically,
210 Ksplice has been unable to find a moment when one or more of the to-be-patched
211 functions is not on a thread's kernel stack.
213 "bad_system_map" => <<'END',
214 Ksplice has aborted the upgrade because it appears that the System.map file
215 provided to ksplice-create does not match the running kernel.
217 "failed_to_find" => <<'END',
218 Ksplice has aborted the upgrade because it was unable to resolve some of the
219 symbols used in the update.
221 "already_reversed" => <<'END',
222 The Ksplice update that you are attempting to apply has already been applied
223 and reversed. You need to unload the Ksplice modules associated with this
224 update before you can apply this update again.
226 "missing_export" => <<'END',
227 Ksplice has aborted the upgrade because the symbols exported by the kernel
228 did not match Ksplice's expectations.
230 "unexpected_running_task" => <<'END',
231 Ksplice has aborted the upgrade because of an unexpected failure during the
232 kernel stack check. Please consider reporting a bug to PACKAGE_BUGREPORT.
234 "target_not_loaded" => <<'END',
235 Ksplice has aborted the upgrade because one of the modules to be
236 patched by the update was not loaded. If you want to apply this
237 update only to those modules that are loaded, then you should use the
238 --partial option.
240 "out_of_memory" => <<'END',
241 Ksplice has aborted the upgrade because the kernel ran out of memory.
243 "call_failed" => <<'END',
244 Ksplice has aborted the upgrade at the request of a one of the
245 pre-application hooks that were included as part of this Ksplice
246 update. This is likely the result of a bug in the patch used to
247 generate this update.
249 "unexpected" => <<'END',
250 Ksplice has aborted because of an unexpected error.
251 Please consider reporting a bug to PACKAGE_BUGREPORT.
253 "UNKNOWN" => <<'END',
254 The Ksplice kernel component has returned an error code that this version of
255 ksplice-apply does not understand.
257 "ok" => <<'END',
258 Ksplice has aborted the upgrade for unknown reasons.
259 Please consider reporting a bug to PACKAGE_BUGREPORT.
262 $error = "UNKNOWN" if (!exists $errors{$error});
263 print STDERR "\n$errors{$error}\n";
266 =head1 NAME
268 ksplice-apply - Apply an on-disk Ksplice update to the running kernel
270 =head1 SYNOPSIS
272 B<ksplice-apply> [I<OPTIONS>] I<UPDATE_TARBALL>
274 =head1 DESCRIPTION
276 B<ksplice-apply> takes as input a Ksplice update tarball, as generated by
277 L<ksplice-create(8)>, and it applies the update to the running binary kernel.
279 The update tarball used with B<ksplice-apply> must have been generated for the
280 running kernel's version.
282 =head1 OPTIONS
284 =over 8
286 =item B<--debug>
288 Applies the update with debugging output enabled. Recommended only for
289 debugging.
291 =item B<--debugfile=>I<filename>
293 Sets the location where debugging output should be saved. Implies --debug.
295 =item B<--partial>
297 Applies the update only to those modules which are loaded. Any
298 modules patched by the update that are not loaded are ignored (without
299 this option, Ksplice aborts if any modules patched by the update are
300 not loaded).
302 =back
304 =head1 SEE ALSO
306 L<ksplice-create(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
308 =head1 BUGS
310 Please report bugs to <PACKAGE_BUGREPORT>.
312 =head1 AUTHORS
314 Jeff Arnold, Anders Kaseorg, and Tim Abbott
316 =head1 COPYRIGHT
318 Copyright (C) 2007-2009 Ksplice, Inc.
320 This is free software and documentation. You can redistribute and/or modify it
321 under the terms of the GNU General Public License, version 2.
323 =cut