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