Fixed kernel sources path for 'smp' kernels.
[lufs.git] / kernel / Linux / prepmod.in
1 #! /usr/bin/perl -w
2
3 # $Id$
4 # Check and possibly rebuild lufs kernel module for the current kernel.
5 # Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; exactly version 2 of June 1991 is required
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
21 use strict;
22 use Carp qw(cluck confess);
23 use Getopt::Long;
24 # Do not use any non-core Perl modules here.
25 # Be perl-5.0 compatible (is it needed if we require Linux kernel >= 2.4 ?)
26
27
28 my $basedir='@datadir@/lufs';
29 my $vardir='@localstatedir@/lib/lufs';
30 $basedir=~s#\$\Q{prefix}\E#'@prefix@';#ge;
31 $vardir=~s#\$\Q{prefix}\E#'@prefix@';#ge;
32 $vardir="" if $vardir=~/^@/;
33 sub srcdir { my($uname_r)=@_; $basedir."/".($uname_r lt "2.5" ? "2.4" : "2.6"); }
34 my $modbindir=$basedir."/modbin";
35 my @sources=qw(proc.c inode.c dir.c file.c symlink.c);
36
37 my $quiet;
38 my $kernel;
39 my $prebuild;
40 use vars qw($kernel_gcc_args);
41 $kernel_gcc_args="";
42
43 sub modext { my($uname_r)=@_; ($uname_r lt "2.5" ? "o" : "ko"); }
44
45 # mount(8) will util-linux/lib/env.c/sanitize_env()
46 # including the clearance our $PATH:
47 $ENV{"PATH"}=join(":",qw(/usr/local/bin /usr/bin /bin),($ENV{"PATH"} || ()));
48
49 my $lufsd_base='lufsd';
50 do { eval '$lufsd_base=~'.$_.';' if !/^@/; } for ('@program_transform_name@');
51 my $lufsd_bin;
52 if ($0 eq $lufsd_base || $0=~m#/\Q$lufsd_base\E$#) {
53         $quiet=1;
54         $lufsd_bin='@bindir@/'.$lufsd_base.'-bin';
55         $lufsd_bin=~s#\$\Q{exec_prefix}\E#'@exec_prefix@';#ge;
56         $lufsd_bin=~s#\$\Q{prefix}\E#'@prefix@';#ge;
57         }
58 else {
59         die if !GetOptions(
60                         "q|quiet",\$quiet,
61                           "kernel",\$kernel,    # it must contain "include" subdir
62                           "modbindir=s",\$modbindir,
63                           "basedir=s",\$basedir,
64                           "prebuild",\&prebuild,
65                           "kernel-gcc-args=s",\$kernel_gcc_args,
66                         );
67         }
68
69
70 sub _readfile
71 {
72 my($filename,$optional)=@_;
73
74         local $/=undef();
75         local *F;
76         open F,$filename or do { cluck "Open \"$filename\": $!" if !$optional; return ""; };
77         my $r=<F>;
78         close F or cluck "Close \"$filename\": $!";
79         return $r;
80 }
81
82 sub _writefile
83 {
84 my($filename,@content)=@_;
85
86         local *F;
87         open F,($filename=~/^[|]/ ? "" : ">").$filename or confess "rewrite \"$filename\": $!";
88         print F @content;
89         close F or confess "close \"$filename\": $!";   # Do not &cluck as it may be pipe result
90 }
91
92 sub _system
93 {
94 my(@args)=@_;
95
96         print STDERR "+ ".join(" ",@args)."\n" if !$quiet;
97         return system @args;
98 }
99
100 sub _pass
101 {
102 my($load)=@_;
103
104         my $modproberc;
105         if ($load) {
106                 do { ($modproberc=_system $_) and cluck "$_ failed - ignoring"; }
107                                 for ("/sbin/depmod -aq","/sbin/modprobe lufs");
108                 }
109         exit ($modproberc ? 1 : 0) if !$lufsd_bin;
110         do { exec $lufsd_bin,@ARGV; };
111         confess "Failed to exec '$lufsd_bin': $!";
112 }
113
114
115 my $filesystems=_readfile "/proc/filesystems";  # 'lufs' may be built-in
116 _pass if $filesystems=~/\blufs$/m;
117 my $modules=_readfile "/proc/modules";  # 'lufs' may be already loaded
118 _pass if $modules=~/^lufs\b/m;
119 _pass if !_system "/sbin/modprobe lufs 2>/dev/null";
120
121 print STDERR "Preparing LUFS kernel module... Run $basedir/prepmod if problems occur.\n";       # if !$quiet;
122
123 my $proc_version=_readfile "/proc/version";
124 my $uname_r=($proc_version=~/^Linux version (\S+)/)[0] || _readfile "uname -r|";
125 chomp $uname_r;
126 confess "Failed to detect kernel version" if !$uname_r;
127 my $uname_smp=($uname_r=~s/smp$// && "smp");
128 my $uname_r_base=($uname_r=~/^([^-]+)/)[0];
129 print STDERR "Running kernel version: $uname_r (base version $uname_r_base)\n" if !$quiet;
130
131 my $lufs_o="lufs.".modext($uname_r);
132
133 my $moduledir="/lib/modules/$uname_r$uname_smp/kernel/fs/lufs";
134 print STDERR "Destination module directory: $moduledir\n" if !$quiet;
135
136 my @kernel_paths=(
137                                 "/lib/modules/$uname_r$uname_smp/build",
138                                 "/usr/src/kernel-headers-$uname_r",
139                                 "/usr/src/linux-$uname_r",
140                                 "/usr/src/linux-$uname_r_base",
141                                 "/usr/src/linux",
142                                 "/usr/src/kernel-source-$uname_r",      # no .config?
143                                 );
144 do { $kernel||=$_ if -d $_; } for (@kernel_paths);
145 if (!$kernel) {
146         print STDERR "Failed to find kernel headers for $uname_r\n" if !$kernel && !$quiet;
147         }
148 else {
149         # We need /usr/include/lufs/* for standard package compilation.
150         # There is no sense to make separate 'devel' package.
151         for ("$vardir/$lufs_o") {
152                 next if !$vardir;
153                 # Create the 'lufs.o' in our /var/lib directory and only link it
154                 # to prevent using obsolete modules after upgrading 'lufs' package.
155                 # depmod(1) will take the larget symlink name - we must create directory for  it.
156                 if (build($kernel,$uname_r,$_)) {
157                         do { cluck "Failed to symlink $_"; next; }
158                                         if _system "/bin/rm -rf $moduledir; /bin/mkdir -p $moduledir; /bin/ln -s $_ $moduledir/$lufs_o";
159                         # Check if the compiled module matches the currently running kernel.
160                         # We may found some unspecific sources ('/usr/src/linux/'?) not matching the current kernel.
161                         # It still may be worth to try precompiled modules and/or give suggestive error messages.
162                         # Do not: /sbin/insmod -o lufs -p $_ 2>/dev/null
163                         # as 2.6 insmod has no options at all.
164                         next if _system "/sbin/rmmod lufs 2>/dev/null; /sbin/insmod $moduledir/$lufs_o 2>/dev/null";
165                         _pass 1;
166                         }
167                 }
168         }
169
170 local $_;
171 for (<$modbindir/*-$uname_r*/$lufs_o>,<$modbindir/*-${uname_r_base}*/$lufs_o>,<$modbindir/*/$lufs_o>) {
172         # Do not: /sbin/insmod -o lufs -p $_ 2>/dev/null
173         # as 2.6 insmod has no options at all.
174         next if _system "/sbin/rmmod lufs 2>/dev/null; /sbin/insmod $_ 2>/dev/null";
175         do { cluck "Failed to symlink $_"; next; }
176                         if _system "/bin/rm -rf $moduledir; /bin/mkdir -p $moduledir; /bin/ln -s $_ $moduledir/$lufs_o";
177         # 0 as already insmod(8)ed.
178         _pass 0;
179         }
180 confess "lufs module not loaded: Try running $basedir/prepmod to see more." if $quiet;
181 confess "Failed to prepare $lufs_o module for your Linux kernel $uname_r.\n"
182                 .($kernel ? "Detected Linux kernel sources \"$kernel\" do not appear to be valid.\n"
183                                 : "No Linux kernel sources for your running kernel were found.\n")
184                 ."Please install kernel-source-x.y.z.i386.rpm or kernel-headers_x.y.z_i386.deb.\n"
185                 ."The following directory paths were searched (first existing directory used):\n"
186                 .join("",map("\t\t$_\n",@kernel_paths));
187
188
189 sub build_gcc
190 {
191 my($kernel,$uname_r,$destmodule)=@_;
192
193         print STDERR "Using kernel sources: $kernel\n" if !$quiet;
194         confess "Kernel sources $kernel do not contain 'include' subdirectory" if ! -d $kernel."/include";
195
196         my $kdebug="";
197         do { $kdebug=$_ if !/^@/; } for ('@KDEBUG_FLAGS@');
198         my $cmdline="/usr/bin/gcc"
199                         .($quiet ? " 2>/dev/null" : "")
200                         ." -Wall -Wstrict-prototypes -Wno-trigraphs"
201                         ." -O2 -fno-strict-aliasing -fno-common -fomit-frame-pointer -pipe -mpreferred-stack-boundary=2"
202                         #." -march=i586"
203                         ." -D__KERNEL__ -DMODULE -DLINUX -DKBUILD_MODNAME=lufs"
204                         ." ".$kdebug
205                         ." ".$kernel_gcc_args
206                         ." -I$kernel/include"
207                         ." -I$kernel/include/asm-i386/mach-default";    # gcc should not care if this 2.6 dir does not exist
208
209         my $config=_readfile "$kernel/.config","optional";
210         my $autoconf=_readfile "$kernel/include/linux/autoconf.h","optional";
211         if (0
212                         || $config=~/^CONFIG_MODVERSIONS=y\b/m
213                         || $autoconf=~/^#define CONFIG_MODVERSIONS\b/m
214                         || (!$config && !$autoconf)) {  # assume modversions if not known
215                 $cmdline.=" -DMODVERSIONS";
216                 $cmdline.=" -include $kernel/include/linux/version.h"
217                                 if -f "$kernel/include/linux/version.h";
218                 $cmdline.=" -include $kernel/include/linux/modversions.h";
219                 }
220
221         my @objects=map({ my $o=$_; $o=~s/[.]c$/.o/; $o; } @sources);
222         return !_system "set -e; /bin/mkdir -p `dirname $destmodule`; /bin/rm -f $destmodule;"
223                         .(!$vardir ? "" : " cd $vardir;")
224                         ." $cmdline -c ".join(" ",map({ srcdir($uname_r)."/".$_; } @sources)).";"
225                         ." /usr/bin/ld -r -o $destmodule ".join(" ",@objects).";"
226                         ." /bin/rm -f ".join(" ",@objects);
227 }
228
229
230 sub build_make
231 {
232 my($kernel,$uname_r,$destmodule,%args)=@_;
233
234         print STDERR "Using kernel sources: $kernel\n" if !$quiet;
235         # 'Rules.make' not present in 2.6+ kernels
236         do { confess "Kernel sources $kernel do not contain '$_' file" if ! -f $_; } for ($kernel."/arch/i386/Makefile");
237
238         my $kdebug="";
239         do { $kdebug=$_ if !/^@/; } for ('@KDEBUG_FLAGS@');
240         my $predir=srcdir $uname_r;
241         $predir=$ENV{"PWD"}."/$predir" if $predir!~m#^/#;
242         # Workaround a bug in at least Red Hat 2.4.18-18.8.0
243         if (-f "$kernel/include/linux/version.h") {
244                 for ("$kernel/include/linux/modversions.h") {
245                         _system "cp -p $_ $_-orig" if ! -f "$_-orig";
246                         _writefile $_,"#include <linux/version.h>\t/* lufs */\n"._readfile $_;
247                         }
248                 }
249         my $cmdline="make -C $kernel"
250                         ." SUBDIRS=\"$predir\" modules"
251                         ." EXTRA_CFLAGS=\"$kernel_gcc_args\""
252                         .($quiet ? ' &>/dev/null' : '');
253         my @objects=map({ my $o=$_; $o=~s/[.]c$/.o/; $o; } @sources,"lufs.mod.c","lufs.c");
254         $cmdline="set -e; /bin/mkdir -p `dirname $destmodule`; /bin/rm -f $destmodule;"
255                         ." $cmdline;"
256                         ." /bin/mv -f $predir/lufs.".modext($uname_r)." $destmodule;"
257                         ." /bin/rm -f ".join(" ",map(("$predir/$_","$predir/.$_.flags","$predir/.$_.cmd"),@objects),"$predir/lufs.mod.c","$predir/.lufs.ko.cmd").";";
258         my $r=!_system $cmdline;
259         return $r if $r;
260         # Rebuild existing '$kernel/tmp_include_depends' or '$kernel/.depend'
261         # as it may contain non-existing pathnames:
262         _system "find $kernel -name .depend|xargs rm -f;"
263                         # Red Hat 2.4.18-14 contains precompiled .so-dependent 'mkdep'
264                         ." rm -f $kernel/scripts/mkdep;"
265                         # SuSE 2.4.21-144 contains precompiled .so-dependent 'split-include'
266                         ." rm -f $kernel/scripts/split-include;"
267                         ." make -C $kernel dep"
268                                     .($quiet ? ' &>/dev/null' : '');
269         return !_system $cmdline;
270 }
271
272
273 sub build
274 {
275 my($kernel,$uname_r,$destmodule,%args)=@_;
276
277         # Debian uname(1) does not support '-p'.
278         my $arch=_readfile "uname -p 2>/dev/null || true|"
279                         || _readfile "uname -m|";
280         chomp $arch;
281         my $single_config=-f "$kernel/.config";
282         if (!$single_config) {
283                 my $spec_config="$kernel/configs/kernel-$uname_r_base-$arch".($uname_smp ? "-smp" : "").".config";
284                 _system "ln -s $spec_config $kernel/.config"
285                                 if -f $spec_config;
286                 }
287         my $r;
288         # 'Rules.make' not present in 2.6+ kernels
289         if (-f $kernel."/arch/i386/Makefile" && -f "$kernel/.config")
290                 { $r=build_make $kernel,$uname_r,$destmodule,%args; }
291         else
292                 { $r=build_gcc $kernel,$uname_r,$destmodule,%args; }
293         _system "rm -f $kernel/.config"
294                         if !$single_config;
295         return $r;
296 }
297
298
299 sub prebuild_kernel
300 {
301 my($dir,$vendor,$uname_r,%args)=@_;
302
303         confess "Unrecognized vendor for dir: $dir" if !$vendor;
304         confess "Unrecognized uname_r for dir: $dir" if !$uname_r;
305         confess "Failed to build $dir" if !build $dir,$uname_r,$modbindir."/lufs-$vendor-$uname_r.".modext($uname_r),%args;
306 }
307
308
309 sub prebuild_rpm
310 {
311 my($rpm)=@_;
312
313         my $vendor=_readfile "rpm 2>/dev/null --qf '%{VENDOR}' -qp '$rpm' |";
314         $vendor="unknown" if $vendor eq "(none)";       # TurboLinux 2.4.18-14
315         $vendor=~s/[,<].*//;
316         $vendor=~tr/ //d;
317         my $uname_r;
318         $uname_r||=($rpm=~m#/kernel-source-([^-]+-[^-]+)[.][^.]+[.]rpm$#)[0];
319         my $uname_r_base=($uname_r=~/^([^-]+)/)[0];
320         # Do not: rpm -i ...
321         # as SuSE kernels require 1.5GB of VM during later 'rpm -e' in Shrike.
322         _system "rm -rf modbin-tmp;mkdir modbin-tmp;rpm2cpio '$rpm'|(cd modbin-tmp;cpio -id)"
323                         and confess "RPM extraction of $rpm";
324         my $dir;
325         # Do not: -f $_
326         # as we are satisfied with symlink
327         # (such as base 'linux' used for some SuSE kernels with weird directory name).
328         do { $dir||=$_ if -e $_; } for ($ENV{"PWD"}."/modbin-tmp/usr/src/linux-$uname_r-include");      # new SuSE kernels
329         do { $dir||=$_ if -e $_; } for ($ENV{"PWD"}."/modbin-tmp/usr/src/linux-$uname_r");
330         do { $dir||=$_ if -e $_; } for ($ENV{"PWD"}."/modbin-tmp/usr/src/linux-$uname_r_base"); # older RedHat kernels
331         do { $dir||=$_ if -e $_; } for ($ENV{"PWD"}."/modbin-tmp/usr/src/linux");
332         $dir or confess "Kernel source tree not found in: $rpm";
333         _system "chmod -R u+w $dir" and confess "Make kernel source writable in $dir";
334         _system "cp -p $dir/include/linux/rhconfig.h $dir/include/linux/rhconfig.h-orig"
335                                 if -f "$dir/include/linux/rhconfig.h";
336         my $built_total=0;
337         if (-d "$dir/default" && -d "$dir/smp") {
338                 # new SuSE kernels
339                 for my $arch_d (glob "$dir/*") {
340                         (my $arch=$arch_d)=~s#^.*/##;
341                         prebuild_kernel $arch_d,$vendor,$uname_r.".".$arch;
342                         $built_total++;
343                         }
344                 }
345         elsif (-e "$dir/SetupKernelSource.sh") {
346                 # TurboLinux
347                 if (! -e "$dir/include") {
348                         (my $kheaders=$rpm)=~s#/kernel-source-([^/]*)$#/kernel-headers-$1#
349                                         or confess "Invalid RPM basename of: $rpm";
350                         _system "rpm2cpio '$kheaders'|(cd modbin-tmp;cpio -id)"
351                                         and confess "RPM kheaders extraction of $rpm";
352                         }
353                 for my $TYPE ("","smp","smp64G") {
354                         for my $ARCH (qw(i386 i586 i686 athlon)) {
355                                 # Use even 'A-Z' for the 'release' for TurboLinux 2.4.9-3D
356                                 next if !glob "$dir/configs/kernel-*-*[0-9A-Z]$TYPE-$ARCH.config";
357                                 _system "set -ex; cd '$dir';"
358                                                                 # Forbid 'cd /usr/src/linux' in './SetupKernelSource.sh'
359                                                                 # but permit later 'cd's by make(1) subprocesses.
360                                                                 # Therefore do not 'export -f cd;' but 'source ./SetupKernelSource.sh'.
361                                                                 .' function cd { :; };' # Do not: 'export -f cd;'
362 #                                                               .' function make {(set -ex;unset make; make "$@"; make symlinks; );}; export -f make;'
363                                                                 # Permit 'errors' for 'grep smp' in TurboLinux 2.4.5-0.5
364                                                                 ." set +e; source ./SetupKernelSource.sh $ARCH$TYPE;"
365                                                 and confess "SetupKernelSource.sh $ARCH$TYPE in $dir";
366                                 prebuild_kernel $dir,$vendor,$uname_r.".".$ARCH.$TYPE;
367                                 $built_total++;
368                                 }
369                         }
370                 }
371         else {
372                 # RedHat/Mandrake/old-SuSE kernels
373                 for my $smp ("","smp") {
374                         my @archs=qw(i386 i586 i686 athlon);
375                         for my $arch (@archs) {
376                                 my %boot=(
377                                                 "__BOOT_KERNEL_SMP"=>$smp,
378                                                 map({ ("__MODULE_KERNEL_$_"=>($arch eq $_)); } @archs),
379                                                 "__BOOT_KERNEL_BOOT"=>0,
380                                                 "__BOOT_KERNEL_BOOTSMP"=>0,
381                                                 "__BOOT_KERNEL_ENTERPRISE"=>0,
382                                                 "__BOOT_KERNEL_BIGMEM"=>0,
383                                                 "__BOOT_KERNEL_DEBUG"=>0,
384                                                 );
385                                 if (-f "$dir/include/linux/rhconfig.h") {
386                                         my $defines="";
387                                         for (keys(%boot)) {
388                                                 $defines.="+#define $_ ".($boot{$_} ? 1 : 0)."\n";
389                                                 }
390                                         _system "cp -p -f $dir/include/linux/rhconfig.h-orig $dir/include/linux/rhconfig.h";
391                                         _writefile "| patch $dir/include/linux/rhconfig.h",<<"RHCONFIG_H_NOBOOT_EOF";
392 --- ./include/linux/rhconfig.h-orig     Thu Aug 21 14:50:16 2003
393 +++ ./include/linux/rhconfig.h  Thu Aug 21 14:59:22 2003
394 \@\@ -10,7 +10,@{[ 7+keys(%boot) ]} \@\@
395   * /boot/kernel.h - initscripts should create it for us
396   */
397  
398 -#include "/boot/kernel.h"
399 +/* lufs: #include "/boot/kernel.h" */
400 $defines
401  #if defined(__BOOT_KERNEL_SMP) && (__BOOT_KERNEL_SMP == 1)
402  #define __module__smp
403 RHCONFIG_H_NOBOOT_EOF
404                                         }
405                                 # Mandrake packages have no configs/ and they have just that '.config' file.
406                                 my $single_config=-f "$dir/.config";
407                                 if (!$single_config) {
408                                         my $spec_config="$dir/configs/kernel-$uname_r_base-$arch".($smp ? "-smp" : "").".config";
409                                         next if ! -f $spec_config;
410                                         _system "ln -s $spec_config $dir/.config";
411                                         }
412                                 prebuild_kernel $dir,$vendor,$uname_r.$smp.".".$arch;
413                                 $built_total++;
414                                 _system "rm -f $dir/.config"
415                                                 if !$single_config;
416                                 }
417                         }
418                 }
419         # Old SuSE kernels have no '.config' or 'configs/'.
420         confess "Not a buildable kernel: $rpm" if !$built_total;
421         _system "rm -rf modbin-tmp" and confess "Remove of extracted kernel-source-$uname_r";
422 }
423
424 sub prebuild_dir
425 {
426 my($dir)=@_;
427
428         my $vendor;
429         chomp (my $debian=_readfile "/etc/debian_version","optional");
430         $debian=~tr#/#_#;
431         $vendor="Debian".$debian if $debian && $dir=~m#^/usr/src/#;
432         my $uname_r;
433         $uname_r||=($dir=~m#/kernel-headers-([^/]+)/*$#)[0];
434         $uname_r||=($dir=~m#/linux-([^/]+)/*$#)[0];
435         $uname_r||=($dir=~m#^/lib/modules/([^/]+)/build/*$#)[0];
436         prebuild_kernel $dir,$vendor,$uname_r;
437 }
438
439 sub prebuild
440 {
441         $vardir=undef();        # we may not yet have /var/lib/lufs
442         for (@ARGV) {
443                 do { prebuild_rpm $_; next; } if /[.]rpm$/;
444                 do { prebuild_dir $_; next; } if -d $_;
445                 confess "Unrecognized kernel: $_";
446                 }
447         exit 0;
448 }