Always rebuild Linux kernel dependencies as the shipped one may be broken.
[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 my $includedir='@includedir@';  # '/lufs' is required by C '#include'
31 $basedir=~s#\$\Q{prefix}\E#'@prefix@';#ge;
32 $vardir=~s#\$\Q{prefix}\E#'@prefix@';#ge;
33 $vardir="" if $vardir=~/^@/;
34 $includedir=~s#\$\Q{prefix}\E#'@prefix@';#ge;
35 $includedir="" if $includedir=~/^@/;
36 sub srcdir { my($uname_r)=@_; $basedir."/".($uname_r lt "2.5" ? "2.4" : "2.5"); }
37 my $modbindir=$basedir."/modbin";
38 my @sources=qw(proc.c inode.c dir.c file.c symlink.c);
39
40 my $quiet;
41 my $kernel;
42 my $prebuild;
43 use vars qw($kernel_gcc_args);
44 $kernel_gcc_args="";
45 $kernel_gcc_args="-I$includedir" if $includedir;
46
47 my $lufsmnt_bin;
48 if ($0 eq "lufsmnt" || $0=~m#/lufsmnt$#) {
49         $quiet=1;
50         $lufsmnt_bin='@bindir@/lufsmnt-bin';
51         $lufsmnt_bin=~s#\$\Q{exec_prefix}\E#'@exec_prefix@';#ge;
52         $lufsmnt_bin=~s#\$\Q{prefix}\E#'@prefix@';#ge;
53         }
54 else {
55         die if !GetOptions(
56                         "q|quiet",\$quiet,
57                           "kernel",\$kernel,    # it must contain "include" subdir
58                           "modbindir=s",\$modbindir,
59                           "basedir=s",\$basedir,
60                           "prebuild",\&prebuild,
61                           "kernel-gcc-args=s",\$kernel_gcc_args,
62                         );
63         }
64
65
66 sub _readfile
67 {
68 my($filename,$optional)=@_;
69
70         local $/=undef();
71         local *F;
72         open F,$filename or do { cluck "Open \"$filename\": $!" if !$optional; return ""; };
73         my $r=<F>;
74         close F or cluck "Close \"$filename\": $!";
75         return $r;
76 }
77
78 sub _writefile
79 {
80 my($filename,@content)=@_;
81
82         local *F;
83         open F,($filename=~/^[|]/ ? "" : ">").$filename or confess "rewrite \"$filename\": $!";
84         print F @content;
85         close F or confess "close \"$filename\": $!";   # Do not &cluck as it may be pipe result
86 }
87
88 sub _system
89 {
90 my(@args)=@_;
91
92         print STDERR "+ ".join(" ",@args)."\n" if !$quiet;
93         return system @args;
94 }
95
96 sub _pass
97 {
98 my($load)=@_;
99
100         my $modproberc;
101         if ($load) {
102                 do { ($modproberc=_system $_) and cluck "$_ failed - ignoring"; }
103                                 for ("/sbin/depmod -aq","/sbin/modprobe lufs");
104                 }
105         exit ($modproberc ? 1 : 0) if !$lufsmnt_bin;
106         do { exec $lufsmnt_bin,@ARGV; };
107         confess "Failed to exec '$lufsmnt_bin': $!";
108 }
109
110
111 my $filesystems=_readfile "/proc/filesystems";  # 'lufs' may be built-in
112 _pass if $filesystems=~/\blufs$/m;
113 my $modules=_readfile "/proc/modules";  # 'lufs' may be already loaded
114 _pass if $modules=~/^lufs\b/m;
115 _pass if !_system "/sbin/modprobe lufs 2>/dev/null";
116
117 my $proc_version=_readfile "/proc/version";
118 my $uname_r=($proc_version=~/^Linux version (\S+)/)[0] || _readfile "uname -r|";
119 chomp $uname_r;
120 confess "Failed to detect kernel version" if !$uname_r;
121 my $uname_smp=($uname_r=~s/smp$// && "smp");
122 my $uname_r_base=($uname_r=~/^([^-]+)/)[0];
123 print STDERR "Running kernel version: $uname_r (base version $uname_r_base)\n" if !$quiet;
124
125 my $moduledir="/lib/modules/$uname_r$uname_smp/kernel/fs/lufs";
126 print STDERR "Destination module directory: $moduledir\n" if !$quiet;
127
128 do { $kernel||=$_ if -d $_; } for ("/lib/modules/$uname_r/build");
129 do { $kernel||=$_ if -d $_; } for ("/usr/src/kernel-headers-$uname_r");
130 do { $kernel||=$_ if -d $_; } for ("/usr/src/linux-$uname_r");
131 do { $kernel||=$_ if -d $_; } for ("/usr/src/linux-$uname_r_base");
132 do { $kernel||=$_ if -d $_; } for ("/usr/src/linux");
133 if (!$kernel) {
134         print STDERR "Failed to find kernel headers for $uname_r\n" if !$kernel && !$quiet;
135         }
136 else {
137         # We need /usr/include/lufs/* for standard package compilation.
138         # There is no sense to make separate 'devel' package.
139         for ("$vardir/lufs.o") {
140                 next if !$vardir;
141                 # Create the 'lufs.o' in our /var/lib directory and only link it
142                 # to prevent using obsolete modules after upgrading 'lufs' package.
143                 # depmod(1) will take the larget symlink name - we must create directory for  it.
144                 if (build($kernel,$uname_r,$_)) {
145                         do { cluck "Failed to symlink $_"; next; }
146                                         if _system "/bin/rm -rf $moduledir; /bin/mkdir -p $moduledir; /bin/ln -s $_ $moduledir/lufs.o";
147                         _pass 1;
148                         }
149                 }
150         }
151
152 local $_;
153 for (<$modbindir/*-$uname_r*/*.o>,<$modbindir/*-${uname_r_base}*/*.o>,<$modbindir/*/*.o>) {
154         next if _system "/sbin/rmmod lufs 2>/dev/null; /sbin/insmod -o lufs -p $_ 2>/dev/null";
155         do { cluck "Failed to symlink $_"; next; }
156                         if _system "/bin/rm -rf $moduledir; /bin/mkdir -p $moduledir; /bin/ln -s $_ $moduledir/lufs.o";
157         _pass 1;
158         }
159 confess "lufs module not loaded: Try running $basedir/prepmod to see more.";
160
161
162 sub build_gcc
163 {
164 my($kernel,$uname_r,$destmodule)=@_;
165
166         print STDERR "Using kernel sources: $kernel\n" if !$quiet;
167         confess "Kernel sources $kernel do not contain 'include' subdirectory" if ! -d $kernel."/include";
168
169         my $kdebug="";
170         do { $kdebug=$_ if !/^@/; } for ('@KDEBUG_FLAGS@');
171         my $cmdline="/usr/bin/gcc"
172                         .($quiet ? " 2>/dev/null" : "")
173                         ." -Wall -Wstrict-prototypes -Wno-trigraphs"
174                         ." -O2 -fno-strict-aliasing -fno-common -fomit-frame-pointer -pipe -mpreferred-stack-boundary=2"
175                         #." -march=i586"
176                         ." -D__KERNEL__ -DMODULE -DLINUX -DKBUILD_MODNAME=lufs"
177                         ." ".$kdebug
178                         ." ".$kernel_gcc_args
179                         ." -I$kernel/include"
180                         ." -I$kernel/include/asm-i386/mach-default";    # gcc should not care if this 2.5 dir does not exist
181
182         my $config=_readfile "$kernel/.config","optional";
183         my $autoconf=_readfile "$kernel/include/linux/autoconf.h","optional";
184         $cmdline.=" -DMODVERSIONS -include $kernel/include/linux/modversions.h"
185                         if 0
186                                         || $config=~/^CONFIG_MODVERSIONS=y\b/m
187                                         || $autoconf=~/^#define CONFIG_MODVERSIONS\b/m
188                                         || (!$config && !$autoconf);    # assume modversions if not known
189
190         my @objects=map({ my $o=$_; $o=~s/[.]c$/.o/; $o; } @sources);
191         return !_system "set -e; /bin/mkdir -p `dirname $destmodule`; /bin/rm -f $destmodule;"
192                         .(!$vardir ? "" : " cd $vardir;")
193                         ." $cmdline -c ".join(" ",map({ srcdir($uname_r)."/".$_; } @sources)).";"
194                         ." /usr/bin/ld -r -o $destmodule ".join(" ",@objects).";"
195                         ." /bin/rm -f ".join(" ",@objects);
196 }
197
198
199 sub build_make
200 {
201 my($kernel,$uname_r,$destmodule)=@_;
202
203         print STDERR "Using kernel sources: $kernel\n" if !$quiet;
204         confess "Kernel sources $kernel do not contain 'Rules.make' file" if ! -f $kernel."/Rules.make";
205
206         my $kdebug="";
207         do { $kdebug=$_ if !/^@/; } for ('@KDEBUG_FLAGS@');
208         my $predir=srcdir $uname_r;
209         $predir=$ENV{"PWD"}."/$predir" if $predir!~m#^/#;
210         my $cmdline="make -C $kernel"
211                         ." SUBDIRS=\"$predir\" modules"
212                         ." EXTRA_CFLAGS=\"$kernel_gcc_args\""
213                         .($quiet ? ' &>/dev/null' : '');
214         my @objects=map({ my $o=$_; $o=~s/[.]c$/.o/; $o; } @sources);
215         return !_system "set -e; /bin/mkdir -p `dirname $destmodule`; /bin/rm -f $destmodule;"
216                         ." $cmdline;"
217                         ." /bin/rm -f ".join(" ",map(("$predir/$_","$predir/.$_.flags"),@objects)).";"
218                         ." /bin/mv -f $predir/lufs.o $destmodule;";
219 }
220
221
222 sub build
223 {
224 my($kernel,$uname_r,$destmodule)=@_;
225
226         # Debian uname(1) does not support '-p'.
227         my $arch=_readfile "uname -p|" || _readfile "uname -m|";
228         chomp $arch;
229         my $single_config=-f "$kernel/.config";
230         if (!$single_config) {
231                 my $spec_config="$kernel/configs/kernel-$uname_r_base-$arch".($uname_smp ? "-smp" : "").".config";
232                 _system "ln -s $spec_config $kernel/.config"
233                                 if -f $spec_config;
234                 }
235         my $r;
236         if (-f "$kernel/Rules.make" && -f "$kernel/.config")
237                 { $r=build_make $kernel,$uname_r,$destmodule; }
238         else
239                 { $r=build_gcc $kernel,$uname_r,$destmodule; }
240         _system "rm -f $kernel/.config"
241                         if !$single_config;
242         return $r;
243 }
244
245
246 sub prebuild_kernel
247 {
248 my($dir,$vendor,$uname_r)=@_;
249
250         confess "Unrecognized vendor for dir: $dir" if !$vendor;
251         confess "Unrecognized uname_r for dir: $dir" if !$uname_r;
252         confess "Failed to build $dir" if !build $dir,$uname_r,$modbindir."/lufs-$vendor-$uname_r.o";
253 }
254
255
256 sub prebuild_rpm
257 {
258 my($rpm)=@_;
259
260         my $vendor=_readfile "rpm 2>/dev/null --qf '%{VENDOR}' -qp '$rpm' |";
261         $vendor=~s/,.*//;
262         $vendor=~tr/ //d;
263         my $uname_r;
264         $uname_r||=($rpm=~m#/kernel-source-([^-]+-[^-]+)[.][^.]+[.]rpm$#)[0];
265         my $uname_r_base=($uname_r=~/^([^-]+)/)[0];
266         my $dir;
267         do { $dir||=$_ if -d $_; } for ("/usr/src/linux-$uname_r");
268         do { $dir||=$_ if -d $_; } for ("/usr/src/linux-$uname_r_base");        # older RedHat kernels
269         confess "Kernel sources already found in $dir" if $dir;
270         # '--nodeps' to prevent: error: Failed dependencies: perl-base is needed by *mdk
271         _system "rpm -i --nodeps '$rpm'" and confess "RPM installation of $rpm";
272         do { $dir||=$_ if -d $_; } for ("/usr/src/linux-$uname_r");
273         do { $dir||=$_ if -d $_; } for ("/usr/src/linux-$uname_r_base");        # older RedHat kernels
274         $dir or confess "Kernel source tree not found in: $rpm";
275         _system "cp -p $dir/include/linux/rhconfig.h $dir/include/linux/rhconfig.h-orig"
276                                 if -f "$dir/include/linux/rhconfig.h";
277         # Do not use existing '$dir/tmp_include_depends' or '$dir/.depend'
278         # as it may contain non-existing pathnames:
279         _system "make -C $dir dep";
280         for my $smp ("","smp") {
281                 my @archs=qw(i386 i586 i686 athlon);
282                 for my $arch (@archs) {
283                         my %boot=(
284                                         "__BOOT_KERNEL_SMP"=>$smp,
285                                         map({ ("__MODULE_KERNEL_$_"=>($arch eq $_)); } @archs),
286                                         "__BOOT_KERNEL_BOOT"=>0,
287                                         "__BOOT_KERNEL_BOOTSMP"=>0,
288                                         "__BOOT_KERNEL_ENTERPRISE"=>0,
289                                         "__BOOT_KERNEL_BIGMEM"=>0,
290                                         "__BOOT_KERNEL_DEBUG"=>0,
291                                         );
292                         if (-f "$dir/include/linux/rhconfig.h") {
293                                 my $defines="";
294                                 for (keys(%boot)) {
295                                         $defines.="+#define $_ ".($boot{$_} ? 1 : 0)."\n";
296                                         }
297                                 _system "cp -p -f $dir/include/linux/rhconfig.h-orig $dir/include/linux/rhconfig.h";
298                                 _writefile "| patch $dir/include/linux/rhconfig.h",<<"RHCONFIG_H_NOBOOT_EOF";
299 --- ./include/linux/rhconfig.h-orig     Thu Aug 21 14:50:16 2003
300 +++ ./include/linux/rhconfig.h  Thu Aug 21 14:59:22 2003
301 \@\@ -10,7 +10,@{[ 7+keys(%boot) ]} \@\@
302   * /boot/kernel.h - initscripts should create it for us
303   */
304  
305 -#include "/boot/kernel.h"
306 +/* lufs: #include "/boot/kernel.h" */
307 $defines
308  #if defined(__BOOT_KERNEL_SMP) && (__BOOT_KERNEL_SMP == 1)
309  #define __module__smp
310 RHCONFIG_H_NOBOOT_EOF
311                                 }
312                         # Mandrake packages have no configs/ and they have just that '.config' file.
313                         my $single_config=-f "$dir/.config";
314                         if (!$single_config) {
315                                 my $spec_config="$dir/configs/kernel-$uname_r_base-$arch".($smp ? "-smp" : "").".config";
316                                 next if ! -f $spec_config;
317                                 _system "ln -s $spec_config $dir/.config";
318                                 }
319                         prebuild_kernel $dir,$vendor,$uname_r.$smp.".".$arch;
320                         _system "rm -f $dir/.config"
321                                         if !$single_config;
322                         }
323                 }
324         _system "rpm -e kernel-source-$uname_r" and confess "Remove of kernel-source-$uname_r";
325         _system "rm -rf $dir";
326 }
327
328 sub prebuild_dir
329 {
330 my($dir)=@_;
331
332         my $vendor;
333         chomp (my $debian=_readfile "/etc/debian_version","optional");
334         $debian=~tr#/#_#;
335         $vendor="Debian".$debian if $debian && $dir=~m#^/usr/src/#;
336         my $uname_r;
337         $uname_r||=($dir=~m#/kernel-headers-([^/]+)/*$#)[0];
338         $uname_r||=($dir=~m#/linux-([^/]+)/*$#)[0];
339         $uname_r||=($dir=~m#^/lib/modules/([^/]+)/build/*$#)[0];
340         prebuild_kernel $dir,$vendor,$uname_r;
341 }
342
343 sub prebuild
344 {
345         $vardir=undef();        # we may not yet have /var/lib/lufs
346         for (@ARGV) {
347                 do { prebuild_rpm $_; next; } if /[.]rpm$/;
348                 do { prebuild_dir $_; next; } if -d $_;
349                 confess "Unrecognized kernel: $_";
350                 }
351         exit 0;
352 }