Fixed build if '/etc/debian_version' contains slashes ('/').
[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.5"); }
34 my $modbindir=$basedir."/modbin";
35 my @sources=qw(proc.c inode.c dir.c file.c symlink.c);
36 my $tmp_dir="prepmod-tmp-dir";
37
38 my $quiet;
39 my $kernel;
40 my $prebuild;
41 use vars qw($kernel_gcc_args);
42 $kernel_gcc_args="";
43
44 my $lufsmnt_bin;
45 if ($0 eq "lufsmnt" || $0=~m#/lufsmnt$#) {
46         $quiet=1;
47         $lufsmnt_bin='@bindir@/lufsmnt-bin';
48         $lufsmnt_bin=~s#\$\Q{exec_prefix}\E#'@exec_prefix@';#ge;
49         $lufsmnt_bin=~s#\$\Q{prefix}\E#'@prefix@';#ge;
50         }
51 else {
52         die if !GetOptions(
53                         "q|quiet",\$quiet,
54                           "kernel",\$kernel,    # it must contain "include" subdir
55                           "modbindir=s",\$modbindir,
56                           "basedir=s",\$basedir,
57                           "prebuild",\&prebuild,
58                           "kernel-gcc-args=s",\$kernel_gcc_args,
59                         );
60         }
61
62
63 sub _readfile
64 {
65 my($filename,$optional)=@_;
66
67         local $/=undef();
68         local *F;
69         open F,$filename or do { cluck "Open \"$filename\": $!" if !$optional; return ""; };
70         my $r=<F>;
71         close F or cluck "Close \"$filename\": $!";
72         return $r;
73 }
74
75 sub _writefile
76 {
77 my($filename,@content)=@_;
78
79         local *F;
80         open F,($filename=~/^[|]/ ? "" : ">").$filename or confess "rewrite \"$filename\": $!";
81         print F @content;
82         close F or confess "close \"$filename\": $!";   # Do not &cluck as it may be pipe result
83 }
84
85 sub _system
86 {
87 my(@args)=@_;
88
89         print STDERR "+ ".join(" ",@args)."\n" if !$quiet;
90         return system @args;
91 }
92
93 sub _pass
94 {
95 my($load)=@_;
96
97         my $modproberc;
98         if ($load) {
99                 do { ($modproberc=_system $_) and cluck "$_ failed - ignoring"; }
100                                 for ("/sbin/depmod -aq","/sbin/modprobe lufs");
101                 }
102         exit ($modproberc ? 1 : 0) if !$lufsmnt_bin;
103         do { exec $lufsmnt_bin,@ARGV; };
104         confess "Failed to exec '$lufsmnt_bin': $!";
105 }
106
107
108 my $filesystems=_readfile "/proc/filesystems";  # 'lufs' may be built-in
109 _pass if $filesystems=~/\blufs$/m;
110 my $modules=_readfile "/proc/modules";  # 'lufs' may be already loaded
111 _pass if $modules=~/^lufs\b/m;
112 _pass if !_system "/sbin/modprobe lufs 2>/dev/null";
113
114 my $proc_version=_readfile "/proc/version";
115 my $uname_r=($proc_version=~/^Linux version (\S+)/)[0] || _readfile "uname -r|";
116 chomp $uname_r;
117 confess "Failed to detect kernel version" if !$uname_r;
118 my $uname_r_base=($uname_r=~/^([^-]+)/)[0];
119 print STDERR "Running kernel version: $uname_r (base version $uname_r_base)\n" if !$quiet;
120
121 my $moduledir="/lib/modules/$uname_r/kernel/fs/lufs";
122 print STDERR "Destination module directory: $moduledir\n" if !$quiet;
123
124 do { $kernel||=$_ if -d $_; } for ("/lib/modules/$uname_r/build");
125 do { $kernel||=$_ if -d $_; } for ("/usr/src/kernel-headers-$uname_r");
126 do { $kernel||=$_ if -d $_; } for ("/usr/src/linux-$uname_r");
127 do { $kernel||=$_ if -d $_; } for ("/usr/src/linux-$uname_r_base");
128 do { $kernel||=$_ if -d $_; } for ("/usr/src/linux");
129 if (!$kernel) {
130         print STDERR "Failed to find kernel headers for $uname_r\n" if !$kernel && !$quiet;
131         }
132 else {
133         # We need /usr/include/lufs/* for standard package compilation.
134         # There is no sense to make separate 'devel' package.
135         for ("$vardir/lufs.o") {
136                 next if !$vardir;
137                 # Create the 'lufs.o' in our /var/lib directory and only link it
138                 # to prevent using obsolete modules after upgrading 'lufs' package.
139                 # depmod(1) will take the larget symlink name - we must create directory for  it.
140                 if (build($kernel,$uname_r,$_)) {
141                         do { cluck "Failed to symlink $_"; next; }
142                                         if _system "/bin/rm -rf $moduledir; /bin/mkdir -p $moduledir; /bin/ln -s $_ $moduledir/lufs.o";
143                         _pass 1;
144                         }
145                 }
146         }
147
148 local $_;
149 for (<$modbindir/*-$uname_r*/*.o>,<$modbindir/*-${uname_r_base}*/*.o>,<$modbindir/*/*.o>) {
150         next if _system "/sbin/rmmod lufs 2>/dev/null; /sbin/insmod -o lufs -p $_ 2>/dev/null";
151         do { cluck "Failed to symlink $_"; next; }
152                         if _system "/bin/rm -rf $moduledir; /bin/mkdir -p $moduledir; /bin/ln -s $_ $moduledir/lufs.o";
153         _pass 1;
154         }
155 confess "lufs module not loaded: Try running $basedir/prepmod to see more.";
156
157
158 sub build
159 {
160 my($kernel,$uname_r,$destmodule)=@_;
161
162         print STDERR "Using kernel sources: $kernel\n" if !$quiet;
163         confess "Kernel sources $kernel do not contain 'include' subdirectory" if ! -d $kernel."/include";
164
165         my $kdebug="";
166         do { $kdebug=$_ if !/^@/; } for ('@KDEBUG_FLAGS@');
167         my $cmdline="/usr/bin/gcc"
168                         .($quiet ? " 2>/dev/null" : "")
169                         ." -Wall -Wstrict-prototypes -Wno-trigraphs"
170                         ." -O2 -fno-strict-aliasing -fno-common -fomit-frame-pointer -pipe -mpreferred-stack-boundary=2"
171                         #." -march=i586"
172                         ." -D__KERNEL__ -DMODULE -DLINUX -DKBUILD_MODNAME=lufs"
173                         ." ".$kdebug
174                         ." ".$kernel_gcc_args
175                         ." -I$kernel/include"
176                         ." -I$kernel/include/asm-i386/mach-default";    # gcc should not care if this 2.5 dir does not exist
177
178         my $config=_readfile "$kernel/.config","optional";
179         my $autoconf=_readfile "$kernel/include/linux/autoconf.h","optional";
180         $cmdline.=" -DMODVERSIONS -include $kernel/include/linux/modversions.h"
181                         if 0
182                                         || $config=~/^CONFIG_MODVERSIONS=y\b/m
183                                         || $autoconf=~/^#define CONFIG_MODVERSIONS\b/m
184                                         || (!$config && !$autoconf);    # assume modversions if not known
185
186         my @objects=map({ my $o=$_; $o=~s/[.]c$/.o/; $o; } @sources);
187         return !_system "set -e; /bin/mkdir -p `dirname $destmodule`; /bin/rm -f $destmodule;"
188                         .(!$vardir ? "" : " cd $vardir;")
189                         ." $cmdline -c ".join(" ",map({ srcdir($uname_r)."/".$_; } @sources)).";"
190                         ." /usr/bin/ld -r -o $destmodule ".join(" ",@objects).";"
191                         ." /bin/rm -f ".join(" ",@objects);
192 }
193
194
195 sub prebuild_kernel
196 {
197 my($dir,$vendor,$uname_r)=@_;
198
199         confess "Unrecognized vendor for dir: $dir" if !$vendor;
200         confess "Unrecognized uname_r for dir: $dir" if !$uname_r;
201         confess "Failed to build $dir" if !build $dir,$uname_r,$modbindir."/lufs-$vendor-$uname_r.o";
202 }
203
204
205 sub prebuild_rpm
206 {
207 my($rpm)=@_;
208
209         my $vendor=_readfile "rpm 2>/dev/null --qf '%{VENDOR}' -qp '$rpm' |";
210         $vendor=~s/,.*//;
211         $vendor=~tr/ //d;
212         my $uname_r;
213         $uname_r||=($rpm=~m#/kernel-source-([^-]+-[^-]+)[.][^.]+[.]rpm$#)[0];
214         my $uname_r_base=($uname_r=~/^([^-]+)/)[0];
215         _system "/bin/rm -rf $tmp_dir; /bin/mkdir $tmp_dir; set -e;"
216                         # Here we will extract even some false 'include's but we filter dirs just for speed/space.
217                         ." /usr/bin/rpm2cpio '$rpm' | (cd $tmp_dir; /bin/cpio -id --quiet ./usr/src/linux*/{.config,include/*})"
218                         and confess "Extraction of: $rpm";
219         my $dir;
220         do { $dir||=$_ if -d $_; } for ($tmp_dir."/usr/src/linux-$uname_r");
221         do { $dir||=$_ if -d $_; } for ($tmp_dir."/usr/src/linux-$uname_r_base");       # older RedHat kernels
222         $dir or confess "Kernel source tree not found in: $rpm";
223         _writefile "| patch $dir/include/linux/rhconfig.h",<<'RHCONFIG_H_NOBOOT_EOF';
224 --- ./include/linux/rhconfig.h-orig     Thu Aug 21 14:50:16 2003
225 +++ ./include/linux/rhconfig.h  Thu Aug 21 14:59:22 2003
226 @@ -10,7 +10,7 @@
227   * /boot/kernel.h - initscripts should create it for us
228   */
229  
230 -#include "/boot/kernel.h"
231 +/* lufs: #include "/boot/kernel.h" */
232  
233  #if defined(__BOOT_KERNEL_SMP) && (__BOOT_KERNEL_SMP == 1)
234  #define __module__smp
235 RHCONFIG_H_NOBOOT_EOF
236         for my $smp ("","smp") {
237                 my @archs=qw(i386 i586 i686 athlon);
238                 for my $arch (@archs) {
239                         my %boot=(
240                                         "__BOOT_KERNEL_SMP"=>$smp,
241                                         map({ ("__MODULE_KERNEL_$_"=>($arch eq $_)); } @archs),
242                                         "__BOOT_KERNEL_BOOT"=>0,
243                                         "__BOOT_KERNEL_BOOTSMP"=>0,
244                                         "__BOOT_KERNEL_ENTERPRISE"=>0,
245                                         "__BOOT_KERNEL_BIGMEM"=>0,
246                                         "__BOOT_KERNEL_DEBUG"=>0,
247                                         );
248                         local $kernel_gcc_args=join(" ",map({ " -D$_=".($boot{$_} ? 1 : 0); } keys(%boot)))
249                                         ." -march=$arch"
250                                         ." ".$kernel_gcc_args;
251                         prebuild_kernel $dir,$vendor,$uname_r.$smp.".".$arch;
252                         }
253                 }
254         _system "/bin/rm -rf $tmp_dir" and confess "Deletion of: $tmp_dir";
255 }
256
257 sub prebuild_dir
258 {
259 my($dir)=@_;
260
261         my $vendor;
262         chomp (my $debian=_readfile "/etc/debian_version","optional");
263         $debian=~tr#/#_#;
264         $vendor="Debian".$debian if $debian && $dir=~m#^/usr/src/#;
265         my $uname_r;
266         $uname_r||=($dir=~m#/kernel-headers-([^/]+)/*$#)[0];
267         $uname_r||=($dir=~m#/linux-([^/]+)/*$#)[0];
268         $uname_r||=($dir=~m#^/lib/modules/([^/]+)/build/*$#)[0];
269         prebuild_kernel $dir,$vendor,$uname_r;
270 }
271
272 sub prebuild
273 {
274         $vardir=undef();        # we may not yet have /var/lib/lufs
275         for (@ARGV) {
276                 do { prebuild_rpm $_; next; } if /[.]rpm$/;
277                 do { prebuild_dir $_; next; } if -d $_;
278                 confess "Unrecognized kernel: $_";
279                 }
280         exit 0;
281 }