Add --series option for building series of updates.
[ksplice.git] / ksplice-create.in
blob2fb05ddbffb684ee7c01dbe7764941386e53d04c
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 $series = 0;
27 my @only_targets;
28 my @extra_match;
29 my $standalone;
30 my ($prebuild, $skip_prebuild) = (0, 0);
31 my @patch_opt = "-p1";
32 GetOptions(@common_options,
33 "id=s" => \$kid,
34 "patch=s" => \$patchfile,
35 "diffext=s" => \$diffext,
36 "prebuild" => \$prebuild,
37 "series!" => \$series,
38 "only-targets=s" => \@only_targets,
39 "extra-match=s" => \@extra_match,
40 "standalone!" => \$standalone,
41 "skip-prebuild" => \$skip_prebuild,
42 "jobs|j:i" => \$jobs,
43 "config=s" => \$orig_config_dir,
44 "patch-opt=s" => \@patch_opt) or pod2usage(1);
46 pod2usage(1) if($help || scalar(@ARGV) != 1);
47 my $actions = (defined $patchfile) + (defined $diffext) + ($prebuild);
48 pod2usage(1) if($actions != 1);
50 my ($linuxtree) = (abs_path($ARGV[0]));
52 if(!defined $orig_config_dir) {
53 $orig_config_dir = "$linuxtree/ksplice";
55 else {
56 $orig_config_dir = abs_path($orig_config_dir);
57 if($orig_config_dir =~ $linuxtree) {
58 die "Aborting: User-specified ORIG_CONFIG cannot be KERNEL_SOURCE or a subdirectory";
61 if(!defined $orig_config_dir || ! -d $orig_config_dir) {
62 die "Failed to find ORIG_CONFIG directory ($orig_config_dir)";
64 if(! -e "$orig_config_dir/.config") {
65 die "Failed to find .config file in ORIG_CONFIG directory";
67 if(! -e "$orig_config_dir/System.map") {
68 die "Failed to find System.map file in ORIG_CONFIG directory";
71 my $kernel_headers_dir = "$orig_config_dir/build";
72 $kernel_headers_dir = $linuxtree unless(-d $kernel_headers_dir);
74 my @kbuild_flags = ();
75 if(-e "$orig_config_dir/flags") {
76 open(FLAGS, '<', "$orig_config_dir/flags") or die;
77 local $/;
78 @kbuild_flags = shellwords(scalar <FLAGS>);
79 close(FLAGS);
82 $ENV{KSPLICE_VERBOSE} = $Verbose::level;
83 $ENV{KSPLICE_CONFIG_DIR} = $orig_config_dir;
85 my @chars = ('a'..'z', 0..9);
86 $kid = join '', map { $chars[int(rand(36))] } 0..7 if(!defined $kid);
87 my $ksplice = "ksplice-$kid";
88 $ENV{KSPLICE_KID} = $kid;
90 # Some versions of Fedora have System.map files whose symbol addresses disagree
91 # with the running kernel by a constant address offset. Here, Ksplice notes the
92 # System.map address for printk so that it can later compare this address against
93 # the kernel's address for printk. This comparison helps Ksplice work around
94 # this Fedora problem, and this comparison also helps Ksplice detect whether
95 # the user has provided an incorrect System.map file.
96 my $map_printk = runstr("$datadir/ksplice-obj.pl", "system_map_lookup", "printk");
98 print "Starting kernel builds (this process might take a long time)...\n"
99 if($Verbose::level >= 0);
101 $patchfile = abs_path($patchfile) if(defined $patchfile);
103 my $origdir = getcwd();
104 chdir($linuxtree);
106 my @make = ("make", "-rR");
107 if(defined $jobs) {
108 push @make, "-j$jobs";
109 } elsif(defined $ENV{CONCURRENCY_LEVEL}) {
110 push @make, "-j$ENV{CONCURRENCY_LEVEL}";
113 if($Verbose::level >= 2) {
114 push @make, "V=1";
115 } elsif($Verbose::level < 0) {
116 push @make, "-s";
119 $ENV{PATH} = "$datadir:$ENV{PATH}";
120 my @make_ksplice = (@make, "-f", "$datadir/Makefile.ksplice", @kbuild_flags);
122 sub revert_orig() {
123 for(split(/\0/, runstr(qw(find -name *.KSPLICE_* -print0)))) {
124 if(my ($file) = m/^(.*)\.KSPLICE_pre$/) {
125 if ($series) {
126 unlink($_);
127 } else {
128 rename($_, $file);
130 runval("$datadir/ksplice-obj.pl", "snap", "$file.KSPLICE") if(-e "$file.KSPLICE");
131 } elsif(m/\.KSPLICE_(?:primary|helper)$/) {
132 unlink($_) if(-e $_);
136 revert_orig();
138 if(!$skip_prebuild) {
139 copy("$orig_config_dir/.config", "$linuxtree/.config");
140 utime((stat("$orig_config_dir/.config"))[8, 9], "$linuxtree/.config");
141 my @snap_flags = ("KSPLICE_MODE=snap");
142 if(-e "include/config/kernel.release") {
143 push(@snap_flags, "-o", "include/config/kernel.release");
145 runval_raw(@make_ksplice, @snap_flags) == 0 or
146 die "Aborting: Prebuild failed";
147 sleep(1);
149 exit(0) if($prebuild);
151 my $tmpdir = tempdir('ksplice-tmp-XXXXXX', TMPDIR => 1, CLEANUP => 1);
152 copy($patchfile, "$tmpdir/patch") if(defined $patchfile);
153 $patchfile = "$tmpdir/patch";
155 if(defined $diffext) {
156 open(PATCH, '>', $patchfile) or die;
157 for(split(/\0/, runstr("find", "-name", "*$diffext", "-print0"))) {
158 my ($file) = /^(.*)\Q$diffext\E/ or die;
159 print PATCH runstr("diff", "-u", "--", $file, $_);
161 close(PATCH) or die;
162 @patch_opt = ("-p0");
165 my $kmodsrc = "$tmpdir/kmodsrc";
166 runval("cp", "-a", "--", "$datadir/kmodsrc", $kmodsrc);
167 $ENV{KSPLICE_KMODSRC} = $kmodsrc;
169 my @make_kmodsrc = (@make, "-C", $kernel_headers_dir, "M=$kmodsrc", "KSPLICE_KID=$kid", "KSPLICE_VERSION=PACKAGE_VERSION", "map_printk=$map_printk");
171 if (!defined($standalone)) {
172 $standalone = (runval_raw(qw(grep -q ^CONFIG_KSPLICE=[ym]$), "$linuxtree/.config") != 0);
174 push(@make_kmodsrc, "KSPLICE_STANDALONE=1") if ($standalone);
176 runval(@make_kmodsrc);
178 @patch_opt = ("-s", @patch_opt) if ($Verbose::level < 0);
180 runval_infile($patchfile, "patch", @patch_opt, "-bz", ".KSPLICE_pre");
181 push @make_ksplice, "KSPLICE_EXTRA_MATCH=@extra_match" if (@extra_match);
182 push @make_ksplice, "KSPLICE_ONLY_TARGETS=@only_targets" if (@only_targets);
183 if(runval_raw(@make_ksplice, "KSPLICE_MODE=diff") != 0) {
184 revert_orig() if(defined($diffext));
185 die "Aborting: Applying the patch appears to break the kernel build";
188 sub copy_debug {
189 my ($file) = @_;
190 my ($dir, $base) = (dirname($file), basename($file));
191 -d "$tmpdir/objects/$dir" or mkpath("$tmpdir/objects/$dir");
192 copy($file, "$tmpdir/objects/$file");
193 my $cmdfile = "$dir/.$base.cmd";
194 copy($cmdfile, "$tmpdir/objects/$cmdfile") if(-e $cmdfile);
197 mkdir("$tmpdir/objects");
198 for (split(/\0/, runstr(qw(find -name *.KSPLICE* ! ( -name *.KSPLICE -empty ) ! -name .*.KSPLICE.cmd -print0)))) {
199 copy_debug($_);
200 copy_debug($1) if (m/^(.*)\.KSPLICE_pre$/);
203 my @modules = ();
204 foreach(glob(".tmp_versions/*.mod.KSPLICE")) {
205 open MOD, '<', $_;
206 chomp(my $mod = <MOD>);
207 close MOD;
208 foreach my $collect ("$mod.o.KSPLICE", "$mod.o.KSPLICE_primary",
209 "$mod.o.KSPLICE_helper") {
210 copy($collect, "$kmodsrc/" . basename($collect));
212 push @modules, basename($mod);
215 if(!@modules) {
216 revert_orig() if(defined($diffext));
217 die "Aborting: No changes detected";
220 revert_orig() if(defined($diffext));
222 runval(@make_kmodsrc, "modules", "KSPLICE_MODULES=@modules");
224 chdir($tmpdir);
225 mkdir($ksplice);
226 move($patchfile, $ksplice);
227 foreach my $mod (@modules) {
228 (my $target = $mod) =~ s/-/_/g;
229 my $mid = "${kid}_$target";
230 my $module = "ksplice-$mid";
231 rename("$kmodsrc/$module.ko", "$ksplice/$module.ko");
232 rename("$kmodsrc/$module-helper.ko", "$ksplice/$module-helper.ko");
234 rename("$kmodsrc/ksplice-$kid.ko", "$ksplice/ksplice-$kid.ko") if ($standalone);
236 mkdir("$ksplice/debug");
237 rename("objects", "$ksplice/debug/objects");
238 rename("$kmodsrc", "$ksplice/debug/kmodsrc");
239 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
240 copy("$ksplice.tar.gz", "$origdir/$ksplice.tar.gz");
242 print "Ksplice update tarball written to $ksplice.tar.gz\n";
243 exit(0);
245 =head1 NAME
247 ksplice-create - Create a set of kernel modules for a rebootless kernel update
249 =head1 SYNOPSIS
251 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
253 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
255 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
257 =head1 DESCRIPTION
259 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
260 will apply a user-specified source code patch to the running binary kernel.
262 Before you use B<ksplice-create> on a patch, you should confirm that the
263 desired source code change does not make any semantic changes to kernel data
264 structures--that is, changes that would require existing instances of kernel
265 data structures to be transformed (e.g., a patch that adds a field to a global
266 data structure would require the existing data structures to change). If you
267 use Ksplice on a patch that changes data structure semantics, Ksplice will not
268 detect the problem and you could experience kernel problems as a result.
270 The to-be-applied source code patch can be specified by providing a L<patch(1)>
271 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
272 (B<--diffext=>I<EXTENSION>).
274 If a file extension is specified, then the desired source code patch will be
275 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
276 whose names end with the extra extension I<EXTENSION> against the corresponding
277 files without the extra extension. Only the new files containing the extra
278 extension in their filenames should be modified.
280 Here is an example of using a file extension to specify a patch:
282 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
283 [edit sys.c.prctl_fixed to include the desired changes]
284 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
286 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
287 original source code. If your Linux distribution applies patches to the Linux
288 kernel during the kernel build process, then those patches must be applied to
289 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
290 directory. B<ksplice-create> will not modify the source code in the
291 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
292 directory tree.
294 I<ORIG_CONFIG> can be used to specify the directory containing the
295 to-be-updated kernel's original F<.config> file and original F<System.map> file
296 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
297 I<KERNEL_SOURCE>B</ksplice>.
299 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
300 close to the compiler and assembler originally used to build the running kernel
301 as possible. If the current compiler and linker are too different from the
302 original compiler and linker, B<ksplice-apply> will abort when applying the
303 update.
305 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
306 containing the desired Ksplice update modules. This tarball will be created in
307 the current directory, and it can be manipulated using the other Ksplice
308 utilities, such as B<ksplice-apply>.
310 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
311 directory, it must build that kernel from scratch, which is much slower than
312 the rest of the update-creation process. B<--prebuild> can be used to perform
313 this initial kernel build without providing a source code patch.
315 In order to patch a function that has previously been patched by Ksplice, the
316 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
317 contains the source for the currently running kernel, including any patches
318 that have previously been applied by Ksplice.
320 =head1 OPTIONS
322 =over 8
324 =item B<-v>, B<--verbose>
326 Causes B<ksplice-create> to print debugging messages about its progress. Using
327 multiple -v options increases the verbosity. The maximum is 2.
329 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
331 Specifies the number of jobs to run simultaneously while performing kernel
332 builds. B<ksplice-create> also honors the environment variable
333 CONCURRENCY_LEVEL.
335 =item B<--patch-opt=>I<OPTIONS>
337 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
338 B<-p1> is passed to B<patch>. If this option is specified, then only the
339 specified options will be passed to B<patch>. This option can be repeated in
340 order to pass multiple options to B<patch>. This option is ignored when the
341 to-be-applied source code patch is specified using B<--diffext>.
343 =item B<--id=>I<ID>
345 Specifies the unique value that will be used as the identifier of the
346 Ksplice update. This identifier will, for example, appear in the name
347 of the update tarball. By default, a random 8-character ID will be
348 generated.
350 =back
352 =head1 SEE ALSO
354 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
356 =head1 BUGS
358 Please report bugs to <PACKAGE_BUGREPORT>.
360 =head1 COPYRIGHT
362 Copyright (C) 2007-2008 Jeffrey Brian Arnold <jbarnold@mit.edu>
364 Copyright (C) 2008 Anders Kaseorg <andersk@mit.edu>,
365 Tim Abbott <tabbott@mit.edu>
367 This is free software and documentation. You can redistribute and/or modify it
368 under the terms of the GNU General Public License, version 2.
370 =cut