Replace find_nameval(*, *, 1) with a new function create_nameval.
[ksplice.git] / ksplice-create.in
blobe287124ee265d274105648dd075e647df041dc13
1 #!/usr/bin/perl
3 # Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License, version 2.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
16 # 02110-1301, USA.
18 use strict;
19 use warnings;
20 use lib 'KSPLICE_DATA_DIR';
21 use Ksplice;
23 my ($patchfile, $diffext, $orig_config_dir, $jobs, $kid);
24 my $standalone;
25 my ($help, $wantversion, $prebuild, $skip_prebuild, $apply, $patch_opt) = (0, 0, 0, 0, 0, "-p1");
26 GetOptions("help|?" => \$help,
27 "version" => \$wantversion,
28 "verbose|v:+" => \$Verbose::level,
29 "id=s" => \$kid,
30 "patch=s" => \$patchfile,
31 "diffext=s" => \$diffext,
32 "prebuild" => \$prebuild,
33 "standalone!" => \$standalone,
34 "skip-prebuild" => \$skip_prebuild,
35 "jobs|j:i" => \$jobs,
36 "config=s" => \$orig_config_dir,
37 "apply" => \$apply,
38 "patch-opt=s@" => \$patch_opt) or pod2usage(1);
40 if($wantversion) {
41 print $version_str;
42 exit(0);
44 pod2usage(1) if($help || scalar(@ARGV) != 1);
45 my $actions = (defined $patchfile) + (defined $diffext) + ($prebuild);
46 pod2usage(1) if($actions != 1);
48 my ($linuxtree) = (abs_path($ARGV[0]));
50 my $tmpdir = tempdir('ksplice-tmp-XXXXXX', TMPDIR => 1, CLEANUP => 1);
51 copy($patchfile, "$tmpdir/patch") if(defined $patchfile);
52 $patchfile = "$tmpdir/patch";
54 $patch_opt = "-p0" if(defined $diffext);
55 $patch_opt = join(" ", @$patch_opt) if(ref $patch_opt);
57 if(!defined $orig_config_dir) {
58 $orig_config_dir = "$linuxtree/ksplice";
60 else {
61 $orig_config_dir = abs_path($orig_config_dir);
62 if($orig_config_dir =~ $linuxtree) {
63 die "Aborting: User-specified ORIG_CONFIG cannot be KERNEL_SOURCE or a subdirectory";
66 if(!defined $orig_config_dir || ! -d $orig_config_dir) {
67 die "Failed to find ORIG_CONFIG directory ($orig_config_dir)";
69 if(! -e "$orig_config_dir/.config") {
70 die "Failed to find .config file in ORIG_CONFIG directory";
72 if(! -e "$orig_config_dir/System.map") {
73 die "Failed to find System.map file in ORIG_CONFIG directory";
76 my $kernel_headers_dir = "$orig_config_dir/build";
77 $kernel_headers_dir = $linuxtree unless(-d $kernel_headers_dir);
79 my @kbuild_flags = ();
80 if(-e "$orig_config_dir/flags") {
81 open(FLAGS, '<', "$orig_config_dir/flags") or die;
82 local $/;
83 @kbuild_flags = &shellwords(<FLAGS>);
84 close(FLAGS);
87 $ENV{KSPLICE_VERBOSE} = $Verbose::level;
88 $ENV{KSPLICE_CONFIG_DIR} = $orig_config_dir;
90 my @chars = ('a'..'z', 0..9);
91 $kid = join '', map { $chars[int(rand(36))] } 0..7 if(!defined $kid);
92 my $ksplice = "ksplice-$kid";
93 $ENV{KSPLICE_KID} = $kid;
95 # Some versions of Fedora have System.map files whose symbol addresses disagree
96 # with the running kernel by a constant address offset. Here, Ksplice notes the
97 # System.map address for printk so that it can later compare this address against
98 # the kernel's address for printk. This comparison helps Ksplice work around
99 # this Fedora problem, and this comparison also helps Ksplice detect whether
100 # the user has provided an incorrect System.map file.
101 my $map_printk = runstr("$datadir/ksplice-obj.pl", "system_map_lookup", "printk");
103 print "Starting kernel builds (this process might take a long time)...\n";
104 if($Verbose::level < 1) {
105 print "For output during this process, run ksplice-create with the option -v\n";
108 ###################################################################
109 # PHASE 1: Determine which object files are modified by the patch #
110 # - performs the pre and post kernel builds #
111 # - uses objdiff to identify which ELF sections have changed and #
112 # which ELF symbols are entry points to those sections #
113 ###################################################################
115 # We will refer to the object files modified by the patch as the "target object
116 # files", the ELF sections changed by the patch as the "target sections", and
117 # the entry points of those sections as the "target entry points".
119 my $origdir = getcwd();
120 chdir($linuxtree);
121 if(defined $diffext) {
122 runval("$libexecdir/ksplice-gendiff-reversed >$patchfile . $diffext");
125 my @jlevel = (defined $ENV{CONCURRENCY_LEVEL} ? ("-j$ENV{CONCURRENCY_LEVEL}") : ());
126 @jlevel = ("-j$jobs") if(defined $jobs);
127 my @make_ksplice = ("make", "-f", "$datadir/Makefile.ksplice", @jlevel, @kbuild_flags);
129 sub revert_orig() {
130 for(split(/\0/, runstr(qw(find -name *.KSPLICE*pre* -print0)))) {
131 if(my ($file) = m/^(.*)\.KSPLICE_pre$/) {
132 rename($_, $file);
133 runval("$datadir/ksplice-obj.pl", "snap", "$file.KSPLICE") if(-e "$file.KSPLICE");
134 } elsif(m/\.KSPLICE.*\.pre/) {
135 unlink($_);
139 revert_orig();
141 if(!$skip_prebuild) {
142 copy("$orig_config_dir/.config", "$linuxtree/.config");
143 utime((stat("$orig_config_dir/.config"))[8, 9], "$linuxtree/.config");
144 my @snap_flags = ("KSPLICE_MODE=snap");
145 if(-e "include/config/kernel.release") {
146 push(@snap_flags, "-o", "include/config/kernel.release");
148 runval_raw(@make_ksplice, @snap_flags) == 0 or
149 die "Aborting: Prebuild failed";
151 exit(0) if($prebuild);
153 if (!defined($standalone)) {
154 $standalone = (runval_raw(qw(grep -q ^CONFIG_KSPLICE=[ym]$), "$linuxtree/.config") != 0);
156 my @standalone_flag;
157 @standalone_flag = qw(KSPLICE_STANDALONE=1) if ($standalone);
159 runval("patch $patch_opt -bz .KSPLICE_pre < $patchfile");
160 if(runval_raw(@make_ksplice, "KSPLICE_MODE=diff") != 0) {
161 revert_orig();
162 die "Aborting: Applying the patch appears to break the kernel build";
165 sub copy_debug {
166 my ($file) = @_;
167 my ($dir, $base) = (dirname($file), basename($file));
168 -d "$tmpdir/objects/$dir" or mkpath("$tmpdir/objects/$dir");
169 copy($file, "$tmpdir/objects/$file");
170 my $cmdfile = "$dir/.$base.cmd";
171 copy($cmdfile, "$tmpdir/objects/$cmdfile") if(-e $cmdfile);
174 mkdir("$tmpdir/objects");
175 for (split(/\0/, runstr(qw(find -name *.KSPLICE* ! ( -name *.KSPLICE -empty ) ! -name .*.KSPLICE.cmd -print0)))) {
176 copy_debug($_);
177 copy_debug($1) if (m/^(.*)\.KSPLICE_pre$/);
180 runval("cp", "-a", "--", "$datadir/kmodsrc", "$tmpdir/kmodsrc");
182 my @modules = ();
183 foreach(glob(".tmp_versions/*.mod.KSPLICE")) {
184 open MOD, '<', $_;
185 chomp(my $mod = <MOD>);
186 close MOD;
187 foreach my $collect ("$mod.o.KSPLICE", "$mod.o.KSPLICE_primary",
188 "$mod.o.KSPLICE_helper") {
189 copy($collect, "$tmpdir/kmodsrc/" . basename($collect));
191 push @modules, basename($mod);
194 if(!@modules) {
195 revert_orig();
196 die "Aborting: No changes detected";
199 revert_orig();
201 runval("make", "-C", $kernel_headers_dir, "M=$tmpdir/kmodsrc", @jlevel, "modules", "KSPLICE_KID=$kid", "KSPLICE_MODULES=@modules", "KSPLICE_VERSION=PACKAGE_VERSION", "map_printk=$map_printk", @standalone_flag);
203 chdir($tmpdir);
204 mkdir($ksplice);
205 move($patchfile, $ksplice);
206 foreach my $mod (@modules) {
207 (my $target = $mod) =~ s/-/_/g;
208 my $mid = "${kid}_$target";
209 my $module = "ksplice-$mid";
210 rename("kmodsrc/$module.ko", "$ksplice/$module.ko");
211 rename("kmodsrc/$module-helper.ko", "$ksplice/$module-helper.ko");
213 rename("kmodsrc/ksplice-$kid.ko", "$ksplice/ksplice-$kid.ko") if ($standalone);
215 mkdir("$ksplice/debug");
216 rename("objects", "$ksplice/debug/objects");
217 rename("kmodsrc", "$ksplice/debug/kmodsrc");
218 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
219 copy("$ksplice.tar.gz", "$origdir/$ksplice.tar.gz");
221 print "Ksplice update tarball written to $ksplice.tar.gz\n";
223 if($apply) {
224 print "Now running ksplice-apply to apply update...\n";
225 exec("ksplice-apply", $ksplice) || die;
228 exit(0);
230 =head1 NAME
232 ksplice-create - Create a set of kernel modules for a rebootless kernel update
234 =head1 SYNOPSIS
236 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
238 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
240 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
242 =head1 DESCRIPTION
244 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
245 will apply a user-specified source code patch to the running binary kernel.
247 Before you use B<ksplice-create> on a patch, you should confirm that the
248 desired source code change does not make any semantic changes to kernel data
249 structures--that is, changes that would require existing instances of kernel
250 data structures to be transformed (e.g., a patch that adds a field to a global
251 data structure would require the existing data structures to change). If you
252 use Ksplice on a patch that changes data structure semantics, Ksplice will not
253 detect the problem and you could experience kernel problems as a result.
255 The to-be-applied source code patch can be specified by providing a L<patch(1)>
256 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
257 (B<--diffext=>I<EXTENSION>).
259 If a file extension is specified, then the desired source code patch will be
260 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
261 whose names end with the extra extension I<EXTENSION> against the corresponding
262 files without the extra extension. Only the new files containing the extra
263 extension in their filenames should be modified.
265 Here is an example of using a file extension to specify a patch:
267 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
268 [edit sys.c.prctl_fixed to include the desired changes]
269 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
271 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
272 original source code. If your Linux distribution applies patches to the Linux
273 kernel during the kernel build process, then those patches must be applied to
274 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
275 directory. B<ksplice-create> will not modify the source code in the
276 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
277 directory tree.
279 I<ORIG_CONFIG> can be used to specify the directory containing the
280 to-be-updated kernel's original F<.config> file and original F<System.map> file
281 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
282 I<KERNEL_SOURCE>B</ksplice>.
284 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
285 close to the compiler and assembler originally used to build the running kernel
286 as possible. If the current compiler and linker are too different from the
287 original compiler and linker, B<ksplice-apply> will abort when applying the
288 update.
290 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
291 containing the desired Ksplice update modules. This tarball will be created in
292 the current directory, and it can be manipulated using the other Ksplice
293 utilities, such as B<ksplice-apply>.
295 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
296 directory, it must build that kernel from scratch, which is much slower than
297 the rest of the update-creation process. B<--prebuild> can be used to perform
298 this initial kernel build without providing a source code patch.
300 In order to patch a function that has previously been patched by Ksplice, the
301 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
302 contains the source for the currently running kernel, including any patches
303 that have previously been applied by Ksplice.
305 =head1 OPTIONS
307 =over 8
309 =item B<-v>, B<--verbose>
311 Causes B<ksplice-create> to print debugging messages about its progress. Using
312 multiple -v options increases the verbosity. The maximum is 2.
314 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
316 Specifies the number of jobs to run simultaneously while performing kernel
317 builds. B<ksplice-create> also honors the environment variable
318 CONCURRENCY_LEVEL.
320 =item B<--apply>
322 Immediately applies the generated update to the running kernel by invoking
323 B<ksplice-apply>.
325 =item B<--patch-opt=>I<OPTIONS>
327 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
328 B<-p1> is passed to B<patch>. If this option is specified, then only the
329 specified options will be passed to B<patch>. This option can be repeated in
330 order to pass multiple options to B<patch>. This option is ignored when the
331 to-be-applied source code patch is specified using B<--diffext>.
333 =item B<--id=>I<ID>
335 Specifies the unique value that will be used as the identifier of the
336 Ksplice update. This identifier will, for example, appear in the name
337 of the update tarball. By default, a random 8-character ID will be
338 generated.
340 =back
342 =head1 BUGS
344 Please report bugs to <PACKAGE_BUGREPORT>.
346 =head1 SEE ALSO
348 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
350 =head1 COPYRIGHT
352 Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>.
354 This is free software and documentation. You can redistribute and/or modify it
355 under the terms of the GNU General Public License, version 2.
357 =cut