Exit with status 66 if no changes are detected.
[ksplice.git] / ksplice-create.in
blobd0597da81876e3cc751748081bfa128e6b897056
1 #!/usr/bin/perl
3 # Copyright (C) 2007-2008 Jeffrey Brian Arnold <jbarnold@mit.edu>
4 # Copyright (C) 2008 Anders Kaseorg <andersk@mit.edu>,
5 # Tim Abbott <tabbott@mit.edu>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License, version 2.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
18 # 02110-1301, USA.
20 use strict;
21 use warnings;
22 use lib 'KSPLICE_DATA_DIR';
23 use Ksplice;
25 my ($patchfile, $diffext, $orig_config_dir, $jobs, $kid);
26 my $description;
27 my $series = 0;
28 my $build_modules = 0;
29 my @only_targets;
30 my @extra_match;
31 my $standalone;
32 my ($prebuild, $skip_prebuild) = (0, 0);
33 my @patch_opt = "-p1";
34 GetOptions(@common_options,
35 "id=s" => \$kid,
36 "patch=s" => \$patchfile,
37 "description=s" => \$description,
38 "diffext=s" => \$diffext,
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) + ($prebuild);
52 pod2usage(1) if($actions != 1);
54 my ($linuxtree) = (abs_path($ARGV[0]));
56 if(!defined $orig_config_dir) {
57 $orig_config_dir = "$linuxtree/ksplice";
59 else {
60 $orig_config_dir = abs_path($orig_config_dir);
61 if($orig_config_dir =~ $linuxtree) {
62 die "Aborting: User-specified ORIG_CONFIG cannot be KERNEL_SOURCE or a subdirectory";
65 if(!defined $orig_config_dir || ! -d $orig_config_dir) {
66 die "Failed to find ORIG_CONFIG directory ($orig_config_dir)";
68 if(! -e "$orig_config_dir/System.map") {
69 die "Failed to find System.map file in ORIG_CONFIG directory";
72 my $kernel_headers_dir = "$orig_config_dir/build";
73 $kernel_headers_dir = $linuxtree unless(-d $kernel_headers_dir);
75 my @kbuild_flags = ();
76 if(-e "$orig_config_dir/flags") {
77 open(FLAGS, '<', "$orig_config_dir/flags") or die;
78 local $/;
79 @kbuild_flags = shellwords(scalar <FLAGS>);
80 close(FLAGS);
83 $ENV{KSPLICE_VERBOSE} = $Verbose::level;
84 $ENV{KSPLICE_CONFIG_DIR} = $orig_config_dir;
86 my @chars = ('a'..'z', 0..9);
87 $kid = join '', map { $chars[int(rand(36))] } 0..7 if(!defined $kid);
88 my $ksplice = "ksplice-$kid";
89 $ENV{KSPLICE_KID} = $kid;
91 # Some versions of Fedora have System.map files whose symbol addresses disagree
92 # with the running kernel by a constant address offset. Here, Ksplice notes the
93 # System.map address for printk so that it can later compare this address against
94 # the kernel's address for printk. This comparison helps Ksplice work around
95 # this Fedora problem, and this comparison also helps Ksplice detect whether
96 # the user has provided an incorrect System.map file.
97 my $map_printk = runstr("$datadir/ksplice-obj.pl", "system_map_lookup", "printk");
99 print "Starting kernel builds (this process might take a long time)...\n"
100 if($Verbose::level >= 0);
102 $patchfile = abs_path($patchfile) if(defined $patchfile);
104 my $origdir = getcwd();
105 chdir($linuxtree);
107 my @make = ("make", "-rR");
108 if(defined $jobs) {
109 push @make, "-j$jobs";
110 } elsif(defined $ENV{CONCURRENCY_LEVEL}) {
111 push @make, "-j$ENV{CONCURRENCY_LEVEL}";
114 if($Verbose::level >= 2) {
115 push @make, "V=1";
116 } elsif($Verbose::level < 0) {
117 push @make, "-s";
120 $ENV{PATH} = "$datadir:$ENV{PATH}";
121 my @make_ksplice = (@make, "-f", "$datadir/Makefile.ksplice", @kbuild_flags);
123 push(@make_ksplice, "KSPLICE_BUILD_MODULES=1") if ($build_modules);
124 if(-e "include/config/kernel.release") {
125 push(@make_ksplice, "-o", "include/config/kernel.release");
128 my @revert_flags = ("KSPLICE_MODE=revert");
129 push(@revert_flags, "KSPLICE_SERIES=y") if ($series);
131 sub revert_orig() {
132 for(split(/\0/, runstr(qw(find -name *.KSPLICE_presrc -print0)))) {
133 my ($file) = m/^(.*)\.KSPLICE_presrc$/;
134 if ($series) {
135 unlink($_);
136 } else {
137 rename($_, $file);
140 runval(@make_ksplice, @revert_flags);
142 revert_orig();
144 if(!$skip_prebuild) {
145 if(-e "$orig_config_dir/.config") {
146 copy("$orig_config_dir/.config", "$linuxtree/.config");
147 utime((stat("$orig_config_dir/.config"))[8, 9],
148 "$linuxtree/.config");
150 my @snap_flags = ("KSPLICE_MODE=snap");
151 runval_raw(@make_ksplice, @snap_flags) == 0 or
152 die "Aborting: Prebuild failed";
153 sleep(1);
155 exit(0) if($prebuild);
157 my $tmpdir = tempdir('ksplice-tmp-XXXXXX', TMPDIR => 1, CLEANUP => 1);
158 copy($patchfile, "$tmpdir/patch") if(defined $patchfile);
159 $patchfile = "$tmpdir/patch";
161 if(defined $diffext) {
162 open(PATCH, '>', $patchfile) or die;
163 for(split(/\0/, runstr("find", "-name", "*$diffext", "-print0"))) {
164 my ($file) = /^(.*)\Q$diffext\E/ or die;
165 print PATCH runstr("diff", "-u", "--", $file, $_);
167 close(PATCH) or die;
168 @patch_opt = ("-p0");
171 my $kmodsrc = "$tmpdir/kmodsrc";
172 runval("cp", "-a", "--", "$datadir/kmodsrc", $kmodsrc);
173 $ENV{KSPLICE_KMODSRC} = $kmodsrc;
175 my @make_kmodsrc = (@make, "-C", $kernel_headers_dir, "M=$kmodsrc", "KSPLICE_KID=$kid", "KSPLICE_VERSION=PACKAGE_VERSION", "map_printk=$map_printk");
177 if (!defined($standalone)) {
178 $standalone = (runval_raw(qw(grep -q ^CONFIG_KSPLICE=[ym]$), "$linuxtree/.config") != 0);
180 push(@make_kmodsrc, "KSPLICE_STANDALONE=1") if ($standalone);
182 runval(@make_kmodsrc);
184 @patch_opt = ("-s", @patch_opt) if ($Verbose::level < 0);
186 runval_infile($patchfile, "patch", @patch_opt, "-bz", ".KSPLICE_presrc");
187 my @diff_flags = ("KSPLICE_MODE=diff");
188 push @diff_flags, "KSPLICE_EXTRA_MATCH=@extra_match" if (@extra_match);
189 push @diff_flags, "KSPLICE_ONLY_TARGETS=@only_targets" if (@only_targets);
190 if(runval_raw(@make_ksplice, @diff_flags) != 0) {
191 revert_orig() if(defined($diffext));
192 die "Aborting: Applying the patch appears to break the kernel build";
195 sub copy_debug {
196 my ($file) = @_;
197 my ($dir, $base) = (dirname($file), basename($file));
198 -d "$tmpdir/objects/$dir" or mkpath("$tmpdir/objects/$dir");
199 copy($file, "$tmpdir/objects/$file");
200 my $cmdfile = "$dir/.$base.cmd";
201 copy($cmdfile, "$tmpdir/objects/$cmdfile") if(-e $cmdfile);
204 mkdir("$tmpdir/objects");
205 for (split(/\0/, runstr(qw(find -name *.KSPLICE* ! ( -name *.KSPLICE -empty ) ! -name .*.KSPLICE.cmd -print0)))) {
206 next if (basename($_) =~ m/^(?:vmlinux|vmlinux\.o|\.tmp_vmlinux[0-9]+|\.tmp_kallsyms[0-9]+.o)\.KSPLICE_pre$/);
207 copy_debug($_);
208 copy_debug($1) if (m/^(.*)\.KSPLICE_pre(?:src)$/);
211 my @modulepaths = ();
212 my @modules = ();
213 foreach(glob("$kmodsrc/*.mod.KSPLICE")) {
214 open MOD, '<', $_;
215 chomp(my $mod = <MOD>);
216 close MOD;
217 push @modulepaths, "$mod.ko" if (basename($mod) ne "vmlinux");
218 push @modules, basename($mod);
221 if(!@modules) {
222 revert_orig() if(defined($diffext));
223 print STDERR "No changes detected.\n";
224 exit(66);
227 if ($build_modules) {
228 mkdir("$tmpdir/modules");
229 runval(@make_ksplice, "KSPLICE_MODE=modinst", "MODLIB=$tmpdir/modules", "modules=@modulepaths");
232 revert_orig() if(defined($diffext));
234 runval(@make_kmodsrc, "modules", "KSPLICE_MODULES=@modules");
236 chdir($tmpdir);
237 mkdir($ksplice);
238 move($patchfile, $ksplice);
239 if ($description) {
240 write_file("$ksplice/description", "$description\n");
242 write_file("$ksplice/api-version", "KSPLICE_API_VERSION\n");
243 write_file("$ksplice/timestamp", time() . "\n");
244 runval_outfile("$ksplice/utsname", "/usr/local/libexec/ksplice-kernel-utsname", "$kmodsrc/offsets.o");
245 open(CONTENTS, ">", "$ksplice/contents");
246 foreach my $mod (@modules) {
247 (my $target = $mod) =~ s/-/_/g;
248 my $mid = "${kid}_$target";
249 my $module = "ksplice-$mid";
250 rename("$kmodsrc/$module.ko", "$ksplice/$module.ko");
251 print CONTENTS "primary $target ksplice_$mid $module.ko\n";
252 rename("$kmodsrc/$module-helper.ko", "$ksplice/$module-helper.ko");
253 print CONTENTS "helper $target ksplice_$mid $module-helper.ko\n";
255 if ($standalone) {
256 rename("$kmodsrc/ksplice-$kid.ko", "$ksplice/ksplice-$kid.ko");
257 print CONTENTS "core ksplice_$kid ksplice-$kid.ko\n";
259 if ($build_modules) {
260 foreach my $mod (@modulepaths) {
261 (my $target = basename($mod)) =~ s/-/_/g;
262 print CONTENTS "target $target $mod\n";
266 mkdir("$ksplice/debug");
267 rename("objects", "$ksplice/debug/objects");
268 rename("modules", "$ksplice/modules") if ($build_modules);
269 rename("$kmodsrc", "$ksplice/debug/kmodsrc");
271 close(CONTENTS);
272 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
273 copy("$ksplice.tar.gz", "$origdir/$ksplice.tar.gz");
274 print "Ksplice update tarball written to $ksplice.tar.gz\n";
275 exit(0);
277 =head1 NAME
279 ksplice-create - Create a set of kernel modules for a rebootless kernel update
281 =head1 SYNOPSIS
283 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
285 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
287 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
289 =head1 DESCRIPTION
291 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
292 will apply a user-specified source code patch to the running binary kernel.
294 Before you use B<ksplice-create> on a patch, you should confirm that the
295 desired source code change does not make any semantic changes to kernel data
296 structures--that is, changes that would require existing instances of kernel
297 data structures to be transformed (e.g., a patch that adds a field to a global
298 data structure would require the existing data structures to change). If you
299 use Ksplice on a patch that changes data structure semantics, Ksplice will not
300 detect the problem and you could experience kernel problems as a result.
302 The to-be-applied source code patch can be specified by providing a L<patch(1)>
303 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
304 (B<--diffext=>I<EXTENSION>).
306 If a file extension is specified, then the desired source code patch will be
307 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
308 whose names end with the extra extension I<EXTENSION> against the corresponding
309 files without the extra extension. Only the new files containing the extra
310 extension in their filenames should be modified.
312 Here is an example of using a file extension to specify a patch:
314 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
315 [edit sys.c.prctl_fixed to include the desired changes]
316 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
318 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
319 original source code. If your Linux distribution applies patches to the Linux
320 kernel during the kernel build process, then those patches must be applied to
321 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
322 directory. B<ksplice-create> will not modify the source code in the
323 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
324 directory tree.
326 I<ORIG_CONFIG> can be used to specify the directory containing the
327 to-be-updated kernel's original F<.config> file and original F<System.map> file
328 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
329 I<KERNEL_SOURCE>B</ksplice>.
331 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
332 close to the compiler and assembler originally used to build the running kernel
333 as possible. If the current compiler and linker are too different from the
334 original compiler and linker, B<ksplice-apply> will abort when applying the
335 update.
337 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
338 containing the desired Ksplice update modules. This tarball will be created in
339 the current directory, and it can be manipulated using the other Ksplice
340 utilities, such as B<ksplice-apply>.
342 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
343 directory, it must build that kernel from scratch, which is much slower than
344 the rest of the update-creation process. B<--prebuild> can be used to perform
345 this initial kernel build without providing a source code patch.
347 In order to patch a function that has previously been patched by Ksplice, the
348 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
349 contains the source for the currently running kernel, including any patches
350 that have previously been applied by Ksplice.
352 =head1 OPTIONS
354 =over 8
356 =item B<-v>, B<--verbose>
358 Causes B<ksplice-create> to print debugging messages about its progress. Using
359 multiple -v options increases the verbosity. The maximum is 2.
361 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
363 Specifies the number of jobs to run simultaneously while performing kernel
364 builds. B<ksplice-create> also honors the environment variable
365 CONCURRENCY_LEVEL.
367 =item B<--patch-opt=>I<OPTIONS>
369 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
370 B<-p1> is passed to B<patch>. If this option is specified, then only the
371 specified options will be passed to B<patch>. This option can be repeated in
372 order to pass multiple options to B<patch>. This option is ignored when the
373 to-be-applied source code patch is specified using B<--diffext>.
375 =item B<--id=>I<ID>
377 Specifies the unique value that will be used as the identifier of the
378 Ksplice update. This identifier will, for example, appear in the name
379 of the update tarball. By default, a random 8-character ID will be
380 generated.
382 =back
384 =head1 SEE ALSO
386 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
388 =head1 BUGS
390 Please report bugs to <PACKAGE_BUGREPORT>.
392 =head1 COPYRIGHT
394 Copyright (C) 2007-2008 Jeffrey Brian Arnold <jbarnold@mit.edu>
396 Copyright (C) 2008 Anders Kaseorg <andersk@mit.edu>,
397 Tim Abbott <tabbott@mit.edu>
399 This is free software and documentation. You can redistribute and/or modify it
400 under the terms of the GNU General Public License, version 2.
402 =cut