Add ksplice-create --git option to build Ksplice updates from Git commits.
[ksplice.git] / ksplice-create.in
blob43833c59b2e106a32533d901651498f4b67dfa37
1 #!/usr/bin/perl
3 # Copyright (C) 2007-2008 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 @only_targets;
29 my @extra_match;
30 my $standalone;
31 my ($prebuild, $skip_prebuild) = (0, 0);
32 my @patch_opt = "-p1";
33 GetOptions(@common_options,
34 "id=s" => \$kid,
35 "patch=s" => \$patchfile,
36 "description=s" => \$description,
37 "diffext=s" => \$diffext,
38 "git=s" => \$git,
39 "prebuild" => \$prebuild,
40 "series!" => \$series,
41 "build-modules!" => \$build_modules,
42 "only-targets=s" => \@only_targets,
43 "extra-match=s" => \@extra_match,
44 "standalone!" => \$standalone,
45 "skip-prebuild" => \$skip_prebuild,
46 "jobs|j:i" => \$jobs,
47 "config=s" => \$orig_config_dir,
48 "patch-opt=s" => \@patch_opt) or pod2usage(1);
50 pod2usage(1) if($help || scalar(@ARGV) != 1);
51 my $actions = (defined $patchfile) + (defined $diffext) + (defined $git) + $prebuild;
52 pod2usage(1) if($actions != 1);
54 my $have_Git = eval { require Git };
55 $have_Git or die $@ if defined $git;
57 my ($linuxtree) = (abs_path($ARGV[0]));
59 if(!defined $orig_config_dir) {
60 $orig_config_dir = "$linuxtree/ksplice";
62 else {
63 $orig_config_dir = abs_path($orig_config_dir);
64 if($orig_config_dir =~ $linuxtree) {
65 die "Aborting: User-specified ORIG_CONFIG cannot be KERNEL_SOURCE or a subdirectory";
68 if(!defined $orig_config_dir || ! -d $orig_config_dir) {
69 die "Failed to find ORIG_CONFIG directory ($orig_config_dir)";
71 if(! -e "$orig_config_dir/System.map") {
72 die "Failed to find System.map file in ORIG_CONFIG directory";
75 my $kernel_headers_dir = "$orig_config_dir/build";
76 $kernel_headers_dir = $linuxtree unless(-d $kernel_headers_dir);
78 my @kbuild_flags = ();
79 if(-e "$orig_config_dir/flags") {
80 open(FLAGS, '<', "$orig_config_dir/flags") or die;
81 local $/;
82 @kbuild_flags = shellwords(scalar <FLAGS>);
83 close(FLAGS);
86 $ENV{KSPLICE_VERBOSE} = $Verbose::level;
87 $ENV{KSPLICE_CONFIG_DIR} = $orig_config_dir;
89 my @chars = ('a'..'z', 0..9);
90 $kid = join '', map { $chars[int(rand(36))] } 0..7 if(!defined $kid);
91 my $ksplice = "ksplice-$kid";
92 $ENV{KSPLICE_KID} = $kid;
94 # Some versions of Fedora have System.map files whose symbol addresses disagree
95 # with the running kernel by a constant address offset. Here, Ksplice notes the
96 # System.map address for printk so that it can later compare this address against
97 # the kernel's address for printk. This comparison helps Ksplice work around
98 # this Fedora problem, and this comparison also helps Ksplice detect whether
99 # the user has provided an incorrect System.map file.
100 my $map_printk = runstr("$datadir/ksplice-obj.pl", "system_map_lookup", "printk");
102 print "Starting kernel builds (this process might take a long time)...\n"
103 if($Verbose::level >= 0);
105 $patchfile = abs_path($patchfile) if(defined $patchfile);
107 my $origdir = getcwd();
108 chdir($linuxtree);
110 my $git_rev;
111 my $git_repo;
112 if (defined $git) {
113 $git_repo = Git->repository();
114 ($git_rev) = $git_repo->command(qw(rev-parse --verify), $git);
115 } else {
116 $git_repo = eval { Git->repository() };
119 my @make = ("make", "-rR");
120 if(defined $jobs) {
121 push @make, "-j$jobs";
122 } elsif(defined $ENV{CONCURRENCY_LEVEL}) {
123 push @make, "-j$ENV{CONCURRENCY_LEVEL}";
126 if($Verbose::level >= 2) {
127 push @make, "V=1";
128 } elsif($Verbose::level < 0) {
129 push @make, "-s";
132 $ENV{PATH} = "$datadir:$ENV{PATH}";
133 my @make_ksplice = (@make, "-f", "$datadir/Makefile.ksplice", @kbuild_flags);
135 push(@make_ksplice, "KSPLICE_BUILD_MODULES=1") if ($build_modules);
136 if(-e "include/config/kernel.release") {
137 push(@make_ksplice, "-o", "include/config/kernel.release");
140 my @revert_flags = ("KSPLICE_MODE=revert");
141 push(@revert_flags, "KSPLICE_SERIES=y") if ($series);
143 sub git_have_ksplice_pre {
144 return $git_repo->command(qw(for-each-ref refs/ksplice/pre)) ne '';
147 sub revert_orig() {
148 for(split(/\0/, runstr(qw(find -name *.KSPLICE_presrc -print0)))) {
149 my ($file) = m/^(.*)\.KSPLICE_presrc$/;
150 if ($series) {
151 unlink($_);
152 } else {
153 rename($_, $file);
156 if (defined $git_repo && git_have_ksplice_pre) {
157 if ($series) {
158 $git_repo->command_noisy(qw(update-ref -m), "ksplice-create: freeze", qw(refs/ksplice/pre HEAD refs/ksplice/pre));
159 } else {
160 $git_repo->command_noisy(qw(update-index --refresh));
161 $git_repo->command_noisy(qw(read-tree -m --trivial -u refs/ksplice/pre));
162 $git_repo->command_noisy(qw(update-ref -m), "ksplice-create: revert", qw(HEAD refs/ksplice/pre HEAD));
165 runval(@make_ksplice, @revert_flags);
167 revert_orig();
169 if (defined $git_repo && !git_have_ksplice_pre) {
170 $git_repo->command_noisy(qw(update-ref -m), "ksplice-create: snap", qw(refs/ksplice/pre HEAD), '');
173 if(!$skip_prebuild) {
174 if(-e "$orig_config_dir/.config") {
175 copy("$orig_config_dir/.config", "$linuxtree/.config");
176 utime((stat("$orig_config_dir/.config"))[8, 9],
177 "$linuxtree/.config");
179 my @snap_flags = ("KSPLICE_MODE=snap");
180 runval_raw(@make_ksplice, @snap_flags) == 0 or
181 die "Aborting: Prebuild failed";
182 sleep(1);
184 exit(0) if($prebuild);
186 my $tmpdir = tempdir('ksplice-tmp-XXXXXX', TMPDIR => 1, CLEANUP => 1);
187 copy($patchfile, "$tmpdir/patch") if(defined $patchfile);
188 $patchfile = "$tmpdir/patch";
190 if(defined $diffext) {
191 open(PATCH, '>', $patchfile) or die;
192 for(split(/\0/, runstr("find", "-name", "*$diffext", "-print0"))) {
193 my ($file) = /^(.*)\Q$diffext\E/ or die;
194 print PATCH runstr("diff", "-u", "--", $file, $_);
196 close(PATCH) or die;
197 @patch_opt = ("-p0");
200 my $kmodsrc = "$tmpdir/kmodsrc";
201 runval("cp", "-a", "--", "$datadir/kmodsrc", $kmodsrc);
202 $ENV{KSPLICE_KMODSRC} = $kmodsrc;
204 my @make_kmodsrc = (@make, "-C", $kernel_headers_dir, "M=$kmodsrc", "KSPLICE_KID=$kid", "KSPLICE_VERSION=PACKAGE_VERSION", "map_printk=$map_printk");
206 if (!defined($standalone)) {
207 $standalone = (runval_raw(qw(grep -q ^CONFIG_KSPLICE=[ym]$), "$linuxtree/.config") != 0);
209 push(@make_kmodsrc, "KSPLICE_STANDALONE=1") if ($standalone);
211 runval(@make_kmodsrc);
213 @patch_opt = ("-s", @patch_opt) if ($Verbose::level < 0);
215 if (defined $git) {
216 if (!git_have_ksplice_pre) {
217 $git_repo->command_noisy(qw(update-ref -m), "ksplice-create: snap", qw(refs/ksplice/pre HEAD), '');
219 $git_repo->command_noisy(qw(update-index --refresh));
220 $git_repo->command_noisy(qw(read-tree -m --trivial -u), $git_rev);
221 $git_repo->command_noisy(qw(update-ref -m), "ksplice-create: diff", "HEAD", $git_rev);
223 open(PATCH, '>', $patchfile) or die;
224 if ($git_repo->command(qw(rev-parse --verify HEAD^)) eq
225 $git_repo->command(qw(rev-parse --verify refs/ksplice/pre))) {
226 print PATCH scalar($git_repo->command(qw(log -1 HEAD --))), "\n";
228 print PATCH scalar($git_repo->command(qw(diff-tree -p refs/ksplice/pre HEAD --)));
229 close(PATCH) or die;
230 } else {
231 runval_infile($patchfile, "patch", @patch_opt, "-bz", ".KSPLICE_presrc");
234 my @diff_flags = ("KSPLICE_MODE=diff");
235 push @diff_flags, "KSPLICE_EXTRA_MATCH=@extra_match" if (@extra_match);
236 push @diff_flags, "KSPLICE_ONLY_TARGETS=@only_targets" if (@only_targets);
237 if(runval_raw(@make_ksplice, @diff_flags) != 0) {
238 revert_orig() if(defined($diffext));
239 die "Aborting: Applying the patch appears to break the kernel build";
242 sub copy_debug {
243 my ($file) = @_;
244 my ($dir, $base) = (dirname($file), basename($file));
245 -d "$tmpdir/objects/$dir" or mkpath("$tmpdir/objects/$dir");
246 copy($file, "$tmpdir/objects/$file");
247 my $cmdfile = "$dir/.$base.cmd";
248 copy($cmdfile, "$tmpdir/objects/$cmdfile") if(-e $cmdfile);
251 mkdir("$tmpdir/objects");
252 for (split(/\0/, runstr(qw(find -name *.KSPLICE* ! ( -name *.KSPLICE -empty ) ! -name .*.KSPLICE.cmd -print0)))) {
253 next if (basename($_) =~ m/^(?:vmlinux|vmlinux\.o|\.tmp_vmlinux[0-9]+|\.tmp_kallsyms[0-9]+.o)\.KSPLICE_pre$/);
254 copy_debug($_);
255 copy_debug($1) if (m/^(.*)\.KSPLICE_pre(?:src)$/);
258 my @modulepaths = ();
259 my @modules = ();
260 foreach(glob("$kmodsrc/*.mod.KSPLICE")) {
261 open MOD, '<', $_;
262 chomp(my $mod = <MOD>);
263 close MOD;
264 push @modulepaths, "$mod.ko" if (basename($mod) ne "vmlinux");
265 push @modules, basename($mod);
268 if(!@modules) {
269 revert_orig() if(defined($diffext));
270 print STDERR "No changes detected.\n";
271 exit(66);
274 if ($build_modules) {
275 mkdir("$tmpdir/modules");
276 runval(@make_ksplice, "KSPLICE_MODE=modinst", "MODLIB=$tmpdir/modules", "INSTALL_MOD_STRIP=1", "modules=@modulepaths");
279 revert_orig() if(defined($diffext));
281 runval(@make_kmodsrc, "modules", "KSPLICE_MODULES=@modules");
282 runval(@make_kmodsrc, qw(modules_install --old-file=_modinst_post --old-file=_emodinst_post INSTALL_MOD_STRIP=1), "MODLIB=$tmpdir/ksplice-modules", "KSPLICE_MODULES=@modules");
284 chdir($tmpdir);
285 mkdir($ksplice);
286 move($patchfile, $ksplice);
287 if ($description) {
288 write_file("$ksplice/description", "$description\n");
290 write_file("$ksplice/api-version", "KSPLICE_API_VERSION\n");
291 write_file("$ksplice/timestamp", time() . "\n");
292 runval_outfile("$ksplice/utsname", "/usr/local/libexec/ksplice-kernel-utsname", "$kmodsrc/offsets.o");
293 open(CONTENTS, ">", "$ksplice/contents");
294 foreach my $mod (@modules) {
295 (my $target = $mod) =~ s/-/_/g;
296 my $mid = "${kid}_$target";
297 my $module = "ksplice-$mid";
298 rename("$tmpdir/ksplice-modules/extra/$module.ko", "$ksplice/$module.ko");
299 print CONTENTS "primary $target ksplice_$mid $module.ko\n";
300 rename("$tmpdir/ksplice-modules/extra/$module-helper.ko", "$ksplice/$module-helper.ko");
301 print CONTENTS "helper $target ksplice_$mid $module-helper.ko\n";
303 if ($standalone) {
304 rename("$tmpdir/ksplice-modules/extra/ksplice-$kid.ko", "$ksplice/ksplice-$kid.ko");
305 print CONTENTS "core ksplice_$kid ksplice-$kid.ko\n";
307 if ($build_modules) {
308 foreach my $mod (@modulepaths) {
309 (my $target = basename($mod)) =~ s/-/_/g;
310 print CONTENTS "target $target $mod\n";
314 mkdir("$ksplice/debug");
315 rename("objects", "$ksplice/debug/objects");
316 rename("modules", "$ksplice/modules") if ($build_modules);
317 rename("$kmodsrc", "$ksplice/debug/kmodsrc");
319 close(CONTENTS);
320 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
321 copy("$ksplice.tar.gz", "$origdir/$ksplice.tar.gz");
322 print "Ksplice update tarball written to $ksplice.tar.gz\n";
323 exit(0);
325 =head1 NAME
327 ksplice-create - Create a set of kernel modules for a rebootless kernel update
329 =head1 SYNOPSIS
331 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
333 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
335 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
337 =head1 DESCRIPTION
339 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
340 will apply a user-specified source code patch to the running binary kernel.
342 Before you use B<ksplice-create> on a patch, you should confirm that the
343 desired source code change does not make any semantic changes to kernel data
344 structures--that is, changes that would require existing instances of kernel
345 data structures to be transformed (e.g., a patch that adds a field to a global
346 data structure would require the existing data structures to change). If you
347 use Ksplice on a patch that changes data structure semantics, Ksplice will not
348 detect the problem and you could experience kernel problems as a result.
350 The to-be-applied source code patch can be specified by providing a L<patch(1)>
351 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
352 (B<--diffext=>I<EXTENSION>).
354 If a file extension is specified, then the desired source code patch will be
355 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
356 whose names end with the extra extension I<EXTENSION> against the corresponding
357 files without the extra extension. Only the new files containing the extra
358 extension in their filenames should be modified.
360 Here is an example of using a file extension to specify a patch:
362 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
363 [edit sys.c.prctl_fixed to include the desired changes]
364 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
366 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
367 original source code. If your Linux distribution applies patches to the Linux
368 kernel during the kernel build process, then those patches must be applied to
369 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
370 directory. B<ksplice-create> will not modify the source code in the
371 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
372 directory tree.
374 I<ORIG_CONFIG> can be used to specify the directory containing the
375 to-be-updated kernel's original F<.config> file and original F<System.map> file
376 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
377 I<KERNEL_SOURCE>B</ksplice>.
379 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
380 close to the compiler and assembler originally used to build the running kernel
381 as possible. If the current compiler and linker are too different from the
382 original compiler and linker, B<ksplice-apply> will abort when applying the
383 update.
385 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
386 containing the desired Ksplice update modules. This tarball will be created in
387 the current directory, and it can be manipulated using the other Ksplice
388 utilities, such as B<ksplice-apply>.
390 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
391 directory, it must build that kernel from scratch, which is much slower than
392 the rest of the update-creation process. B<--prebuild> can be used to perform
393 this initial kernel build without providing a source code patch.
395 In order to patch a function that has previously been patched by Ksplice, the
396 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
397 contains the source for the currently running kernel, including any patches
398 that have previously been applied by Ksplice.
400 =head1 OPTIONS
402 =over 8
404 =item B<-v>, B<--verbose>
406 Causes B<ksplice-create> to print debugging messages about its progress. Using
407 multiple -v options increases the verbosity. The maximum is 2.
409 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
411 Specifies the number of jobs to run simultaneously while performing kernel
412 builds. B<ksplice-create> also honors the environment variable
413 CONCURRENCY_LEVEL.
415 =item B<--patch-opt=>I<OPTIONS>
417 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
418 B<-p1> is passed to B<patch>. If this option is specified, then only the
419 specified options will be passed to B<patch>. This option can be repeated in
420 order to pass multiple options to B<patch>. This option is ignored when the
421 to-be-applied source code patch is specified using B<--diffext>.
423 =item B<--id=>I<ID>
425 Specifies the unique value that will be used as the identifier of the
426 Ksplice update. This identifier will, for example, appear in the name
427 of the update tarball. By default, a random 8-character ID will be
428 generated.
430 =back
432 =head1 SEE ALSO
434 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
436 =head1 BUGS
438 Please report bugs to <PACKAGE_BUGREPORT>.
440 =head1 AUTHORS
442 Jeff Arnold, Anders Kaseorg, and Tim Abbott
444 =head1 COPYRIGHT
446 Copyright (C) 2007-2008 Ksplice, Inc.
448 This is free software and documentation. You can redistribute and/or modify it
449 under the terms of the GNU General Public License, version 2.
451 =cut