Makefile.ksplice: Rewrite ksplice-cow-check as a wrapper function.
[ksplice.git] / ksplice-create.in
blob2405659046975288a2ca539b94a74d4c31be17ae
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", "$libexecdir/ksplice-kernel-utsname", "$kmodsrc/offsets.o");
306 mkdir("inspect");
308 open(CONTENTS, ">", "$ksplice/contents");
309 foreach my $mod (@modules) {
310 (my $target = $mod) =~ s/-/_/g;
311 my $mid = "${kid}_$target";
312 my $module = "ksplice-$mid";
313 my ($new, $old);
314 unless($ksplice_short_name_hack) {
315 $new = "new";
316 $old = "old";
317 } else {
318 $new = "n";
319 $old = "o";
321 rename("$tmpdir/ksplice-modules/extra/$module-$new.ko",
322 "$ksplice/$module-$new.ko");
323 runval_outfile("inspect/$module-$new",
324 "$libexecdir/ksplice-inspect",
325 "$ksplice/$module-$new.ko");
326 rename("$tmpdir/ksplice-modules/extra/$module-$old.ko",
327 "$ksplice/$module-$old.ko");
328 runval_outfile("inspect/$module-$old",
329 "$libexecdir/ksplice-inspect",
330 "$ksplice/$module-$old.ko");
332 print CONTENTS "change $target ksplice_${mid}_$new $module-$new.ko",
333 " ksplice_${mid}_$old $module-$old.ko\n";
335 if ($standalone) {
336 rename("$tmpdir/ksplice-modules/extra/ksplice-$kid.ko", "$ksplice/ksplice-$kid.ko");
337 print CONTENTS "core ksplice_$kid ksplice-$kid.ko\n";
339 if ($build_modules) {
340 foreach my $mod (@modulepaths) {
341 (my $target = basename($mod)) =~ s/-/_/g;
342 print CONTENTS "module $target $mod\n";
346 mkdir("$ksplice/debug");
347 rename("objects", "$ksplice/debug/objects");
348 rename("inspect", "$ksplice/debug/inspect");
349 rename("modules", "$ksplice/modules") if ($build_modules);
350 rename("$kmodsrc", "$ksplice/debug/kmodsrc");
352 close(CONTENTS);
353 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
354 copy("$ksplice.tar.gz", "$origdir/$ksplice.tar.gz");
355 print "Ksplice update tarball written to $ksplice.tar.gz\n";
356 exit(0);
358 =head1 NAME
360 ksplice-create - Create a set of kernel modules for a rebootless kernel update
362 =head1 SYNOPSIS
364 B<ksplice-create> [I<OPTIONS>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
366 B<ksplice-create> [I<OPTIONS>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
368 B<ksplice-create> [I<OPTIONS>] B<--git=>I<COMMIT> I<KERNEL_SOURCE>
370 B<ksplice-create> [I<OPTIONS>] B<--prebuild> I<KERNEL_SOURCE>
372 =head1 DESCRIPTION
374 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
375 will apply a user-specified source code patch to the running binary kernel.
377 Before you use B<ksplice-create> on a patch, you should confirm that the
378 desired source code change does not make any semantic changes to kernel data
379 structures--that is, changes that would require existing instances of kernel
380 data structures to be transformed (e.g., a patch that adds a field to a global
381 data structure would require the existing data structures to change). If you
382 use Ksplice on a patch that changes data structure semantics, Ksplice will not
383 detect the problem and you could experience kernel problems as a result.
385 The to-be-applied source code patch can be specified by providing a L<patch(1)>
386 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
387 (B<--diffext=>I<EXTENSION>).
389 If a file extension is specified, then the desired source code patch will be
390 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
391 whose names end with the extra extension I<EXTENSION> against the corresponding
392 files without the extra extension. Only the new files containing the extra
393 extension in their filenames should be modified.
395 Here is an example of using a file extension to specify a patch:
397 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
398 [edit sys.c.prctl_fixed to include the desired changes]
399 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
401 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
402 original source code. If your Linux distribution applies patches to the Linux
403 kernel during the kernel build process, then those patches must be applied to
404 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
405 directory. B<ksplice-create> will not modify the source code in the
406 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
407 directory tree.
409 I<ORIG_CONFIG> can be used to specify the directory containing the
410 to-be-updated kernel's original F<.config> file and original F<System.map> file
411 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
412 I<KERNEL_SOURCE>B</ksplice>.
414 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
415 close to the compiler and assembler originally used to build the running kernel
416 as possible. If the current compiler and linker are too different from the
417 original compiler and linker, B<ksplice-apply> will abort when applying the
418 update.
420 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
421 containing the desired Ksplice update modules. This tarball will be created in
422 the current directory, and it can be manipulated using the other Ksplice
423 utilities, such as B<ksplice-apply>.
425 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
426 directory, it must build that kernel from scratch, which is much slower than
427 the rest of the update-creation process. B<--prebuild> can be used to perform
428 this initial kernel build without providing a source code patch.
430 In order to patch a function that has previously been patched by Ksplice, the
431 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
432 contains the source for the currently running kernel, including any patches
433 that have previously been applied by Ksplice.
435 =head1 OPTIONS
437 =over 8
439 =item B<--patch=>I<PATCH_FILE>
441 Builds a Ksplice update out of the given L<patch(1)> file I<PATCH_FILE>.
443 =item B<--diffext=>I<EXTENSION>
445 Builds a Ksplice update using the modified source files with names ending in
446 I<EXTENSION>. The patch will be determined by comparing all of the files in the
447 I<KERNEL_SOURCE> directory tree whose names end with the extra extension
448 I<EXTENSION> against the corresponding files without the extra extension.
450 =item B<--git>=I<COMMIT>
452 Builds a Ksplice update using the commit I<COMMIT> in the Git working tree
453 I<KERNEL_SOURCE>. The original state corresponding to the running kernel is
454 remembered in the Git ref B<refs/ksplice/pre>, which will be created from the
455 current B<HEAD> if it does not yet exist (and can be changed using the
456 B<--series> option). Therefore, the source code change to be applied
457 corresponds to the output of B<git diff ksplice/pre> I<COMMIT>.
459 =item B<--prebuild>
461 Compiles the original source code that will be needed to build future Ksplice
462 updates. If any Ksplice updates have previously been built in the
463 I<KERNEL_SOURCE> tree, the source files in the tree are reverted to their
464 original state.
466 =item B<--series>
468 Specifies that the current state of the I<KERNEL_SOURCE> tree should be used as
469 the original source that corresponds to the running kernel. If a Ksplice update
470 has recently been built in the I<KERNEL_SOURCE> tree, this option specifies that
471 the Ksplice update being built should be applied after the previous update in
472 series. This option can be used with B<--prebuild> to forget the previous
473 original state and perform no other action.
475 =item B<--build-modules>
477 For a patch that includes changes to kernel modules, in addition to building a
478 hot update that can be applied to the running kernel, this option will cause
479 B<ksplice-create> to generate a set of new modules based on the updated source
480 code. These modules can be used to replace the kernel modules stored on disk,
481 where they can later be loaded normally after part of the hot update has been
482 applied using L<ksplice-apply(1)> B<--partial>.
484 =item B<-v>, B<--verbose>
486 Causes B<ksplice-create> to print debugging messages about its progress. Using
487 multiple -v options increases the verbosity. The maximum is 2.
489 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
491 Specifies the number of jobs to run simultaneously while performing kernel
492 builds. B<ksplice-create> also honors the environment variable
493 CONCURRENCY_LEVEL.
495 =item B<--patch-opt=>I<OPTIONS>
497 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
498 B<-p1> is passed to B<patch>. If this option is specified, then only the
499 specified options will be passed to B<patch>. This option can be repeated in
500 order to pass multiple options to B<patch>. This option is ignored when the
501 to-be-applied source code patch is specified using B<--diffext>.
503 =item B<--id=>I<ID>
505 Specifies the unique value that will be used as the identifier of the
506 Ksplice update. This identifier will, for example, appear in the name
507 of the update tarball. By default, a random 8-character ID will be
508 generated.
510 =back
512 =head1 SEE ALSO
514 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
516 =head1 BUGS
518 Please report bugs to <PACKAGE_BUGREPORT>.
520 =head1 AUTHORS
522 Jeff Arnold, Anders Kaseorg, and Tim Abbott
524 =head1 COPYRIGHT
526 Copyright (C) 2007-2009 Ksplice, Inc.
528 This is free software and documentation. You can redistribute and/or modify it
529 under the terms of the GNU General Public License, version 2.
531 =cut