#!/usr/bin/perl -w
#
# Retrieve modules required for an initrd image
# $Id: include-modules,v 1.2 2002/05/26 09:10:59 mcb30 Exp $

unless ( @ARGV ) {
  die "Syntax: $0 [ -d target_directory ] module_1 module_2 module_3\n"
}

# Parse command line arguments
my @requested_modules = ();
my $target_dir = "";
my $kernel_ver = `uname -r`;
my $quiet;
chomp ( my $current_kernel_ver = `uname -r` );
# for pegasus (maybe others)
my @usb = ();
if ($current_kernel_ver =~ /^2.4/) {
  push @usb, ("usb-ohci", "usb-uhci");
} else {
  push @usb, ("ohci-hcd", "uhci-hcd");
}    
while ( $_ = shift ) {
  if    ( /-d/ ) { $target_dir = shift }
  elsif ( /-k/ ) { $kernel_ver = shift }
  elsif ( /-q/ ) { $quiet = 1 }
  else  {
    push @requested_modules, @usb if $_ =~ /pegasus/; 
    push @requested_modules, $_ 
  };
}

# Create target directory if required
if ( $target_dir ) {
  print STDERR "Target directory is $target_dir\n" unless $quiet;
  system ( "mkdir -p $target_dir" );
  chdir $target_dir;
}

# Use modprobe -nav to retrieve locations of modules and their dependencies
print STDERR "Requested modules ". join (' ', @requested_modules)."\n" unless $quiet;
my @modules_dups;
my @module_list;
foreach my $module ( @requested_modules ) {
  # found that modprobe -nva doesn't return the dependencies 
  # if the module is in use on the host machine
  if ($current_kernel_ver =~ /^2.4/) {
    @module_list = map { /^\S+\s+(.*)$/ ; $1 } `/sbin/modprobe -nva $module | grep ^insmod`;
  } else {
    @module_list = map { /^\S+\s+(.*)$/ ; $1 } `/sbin/modprobe --set-version $kernel_ver --show-depends $module | grep ^insmod`;
  }	  
  # some modules (like af_packet), don't respond to -nva under 2.6 kernel
  # some modules also have name mismatches between 2.4 and 2.6 kernels
  # try to grab it with find, although we may have issues with dependencies
  @module_list = `find /lib/modules/$kernel_ver -name '$module.*'` unless @module_list;
  die "Cannot find any modules matching $module\n" unless @module_list;
  # still get some empty results that generate errors later
  push @modules_dups, @module_list if @module_list;
}

# Remove duplicates from list
my %module_basenames = ();
my @modules = ();
my @mod_basenames = ();
foreach my $module ( @modules_dups ) {
  # Ugly hack : assume that dependencies are independent of kernel version
  # This seems to be necessary because we can't run modprobe and specify
  # an alternate modules.dep file; it refuses to understand lines of the 
  # form "depfile=XXX" as documented in modules.conf(5)
  # (sb) if the module was found with "find" this isn't needed  
  if ($module !~ /$kernel_ver/) {
    $module =~ s/.ko.gz/.o.gz/ if ($current_kernel_ver =~ /2.6/ && $kernel_ver =~ /2.4/);
    $module =~ s/.o.gz/.ko.gz/ if ($current_kernel_ver =~ /2.4/ && $kernel_ver =~ /2.6/); 
  }
  my $basename;
  if ($kernel_ver =~ /2.6/) {  
    ($basename) = ( $module =~ /([^\/]+)\.ko/ ); 
  } else {
    ($basename) = ( $module =~ /([^\/]+)\.o/ );   
  }
  push @mod_basenames, $basename if ! grep { /$basename$/ } @mod_basenames;
}

# Process module list
print "#!/bin/sh\n";
foreach my $basename ( @mod_basenames ) {
  my $module = `find /lib/modules/$kernel_ver -name '$basename.*'`;
  chomp $module;
  # Report via stdout
  if ($module) {
    print STDERR "Using module $basename from $module\n" unless $quiet;
    # Copy uncompressed module to current directory
    if ($kernel_ver =~ /2.6/) {
      system ("gunzip -c $module > $basename.ko");
      # Print insmod line to stdout
      print "insmod /lib/modules/$kernel_ver/$basename.ko\n";
      if ($basename =~ /pegasus/) {
        print "sleep 5\n";
      }
    } else {
      system ("gunzip -c $module > $basename.o");
      # Print insmod line to stdout
      print "insmod $basename\n"; 
    }
  }
}
