#! /usr/bin/perl -w # # $Id$ # Check and possibly rebuild lufs kernel module for the current kernel. # Copyright (C) 2003 Jan Kratochvil # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; exactly version 2 of June 1991 is required # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use Carp qw(cluck confess); use Getopt::Long; # Do not use any non-core Perl modules here. # Be perl-5.0 compatible (is it needed if we require Linux kernel >= 2.4 ?) my $basedir='@datadir@/lufs'; my $vardir='@localstatedir@/lib/lufs'; my $includedir='@includedir@'; # '/lufs' is required by C '#include' $basedir=~s#\$\Q{prefix}\E#'@prefix@';#ge; $vardir=~s#\$\Q{prefix}\E#'@prefix@';#ge; $vardir="" if $vardir=~/^@/; $includedir=~s#\$\Q{prefix}\E#'@prefix@';#ge; $includedir="" if $includedir=~/^@/; sub srcdir { my($uname_r)=@_; $basedir."/".($uname_r lt "2.5" ? "2.4" : "2.5"); } my $modbindir=$basedir."/modbin"; my @sources=qw(proc.c inode.c dir.c file.c symlink.c); my $quiet; my $kernel; my $prebuild; use vars qw($kernel_gcc_args); $kernel_gcc_args=""; $kernel_gcc_args="-I$includedir" if $includedir; my $lufsmnt_bin; if ($0 eq "lufsmnt" || $0=~m#/lufsmnt$#) { $quiet=1; $lufsmnt_bin='@bindir@/lufsmnt-bin'; $lufsmnt_bin=~s#\$\Q{exec_prefix}\E#'@exec_prefix@';#ge; $lufsmnt_bin=~s#\$\Q{prefix}\E#'@prefix@';#ge; } else { die if !GetOptions( "q|quiet",\$quiet, "kernel",\$kernel, # it must contain "include" subdir "modbindir=s",\$modbindir, "basedir=s",\$basedir, "prebuild",\&prebuild, "kernel-gcc-args=s",\$kernel_gcc_args, ); } sub _readfile { my($filename,$optional)=@_; local $/=undef(); local *F; open F,$filename or do { cluck "Open \"$filename\": $!" if !$optional; return ""; }; my $r=; close F or cluck "Close \"$filename\": $!"; return $r; } sub _writefile { my($filename,@content)=@_; local *F; open F,($filename=~/^[|]/ ? "" : ">").$filename or confess "rewrite \"$filename\": $!"; print F @content; close F or confess "close \"$filename\": $!"; # Do not &cluck as it may be pipe result } sub _system { my(@args)=@_; print STDERR "+ ".join(" ",@args)."\n" if !$quiet; return system @args; } sub _pass { my($load)=@_; my $modproberc; if ($load) { do { ($modproberc=_system $_) and cluck "$_ failed - ignoring"; } for ("/sbin/depmod -aq","/sbin/modprobe lufs"); } exit ($modproberc ? 1 : 0) if !$lufsmnt_bin; do { exec $lufsmnt_bin,@ARGV; }; confess "Failed to exec '$lufsmnt_bin': $!"; } my $filesystems=_readfile "/proc/filesystems"; # 'lufs' may be built-in _pass if $filesystems=~/\blufs$/m; my $modules=_readfile "/proc/modules"; # 'lufs' may be already loaded _pass if $modules=~/^lufs\b/m; _pass if !_system "/sbin/modprobe lufs 2>/dev/null"; my $proc_version=_readfile "/proc/version"; my $uname_r=($proc_version=~/^Linux version (\S+)/)[0] || _readfile "uname -r|"; chomp $uname_r; confess "Failed to detect kernel version" if !$uname_r; my $uname_smp=($uname_r=~s/smp$// && "smp"); my $uname_r_base=($uname_r=~/^([^-]+)/)[0]; print STDERR "Running kernel version: $uname_r (base version $uname_r_base)\n" if !$quiet; my $moduledir="/lib/modules/$uname_r$uname_smp/kernel/fs/lufs"; print STDERR "Destination module directory: $moduledir\n" if !$quiet; do { $kernel||=$_ if -d $_; } for ("/lib/modules/$uname_r/build"); do { $kernel||=$_ if -d $_; } for ("/usr/src/kernel-headers-$uname_r"); do { $kernel||=$_ if -d $_; } for ("/usr/src/linux-$uname_r"); do { $kernel||=$_ if -d $_; } for ("/usr/src/linux-$uname_r_base"); do { $kernel||=$_ if -d $_; } for ("/usr/src/linux"); if (!$kernel) { print STDERR "Failed to find kernel headers for $uname_r\n" if !$kernel && !$quiet; } else { # We need /usr/include/lufs/* for standard package compilation. # There is no sense to make separate 'devel' package. for ("$vardir/lufs.o") { next if !$vardir; # Create the 'lufs.o' in our /var/lib directory and only link it # to prevent using obsolete modules after upgrading 'lufs' package. # depmod(1) will take the larget symlink name - we must create directory for it. if (build($kernel,$uname_r,$_)) { do { cluck "Failed to symlink $_"; next; } if _system "/bin/rm -rf $moduledir; /bin/mkdir -p $moduledir; /bin/ln -s $_ $moduledir/lufs.o"; _pass 1; } } } local $_; for (<$modbindir/*-$uname_r*/*.o>,<$modbindir/*-${uname_r_base}*/*.o>,<$modbindir/*/*.o>) { next if _system "/sbin/rmmod lufs 2>/dev/null; /sbin/insmod -o lufs -p $_ 2>/dev/null"; do { cluck "Failed to symlink $_"; next; } if _system "/bin/rm -rf $moduledir; /bin/mkdir -p $moduledir; /bin/ln -s $_ $moduledir/lufs.o"; _pass 1; } confess "lufs module not loaded: Try running $basedir/prepmod to see more."; sub build_gcc { my($kernel,$uname_r,$destmodule)=@_; print STDERR "Using kernel sources: $kernel\n" if !$quiet; confess "Kernel sources $kernel do not contain 'include' subdirectory" if ! -d $kernel."/include"; my $kdebug=""; do { $kdebug=$_ if !/^@/; } for ('@KDEBUG_FLAGS@'); my $cmdline="/usr/bin/gcc" .($quiet ? " 2>/dev/null" : "") ." -Wall -Wstrict-prototypes -Wno-trigraphs" ." -O2 -fno-strict-aliasing -fno-common -fomit-frame-pointer -pipe -mpreferred-stack-boundary=2" #." -march=i586" ." -D__KERNEL__ -DMODULE -DLINUX -DKBUILD_MODNAME=lufs" ." ".$kdebug ." ".$kernel_gcc_args ." -I$kernel/include" ." -I$kernel/include/asm-i386/mach-default"; # gcc should not care if this 2.5 dir does not exist my $config=_readfile "$kernel/.config","optional"; my $autoconf=_readfile "$kernel/include/linux/autoconf.h","optional"; $cmdline.=" -DMODVERSIONS -include $kernel/include/linux/modversions.h" if 0 || $config=~/^CONFIG_MODVERSIONS=y\b/m || $autoconf=~/^#define CONFIG_MODVERSIONS\b/m || (!$config && !$autoconf); # assume modversions if not known my @objects=map({ my $o=$_; $o=~s/[.]c$/.o/; $o; } @sources); return !_system "set -e; /bin/mkdir -p `dirname $destmodule`; /bin/rm -f $destmodule;" .(!$vardir ? "" : " cd $vardir;") ." $cmdline -c ".join(" ",map({ srcdir($uname_r)."/".$_; } @sources)).";" ." /usr/bin/ld -r -o $destmodule ".join(" ",@objects).";" ." /bin/rm -f ".join(" ",@objects); } sub build_make { my($kernel,$uname_r,$destmodule)=@_; print STDERR "Using kernel sources: $kernel\n" if !$quiet; confess "Kernel sources $kernel do not contain 'Rules.make' file" if ! -f $kernel."/Rules.make"; my $kdebug=""; do { $kdebug=$_ if !/^@/; } for ('@KDEBUG_FLAGS@'); my $predir=srcdir $uname_r; $predir=$ENV{"PWD"}."/$predir" if $predir!~m#^/#; my $cmdline="make -C $kernel" ." SUBDIRS=\"$predir\" modules" ." EXTRA_CFLAGS=\"$kernel_gcc_args\"" .($quiet ? ' &>/dev/null' : ''); my @objects=map({ my $o=$_; $o=~s/[.]c$/.o/; $o; } @sources); return !_system "set -e; /bin/mkdir -p `dirname $destmodule`; /bin/rm -f $destmodule;" ." $cmdline;" ." /bin/rm -f ".join(" ",map(("$predir/$_","$predir/.$_.flags"),@objects)).";" ." /bin/mv -f $predir/lufs.o $destmodule;"; } sub build { my($kernel,$uname_r,$destmodule)=@_; # Debian uname(1) does not support '-p'. my $arch=_readfile "uname -p|" || _readfile "uname -m|"; chomp $arch; my $single_config=-f "$kernel/.config"; if (!$single_config) { my $spec_config="$kernel/configs/kernel-$uname_r_base-$arch".($uname_smp ? "-smp" : "").".config"; _system "ln -s $spec_config $kernel/.config" if -f $spec_config; } my $r; if (-f "$kernel/Rules.make" && -f "$kernel/.config") { $r=build_make $kernel,$uname_r,$destmodule; } else { $r=build_gcc $kernel,$uname_r,$destmodule; } _system "rm -f $kernel/.config" if !$single_config; return $r; } sub prebuild_kernel { my($dir,$vendor,$uname_r)=@_; confess "Unrecognized vendor for dir: $dir" if !$vendor; confess "Unrecognized uname_r for dir: $dir" if !$uname_r; confess "Failed to build $dir" if !build $dir,$uname_r,$modbindir."/lufs-$vendor-$uname_r.o"; } sub prebuild_rpm { my($rpm)=@_; my $vendor=_readfile "rpm 2>/dev/null --qf '%{VENDOR}' -qp '$rpm' |"; $vendor=~s/,.*//; $vendor=~tr/ //d; my $uname_r; $uname_r||=($rpm=~m#/kernel-source-([^-]+-[^-]+)[.][^.]+[.]rpm$#)[0]; my $uname_r_base=($uname_r=~/^([^-]+)/)[0]; my $dir; do { $dir||=$_ if -d $_; } for ("/usr/src/linux-$uname_r"); do { $dir||=$_ if -d $_; } for ("/usr/src/linux-$uname_r_base"); # older RedHat kernels confess "Kernel sources already found in $dir" if $dir; # '--nodeps' to prevent: error: Failed dependencies: perl-base is needed by *mdk _system "rpm -i --nodeps '$rpm'" and confess "RPM installation of $rpm"; do { $dir||=$_ if -d $_; } for ("/usr/src/linux-$uname_r"); do { $dir||=$_ if -d $_; } for ("/usr/src/linux-$uname_r_base"); # older RedHat kernels $dir or confess "Kernel source tree not found in: $rpm"; _system "cp -p $dir/include/linux/rhconfig.h $dir/include/linux/rhconfig.h-orig" if -f "$dir/include/linux/rhconfig.h"; # Do not use existing '$dir/tmp_include_depends' or '$dir/.depend' # as it may contain non-existing pathnames: _system "make -C $dir dep"; for my $smp ("","smp") { my @archs=qw(i386 i586 i686 athlon); for my $arch (@archs) { my %boot=( "__BOOT_KERNEL_SMP"=>$smp, map({ ("__MODULE_KERNEL_$_"=>($arch eq $_)); } @archs), "__BOOT_KERNEL_BOOT"=>0, "__BOOT_KERNEL_BOOTSMP"=>0, "__BOOT_KERNEL_ENTERPRISE"=>0, "__BOOT_KERNEL_BIGMEM"=>0, "__BOOT_KERNEL_DEBUG"=>0, ); if (-f "$dir/include/linux/rhconfig.h") { my $defines=""; for (keys(%boot)) { $defines.="+#define $_ ".($boot{$_} ? 1 : 0)."\n"; } _system "cp -p -f $dir/include/linux/rhconfig.h-orig $dir/include/linux/rhconfig.h"; _writefile "| patch $dir/include/linux/rhconfig.h",<<"RHCONFIG_H_NOBOOT_EOF"; --- ./include/linux/rhconfig.h-orig Thu Aug 21 14:50:16 2003 +++ ./include/linux/rhconfig.h Thu Aug 21 14:59:22 2003 \@\@ -10,7 +10,@{[ 7+keys(%boot) ]} \@\@ * /boot/kernel.h - initscripts should create it for us */ -#include "/boot/kernel.h" +/* lufs: #include "/boot/kernel.h" */ $defines #if defined(__BOOT_KERNEL_SMP) && (__BOOT_KERNEL_SMP == 1) #define __module__smp RHCONFIG_H_NOBOOT_EOF } # Mandrake packages have no configs/ and they have just that '.config' file. my $single_config=-f "$dir/.config"; if (!$single_config) { my $spec_config="$dir/configs/kernel-$uname_r_base-$arch".($smp ? "-smp" : "").".config"; next if ! -f $spec_config; _system "ln -s $spec_config $dir/.config"; } prebuild_kernel $dir,$vendor,$uname_r.$smp.".".$arch; _system "rm -f $dir/.config" if !$single_config; } } _system "rpm -e kernel-source-$uname_r" and confess "Remove of kernel-source-$uname_r"; _system "rm -rf $dir"; } sub prebuild_dir { my($dir)=@_; my $vendor; chomp (my $debian=_readfile "/etc/debian_version","optional"); $debian=~tr#/#_#; $vendor="Debian".$debian if $debian && $dir=~m#^/usr/src/#; my $uname_r; $uname_r||=($dir=~m#/kernel-headers-([^/]+)/*$#)[0]; $uname_r||=($dir=~m#/linux-([^/]+)/*$#)[0]; $uname_r||=($dir=~m#^/lib/modules/([^/]+)/build/*$#)[0]; prebuild_kernel $dir,$vendor,$uname_r; } sub prebuild { $vardir=undef(); # we may not yet have /var/lib/lufs for (@ARGV) { do { prebuild_rpm $_; next; } if /[.]rpm$/; do { prebuild_dir $_; next; } if -d $_; confess "Unrecognized kernel: $_"; } exit 0; }