undo: clean up even if sysfs already gone
[ksplice.git] / ksplice-apply.in
blobeb3939e12f46c01fd985f77d88ac6305d3cb50a0
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 lib 'KSPLICE_DATA_DIR';
22 use Ksplice;
24 my $debug;
25 my $debugon = 0;
26 my $partial = 0;
27 GetOptions(@common_options,
28 "partial" => \$partial,
29 "debug" => \$debugon,
30 "debugfile=s" => \$debug) or pod2usage(1);
32 pod2usage(1) if($help || scalar(@ARGV) != 1);
34 my $file = abs_path($ARGV[0]);
35 $debugon = 1 if(defined $debug);
36 $debug = abs_path($debug) if (defined $debug);
38 my $tmpdir = tempdir('ksplice-tmp-XXXXXX', TMPDIR => 1, CLEANUP => 1);
39 chdir($tmpdir);
40 my $ksplice = unpack_update($file);
41 chdir($ksplice);
43 die "No contents file in $file\n" if (!-e "contents");
44 open(CONTENTS, '<', "contents");
45 my $core;
46 my @changes;
47 while (<CONTENTS>) {
48 my @line = split(' ', $_);
49 if ($line[0] eq 'core') {
50 die "Multiple core modules in $file!" if (defined $core);
51 $core = {
52 'module' => $line[1],
53 'file' => $line[2],
56 if ($line[0] eq 'change') {
57 my $change = {
58 'target' => $line[1],
59 'new_code' => $line[2],
60 'new_code_file' => $line[3],
61 'old_code' => $line[4],
62 'old_code_file' => $line[5],
64 push @changes, $change;
67 close(CONTENTS);
69 die "Update was built using an old version of Ksplice" if (@changes == 0);
71 my $nounload = runstr("lsmod") =~ m/- $/m;
73 (my $kid = $ksplice) =~s|ksplice[-_]([^-_]+)|$1|;
74 my $update = "ksplice_$kid";
75 if(update_loaded($kid)) {
76 my $stage = get_stage($kid);
77 if ($stage eq "applied") {
78 print STDERR "Ksplice update $kid already applied.\n";
79 exit(0);
81 die "Reversed Ksplice module $update already loaded!" if ($stage eq "reversed");
84 runstr_err(qw(modprobe -q ksplice)) eq "" || die;
85 if (defined $core) {
86 die "Could not find Ksplice core module $core->{file}\n" if (!-e $core->{file});
87 if (runstr("lsmod") =~ m/^\Q$core->{module}\E\s+/) {
88 die "Ksplice core module $core already loaded.";
90 if (!load_module($core->{file}, "debug=$debugon")) {
91 die "Error loading Ksplice core module $core->{module} for update $kid";
95 foreach my $change (@changes) {
96 die unless (-e $change->{old_code_file} && -e $change->{new_code_file});
97 if ($change->{'target'} ne 'vmlinux' &&
98 runstr("lsmod") !~ m/^\Q$change->{target}\E\s+/m) {
99 if (!$partial) {
100 cleanup_modules();
101 print_error("target_not_loaded");
102 die "Module $change->{target} to be patched not loaded";
107 foreach my $change (@changes) {
108 if(!load_module($change->{new_code_file})) {
109 die "Error loading new code module $change->{new_code}";
111 if(!load_module($change->{old_code_file})) {
112 my $debugfile = get_debug_output("init_$kid", $debug);
113 print STDERR "Error loading old code module $change->{old_code}\n";
114 cleanup_modules();
115 if($debugon && defined $debugfile) {
116 print("Debugging output saved to $debugfile\n");
118 die;
122 set_debug_level($kid, $debugon);
123 set_partial($kid, $partial);
124 set_stage($kid, "applied");
125 my $stage = get_stage($kid);
126 if($stage ne 'applied') {
127 my $debugfile = get_debug_output($kid, $debug);
128 my $abort_cause = get_abort_cause($kid);
129 my $conflicts = get_conflicts($kid);
130 cleanup_modules();
131 print STDERR "Error applying Ksplice update $kid:\n";
132 print_error($abort_cause);
133 if ($abort_cause eq 'code_busy') {
134 print $conflicts;
136 if ($debugon && defined $debugfile) {
137 print("Debugging output saved to $debugfile\n");
139 die;
141 mkpath("/var/run/ksplice/updates/$kid");
142 copy("patch", "/var/run/ksplice/updates/$kid/patch") if (-e "patch");
143 copy("description", "/var/run/ksplice/updates/$kid/description") if (-e "description");
144 if (!$nounload) {
145 foreach my $change (@changes) {
146 runval('rmmod', $change->{old_code});
147 runval('rmmod', $change->{new_code}) if ($partial && refcount($change->{new_code}) == 0);
150 print "Done!\n";
151 exit(0);
153 my @modules_loaded = qw();
155 sub load_module {
156 my ($module, @params) = @_;
157 if (runval_raw("insmod", $module, @params) != 0) {
158 child_error();
159 return 0;
161 $module =~ s/\.ko//;
162 push @modules_loaded, $module;
163 return 1;
166 sub refcount {
167 my ($module) = @_;
168 $module =~ s/-/_/g;
169 foreach(split(/\n/, runstr("lsmod"))) {
170 if (m/^(\S+)\s+[0-9]+\s+([0-9])+\s/) {
171 return $2 if ($1 eq $module);
174 return -1;
177 sub cleanup_modules {
178 foreach my $module (reverse(@modules_loaded)) {
179 runval("rmmod", $module) if(!$nounload);
183 sub print_error {
184 my ($error) = @_;
185 my %errors = (
186 "no_match" => <<'END',
187 Ksplice has aborted the upgrade because Ksplice has been unable to match the
188 object code produced by your current compiler and assembler against the running
189 kernel's object code. If you provided the exact kernel source to the running
190 kernel, then it appears that your current compiler and/or assembler are
191 behaving differently from the compiler and assembler used to build the running
192 kernel. If possible, please use the exact compiler and assembler that were
193 used to build the running kernel. If you are using exactly the same compiler
194 and assembler, consider reporting a bug to PACKAGE_BUGREPORT.
196 "code_busy" => <<'END',
197 Ksplice has aborted the upgrade because it appears that the code that you are
198 trying to patch is continuously in use by the system. More specifically,
199 Ksplice has been unable to find a moment when one or more of the to-be-patched
200 functions is not on a thread's kernel stack.
202 "bad_system_map" => <<'END',
203 Ksplice has aborted the upgrade because it appears that the System.map file
204 provided to ksplice-create does not match the running kernel.
206 "failed_to_find" => <<'END',
207 Ksplice has aborted the upgrade because it was unable to resolve some of the
208 symbols used in the update.
210 "already_reversed" => <<'END',
211 The Ksplice update that you are attempting to apply has already been applied
212 and reversed. You need to unload the Ksplice modules associated with this
213 update before you can apply this update again.
215 "missing_export" => <<'END',
216 Ksplice has aborted the upgrade because the symbols exported by the kernel
217 did not match Ksplice's expectations.
219 "unexpected_running_task" => <<'END',
220 Ksplice has aborted the upgrade because of an unexpected failure during the
221 kernel stack check. Please consider reporting a bug to PACKAGE_BUGREPORT.
223 "target_not_loaded" => <<'END',
224 Ksplice has aborted the upgrade because one of the modules to be
225 patched by the update was not loaded. If you want to apply this
226 update only to those modules that are loaded, then you should use the
227 --partial option.
229 "out_of_memory" => <<'END',
230 Ksplice has aborted the upgrade because the kernel ran out of memory.
232 "call_failed" => <<'END',
233 Ksplice has aborted the upgrade at the request of a one of the
234 pre-application hooks that were included as part of this Ksplice
235 update. This is likely the result of a bug in the patch used to
236 generate this update.
238 "unexpected" => <<'END',
239 Ksplice has aborted because of an unexpected error.
240 Please consider reporting a bug to PACKAGE_BUGREPORT.
242 "UNKNOWN" => <<'END',
243 The Ksplice kernel component has returned an error code that this version of
244 ksplice-apply does not understand.
246 "ok" => <<'END',
247 Ksplice has aborted the upgrade for unknown reasons.
248 Please consider reporting a bug to PACKAGE_BUGREPORT.
251 $error = "UNKNOWN" if (!exists $errors{$error});
252 print STDERR "\n$errors{$error}\n";
255 =head1 NAME
257 ksplice-apply - Apply an on-disk Ksplice update to the running kernel
259 =head1 SYNOPSIS
261 B<ksplice-apply> [I<OPTIONS>] I<UPDATE_TARBALL>
263 =head1 DESCRIPTION
265 B<ksplice-apply> takes as input a Ksplice update tarball, as generated by
266 L<ksplice-create(8)>, and it applies the update to the running binary kernel.
268 The update tarball used with B<ksplice-apply> must have been generated for the
269 running kernel's version.
271 =head1 OPTIONS
273 =over 8
275 =item B<--debug>
277 Applies the update with debugging output enabled. Recommended only for
278 debugging.
280 =item B<--debugfile=>I<filename>
282 Sets the location where debugging output should be saved. Implies --debug.
284 =item B<--partial>
286 Applies the update only to those modules which are loaded. Any
287 modules patched by the update that are not loaded are ignored (without
288 this option, Ksplice aborts if any modules patched by the update are
289 not loaded).
291 =back
293 =head1 SEE ALSO
295 L<ksplice-create(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
297 =head1 BUGS
299 Please report bugs to <PACKAGE_BUGREPORT>.
301 =head1 AUTHORS
303 Jeff Arnold, Anders Kaseorg, and Tim Abbott
305 =head1 COPYRIGHT
307 Copyright (C) 2007-2009 Ksplice, Inc.
309 This is free software and documentation. You can redistribute and/or modify it
310 under the terms of the GNU General Public License, version 2.
312 =cut