undo: handle sysfs gone in error_die()
[ksplice.git] / ksplice-create.in
blob72ffa34bf888f10bb2dd1e95855c55df00563c68
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 ($patchfile, $diffext, $git, $orig_config_dir, $jobs, $kid);
25 my $description;
26 my $series = 0;
27 my $build_modules = 0;
28 my $ksplice_short_name_hack = 0;
29 my @only_targets;
30 my @extra_match;
31 my @exclude_match;
32 my $standalone;
33 my ($prebuild, $skip_prebuild) = (0, 0);
34 my @patch_opt = "-p1";
35 GetOptions(@common_options,
36 "id=s" => \$kid,
37 "patch=s" => \$patchfile,
38 "description=s" => \$description,
39 "diffext=s" => \$diffext,
40 "git=s" => \$git,
41 "prebuild" => \$prebuild,
42 "series!" => \$series,
43 "build-modules!" => \$build_modules,
44 "only-targets=s" => \@only_targets,
45 "extra-match=s" => \@extra_match,
46 "exclude-match=s" => \@exclude_match,
47 "standalone!" => \$standalone,
48 "short-name-hack!" => \$ksplice_short_name_hack,
49 "skip-prebuild" => \$skip_prebuild,
50 "jobs|j:i" => \$jobs,
51 "config=s" => \$orig_config_dir,
52 "patch-opt=s" => \@patch_opt) or pod2usage(1);
54 pod2usage(1) if($help || scalar(@ARGV) != 1);
55 my $actions = (defined $patchfile) + (defined $diffext) + (defined $git) + $prebuild;
56 pod2usage(1) if($actions != 1);
58 my $have_Git = eval { require Git };
59 $have_Git or die $@ if defined $git;
61 my ($linuxtree) = (abs_path($ARGV[0]));
63 if(!defined $orig_config_dir) {
64 $orig_config_dir = "$linuxtree/ksplice";
66 else {
67 $orig_config_dir = abs_path($orig_config_dir);
68 if($orig_config_dir =~ $linuxtree) {
69 die "Aborting: User-specified ORIG_CONFIG cannot be KERNEL_SOURCE or a subdirectory";
72 if(!defined $orig_config_dir || ! -d $orig_config_dir) {
73 die "Failed to find ORIG_CONFIG directory ($orig_config_dir)";
75 if(! -e "$orig_config_dir/System.map") {
76 die "Failed to find System.map file in ORIG_CONFIG directory";
79 my $kernel_headers_dir = "$orig_config_dir/build";
80 $kernel_headers_dir = $linuxtree unless(-d $kernel_headers_dir);
82 my @kbuild_flags = ();
83 if(-e "$orig_config_dir/flags") {
84 open(FLAGS, '<', "$orig_config_dir/flags") or die;
85 local $/;
86 @kbuild_flags = shellwords(scalar <FLAGS>);
87 close(FLAGS);
90 $ENV{KSPLICE_VERBOSE} = $Verbose::level;
91 $ENV{KSPLICE_CONFIG_DIR} = $orig_config_dir;
93 my @chars = ('a'..'z', 0..9);
94 $kid = join '', map { $chars[int(rand(36))] } 0..7 if(!defined $kid);
95 my $ksplice = "ksplice-$kid";
96 $ENV{KSPLICE_KID} = $kid;
98 # Some versions of Fedora have System.map files whose symbol addresses disagree
99 # with the running kernel by a constant address offset. Here, Ksplice notes the
100 # System.map address for printk so that it can later compare this address against
101 # the kernel's address for printk. This comparison helps Ksplice work around
102 # this Fedora problem, and this comparison also helps Ksplice detect whether
103 # the user has provided an incorrect System.map file.
104 my $map_printk = runstr("$datadir/ksplice-obj.pl", "system_map_lookup", "printk");
106 print "Starting kernel builds (this process might take a long time)...\n"
107 if($Verbose::level >= 0);
109 $patchfile = abs_path($patchfile) if(defined $patchfile);
111 my $origdir = getcwd();
112 chdir($linuxtree);
114 my $git_rev;
115 my $git_repo;
116 if (defined $git) {
117 $git_repo = Git->repository();
118 ($git_rev) = $git_repo->command(qw(rev-parse --verify), $git);
119 } else {
120 $git_repo = eval { Git->repository() };
123 my @make = ("make", "-rR");
124 if(defined $jobs) {
125 push @make, "-j$jobs";
126 } elsif(defined $ENV{CONCURRENCY_LEVEL}) {
127 push @make, "-j$ENV{CONCURRENCY_LEVEL}";
130 if($Verbose::level >= 2) {
131 push @make, "V=1";
132 } elsif($Verbose::level < 0) {
133 push @make, "-s";
136 $ENV{PATH} = "$datadir:$ENV{PATH}";
137 my @make_ksplice = (@make, "-f", "$datadir/Makefile.ksplice", @kbuild_flags);
139 push(@make_ksplice, "KSPLICE_BUILD_MODULES=1") if ($build_modules);
140 if(-e "include/config/kernel.release") {
141 push(@make_ksplice, "-o", "include/config/kernel.release");
144 my @revert_flags = ("KSPLICE_MODE=revert");
145 push(@revert_flags, "KSPLICE_SERIES=y") if ($series);
147 sub git_have_ksplice_pre {
148 return $git_repo->command(qw(for-each-ref refs/ksplice/pre)) ne '';
151 if(-e "$orig_config_dir/.config" && !-e "$linuxtree/.config") {
152 copy("$orig_config_dir/.config", "$linuxtree/.config");
153 utime((stat("$orig_config_dir/.config"))[8, 9],
154 "$linuxtree/.config");
157 sub revert_orig() {
158 for(split(/\0/, runstr(qw(find -name *.KSPLICE_presrc -print0)))) {
159 my ($file) = m/^(.*)\.KSPLICE_presrc$/;
160 if ($series) {
161 unlink($_);
162 } else {
163 rename($_, $file);
166 if (defined $git_repo && git_have_ksplice_pre) {
167 if ($series) {
168 $git_repo->command_noisy(qw(update-ref -m), "ksplice-create: freeze", qw(refs/ksplice/pre HEAD refs/ksplice/pre));
169 } else {
170 $git_repo->command_noisy(qw(update-index --refresh));
171 $git_repo->command_noisy(qw(read-tree -m --trivial -u refs/ksplice/pre));
172 $git_repo->command_noisy(qw(update-ref -m), "ksplice-create: revert", qw(HEAD refs/ksplice/pre HEAD));
175 runval(@make_ksplice, @revert_flags);
177 revert_orig();
179 if (defined $git_repo && !git_have_ksplice_pre) {
180 $git_repo->command_noisy(qw(update-ref -m), "ksplice-create: snap", qw(refs/ksplice/pre HEAD), '');
183 if(!$skip_prebuild) {
184 if(-e "$orig_config_dir/.config") {
185 copy("$orig_config_dir/.config", "$linuxtree/.config");
186 utime((stat("$orig_config_dir/.config"))[8, 9],
187 "$linuxtree/.config");
189 my @snap_flags = ("KSPLICE_MODE=snap");
190 runval_raw(@make_ksplice, @snap_flags) == 0 or
191 die "Aborting: Prebuild failed";
192 sleep(1);
194 exit(0) if($prebuild);
196 my $tmpdir = tempdir('ksplice-tmp-XXXXXX', TMPDIR => 1, CLEANUP => 1);
197 copy($patchfile, "$tmpdir/patch") if(defined $patchfile);
198 $patchfile = "$tmpdir/patch";
200 if(defined $diffext) {
201 open(PATCH, '>', $patchfile) or die;
202 for(split(/\0/, runstr("find", "-name", "*$diffext", "-print0"))) {
203 my ($file) = /^(.*)\Q$diffext\E/ or die;
204 print PATCH runstr("diff", "-u", "--", $file, $_);
206 close(PATCH) or die;
207 @patch_opt = ("-p0");
210 my $kmodsrc = "$tmpdir/kmodsrc";
211 runval("cp", "-a", "--", "$datadir/kmodsrc", $kmodsrc);
212 $ENV{KSPLICE_KMODSRC} = $kmodsrc;
214 my @make_kmodsrc = (@make, "-C", $kernel_headers_dir, "M=$kmodsrc", "KSPLICE_KID=$kid", "KSPLICE_VERSION=PACKAGE_VERSION", "map_printk=$map_printk");
216 if (!defined($standalone)) {
217 $standalone = (!-e "$linuxtree/.config" || runval_raw(qw(grep -q ^CONFIG_KSPLICE=[ym]$), "$linuxtree/.config") != 0);
219 push(@make_kmodsrc, "KSPLICE_STANDALONE=1") if ($standalone);
220 push(@make_kmodsrc, "KSPLICE_SHORT_NAME_HACK=1") if ($ksplice_short_name_hack);
222 my @make_kmodsrc_install = (@make_kmodsrc, qw(modules_install --old-file=_modinst_post --old-file=_emodinst_post), "MAKE=make --old-file=_modinst_post --old-file=_emodinst_post", "INSTALL_MOD_STRIP=1", "MODLIB=$tmpdir/ksplice-modules");
224 runval(@make_kmodsrc);
225 runval(@make_kmodsrc_install);
227 @patch_opt = ("-s", @patch_opt) if ($Verbose::level < 0);
229 if (defined $git) {
230 $git_repo->command_noisy(qw(update-index --refresh));
231 $git_repo->command_noisy(qw(read-tree -m --trivial -u), $git_rev);
232 $git_repo->command_noisy(qw(update-ref -m), "ksplice-create: diff", "HEAD", $git_rev);
234 open(PATCH, '>', $patchfile) or die;
235 if ($git_repo->command(qw(rev-parse --verify HEAD^)) eq
236 $git_repo->command(qw(rev-parse --verify refs/ksplice/pre))) {
237 print PATCH scalar($git_repo->command(qw(log -1 HEAD --))), "\n";
239 print PATCH scalar($git_repo->command(qw(diff-tree -p refs/ksplice/pre HEAD --)));
240 close(PATCH) or die;
241 } else {
242 runval_infile($patchfile, "patch", @patch_opt, "-bz", ".KSPLICE_presrc");
245 my @diff_flags = ("KSPLICE_MODE=diff");
246 push @diff_flags, "KSPLICE_EXTRA_MATCH=@extra_match" if (@extra_match);
247 push @diff_flags, "KSPLICE_EXCLUDE_MATCH=@exclude_match" if (@exclude_match);
248 push @diff_flags, "KSPLICE_ONLY_TARGETS=@only_targets" if (@only_targets);
249 if(runval_raw(@make_ksplice, @diff_flags) != 0) {
250 revert_orig() if(defined($diffext));
251 die "Aborting: Applying the patch appears to break the kernel build";
254 sub copy_debug {
255 my ($file) = @_;
256 my ($dir, $base) = (dirname($file), basename($file));
257 -d "$tmpdir/objects/$dir" or mkpath("$tmpdir/objects/$dir");
258 copy($file, "$tmpdir/objects/$file");
259 my $cmdfile = "$dir/.$base.cmd";
260 copy($cmdfile, "$tmpdir/objects/$cmdfile") if(-e $cmdfile);
263 mkdir("$tmpdir/objects");
264 for (split(/\0/, runstr(qw(find -name *.KSPLICE* ! ( -name *.KSPLICE -empty ) ! -name .*.KSPLICE.cmd -print0)))) {
265 next if (basename($_) =~ m/^(?:vmlinux|vmlinux\.o|\.tmp_vmlinux[0-9]+|\.tmp_kallsyms[0-9]+.o|built-in.o)\.KSPLICE_pre$/);
266 copy_debug($_);
267 copy_debug($1) if (m/^(.*)\.KSPLICE_pre(?:src)?$/);
270 my @modulepaths = ();
271 my @modules = ();
272 foreach(glob("$kmodsrc/*.mod.KSPLICE")) {
273 open MOD, '<', $_;
274 chomp(my $mod = <MOD>);
275 close MOD;
276 push @modulepaths, "$mod.ko" if (basename($mod) ne "vmlinux");
277 push @modules, basename($mod);
280 if(!@modules) {
281 revert_orig() if(defined($diffext));
282 print STDERR "No changes detected.\n";
283 exit(66);
286 if ($build_modules) {
287 mkdir("$tmpdir/modules");
288 runval(@make_ksplice, "KSPLICE_MODE=modinst", "MODLIB=$tmpdir/modules", "INSTALL_MOD_STRIP=1", "modules=@modulepaths");
291 revert_orig() if(defined($diffext));
293 runval(@make_kmodsrc, "KSPLICE_MODULES=@modules", "KSPLICE_SKIP_CORE=1");
294 runval(@make_kmodsrc_install, "KSPLICE_MODULES=@modules", "KSPLICE_SKIP_CORE=1");
296 chdir($tmpdir);
297 mkdir($ksplice);
298 move($patchfile, $ksplice);
299 if ($description) {
300 write_file("$ksplice/description", "$description\n");
302 write_file("$ksplice/api-version", "KSPLICE_API_VERSION\n");
303 write_file("$ksplice/timestamp", time() . "\n");
304 runval_outfile("$ksplice/utsname", "/usr/local/libexec/ksplice-kernel-utsname", "$kmodsrc/offsets.o");
305 open(CONTENTS, ">", "$ksplice/contents");
306 foreach my $mod (@modules) {
307 (my $target = $mod) =~ s/-/_/g;
308 my $mid = "${kid}_$target";
309 my $module = "ksplice-$mid";
310 unless($ksplice_short_name_hack) {
311 rename("$tmpdir/ksplice-modules/extra/$module-new.ko", "$ksplice/$module-new.ko");
312 rename("$tmpdir/ksplice-modules/extra/$module-old.ko", "$ksplice/$module-old.ko");
313 print CONTENTS "change $target ksplice_${mid}_new $module-new.ko ksplice_${mid}_old $module-old.ko\n";
314 } else {
315 rename("$tmpdir/ksplice-modules/extra/$module-n.ko", "$ksplice/$module-n.ko");
316 rename("$tmpdir/ksplice-modules/extra/$module-o.ko", "$ksplice/$module-o.ko");
317 print CONTENTS "change $target ksplice_${mid}_n $module-n.ko ksplice_${mid}_o $module-o.ko\n";
320 if ($standalone) {
321 rename("$tmpdir/ksplice-modules/extra/ksplice-$kid.ko", "$ksplice/ksplice-$kid.ko");
322 print CONTENTS "core ksplice_$kid ksplice-$kid.ko\n";
324 if ($build_modules) {
325 foreach my $mod (@modulepaths) {
326 (my $target = basename($mod)) =~ s/-/_/g;
327 print CONTENTS "module $target $mod\n";
331 mkdir("$ksplice/debug");
332 rename("objects", "$ksplice/debug/objects");
333 rename("modules", "$ksplice/modules") if ($build_modules);
334 rename("$kmodsrc", "$ksplice/debug/kmodsrc");
336 close(CONTENTS);
337 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
338 copy("$ksplice.tar.gz", "$origdir/$ksplice.tar.gz");
339 print "Ksplice update tarball written to $ksplice.tar.gz\n";
340 exit(0);
342 =head1 NAME
344 ksplice-create - Create a set of kernel modules for a rebootless kernel update
346 =head1 SYNOPSIS
348 B<ksplice-create> [I<OPTIONS>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
350 B<ksplice-create> [I<OPTIONS>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
352 B<ksplice-create> [I<OPTIONS>] B<--git=>I<COMMIT> I<KERNEL_SOURCE>
354 B<ksplice-create> [I<OPTIONS>] B<--prebuild> I<KERNEL_SOURCE>
356 =head1 DESCRIPTION
358 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
359 will apply a user-specified source code patch to the running binary kernel.
361 Before you use B<ksplice-create> on a patch, you should confirm that the
362 desired source code change does not make any semantic changes to kernel data
363 structures--that is, changes that would require existing instances of kernel
364 data structures to be transformed (e.g., a patch that adds a field to a global
365 data structure would require the existing data structures to change). If you
366 use Ksplice on a patch that changes data structure semantics, Ksplice will not
367 detect the problem and you could experience kernel problems as a result.
369 The to-be-applied source code patch can be specified by providing a L<patch(1)>
370 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
371 (B<--diffext=>I<EXTENSION>).
373 If a file extension is specified, then the desired source code patch will be
374 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
375 whose names end with the extra extension I<EXTENSION> against the corresponding
376 files without the extra extension. Only the new files containing the extra
377 extension in their filenames should be modified.
379 Here is an example of using a file extension to specify a patch:
381 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
382 [edit sys.c.prctl_fixed to include the desired changes]
383 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
385 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
386 original source code. If your Linux distribution applies patches to the Linux
387 kernel during the kernel build process, then those patches must be applied to
388 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
389 directory. B<ksplice-create> will not modify the source code in the
390 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
391 directory tree.
393 I<ORIG_CONFIG> can be used to specify the directory containing the
394 to-be-updated kernel's original F<.config> file and original F<System.map> file
395 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
396 I<KERNEL_SOURCE>B</ksplice>.
398 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
399 close to the compiler and assembler originally used to build the running kernel
400 as possible. If the current compiler and linker are too different from the
401 original compiler and linker, B<ksplice-apply> will abort when applying the
402 update.
404 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
405 containing the desired Ksplice update modules. This tarball will be created in
406 the current directory, and it can be manipulated using the other Ksplice
407 utilities, such as B<ksplice-apply>.
409 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
410 directory, it must build that kernel from scratch, which is much slower than
411 the rest of the update-creation process. B<--prebuild> can be used to perform
412 this initial kernel build without providing a source code patch.
414 In order to patch a function that has previously been patched by Ksplice, the
415 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
416 contains the source for the currently running kernel, including any patches
417 that have previously been applied by Ksplice.
419 =head1 OPTIONS
421 =over 8
423 =item B<--patch=>I<PATCH_FILE>
425 Builds a Ksplice update out of the given L<patch(1)> file I<PATCH_FILE>.
427 =item B<--diffext=>I<EXTENSION>
429 Builds a Ksplice update using the modified source files with names ending in
430 I<EXTENSION>. The patch will be determined by comparing all of the files in the
431 I<KERNEL_SOURCE> directory tree whose names end with the extra extension
432 I<EXTENSION> against the corresponding files without the extra extension.
434 =item B<--git>=I<COMMIT>
436 Builds a Ksplice update using the commit I<COMMIT> in the Git working tree
437 I<KERNEL_SOURCE>. The original state corresponding to the running kernel is
438 remembered in the Git ref B<refs/ksplice/pre>, which will be created from the
439 current B<HEAD> if it does not yet exist (and can be changed using the
440 B<--series> option). Therefore, the source code change to be applied
441 corresponds to the output of B<git diff ksplice/pre> I<COMMIT>.
443 =item B<--prebuild>
445 Compiles the original source code that will be needed to build future Ksplice
446 updates. If any Ksplice updates have previously been built in the
447 I<KERNEL_SOURCE> tree, the source files in the tree are reverted to their
448 original state.
450 =item B<--series>
452 Specifies that the current state of the I<KERNEL_SOURCE> tree should be used as
453 the original source that corresponds to the running kernel. If a Ksplice update
454 has recently been built in the I<KERNEL_SOURCE> tree, this option specifies that
455 the Ksplice update being built should be applied after the previous update in
456 series. This option can be used with B<--prebuild> to forget the previous
457 original state and perform no other action.
459 =item B<--build-modules>
461 For a patch that includes changes to kernel modules, in addition to building a
462 hot update that can be applied to the running kernel, this option will cause
463 B<ksplice-create> to generate a set of new modules based on the updated source
464 code. These modules can be used to replace the kernel modules stored on disk,
465 where they can later be loaded normally after part of the hot update has been
466 applied using L<ksplice-apply(1)> B<--partial>.
468 =item B<-v>, B<--verbose>
470 Causes B<ksplice-create> to print debugging messages about its progress. Using
471 multiple -v options increases the verbosity. The maximum is 2.
473 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
475 Specifies the number of jobs to run simultaneously while performing kernel
476 builds. B<ksplice-create> also honors the environment variable
477 CONCURRENCY_LEVEL.
479 =item B<--patch-opt=>I<OPTIONS>
481 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
482 B<-p1> is passed to B<patch>. If this option is specified, then only the
483 specified options will be passed to B<patch>. This option can be repeated in
484 order to pass multiple options to B<patch>. This option is ignored when the
485 to-be-applied source code patch is specified using B<--diffext>.
487 =item B<--id=>I<ID>
489 Specifies the unique value that will be used as the identifier of the
490 Ksplice update. This identifier will, for example, appear in the name
491 of the update tarball. By default, a random 8-character ID will be
492 generated.
494 =back
496 =head1 SEE ALSO
498 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
500 =head1 BUGS
502 Please report bugs to <PACKAGE_BUGREPORT>.
504 =head1 AUTHORS
506 Jeff Arnold, Anders Kaseorg, and Tim Abbott
508 =head1 COPYRIGHT
510 Copyright (C) 2007-2009 Ksplice, Inc.
512 This is free software and documentation. You can redistribute and/or modify it
513 under the terms of the GNU General Public License, version 2.
515 =cut