summaryrefslogtreecommitdiffstats
path: root/contrib/initrd/mknbi-set
blob: e61acac9f9abcb843f3c42b006c4589f0c28e7ab (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#!/usr/bin/perl -w
#
# $Id$
# Maintains set of NBIs based on currently-installed kernels
# Network card module sets are taken from /etc/mknbi-set.conf

use strict;
use vars qw($verbosity);

use constant EB_PCI_DEVICE => 1;

# Utility function: calculate output id given a kernel file name and
# space-separated list of modules
sub calc_output_id ($$) {
  my $kernel = shift;
  my $moduleset = shift;
  my $kernel_ver = "";
  ( $kernel_ver ) = ( $kernel =~ /vmlinuz-(.*)$/ );
  ( my $output_id = "$moduleset".( $kernel_ver ? ".$kernel_ver" : "" ) ) =~ tr/,/./;
  return ( $kernel_ver, $output_id );
}

# Utility function: read modules.pcimap-style file
# Add modules to modulesets hash, write out dhcpd.conf fragment
sub read_config_file ($$$$) {
  my $configfile = shift;
  my $modulesets = shift;
  my $dhcpfh = shift;
  my $alwaysuse = shift;

  print "Scanning through $configfile for network modules...\n" if $verbosity >= 1;
  open CF, $configfile or die "Could not open $configfile: $!\n";
  chomp ( my $tempmodule = `mktemp /tmp/mknbi-set.XXXXXX` );
  chomp ( my $cwd = `pwd` ); chdir '/'; # Modprobe searches the current directory...
  print $dhcpfh "    \# Generated from $configfile\n";
  while (<CF>) {
    chomp;
    next if /^[\#;]/ or /^\s*$/;
    ( my $module, undef, my $vendor, my $device ) = /^(\S+)(\s+(\S+)\s+(\S+))?/ ;
    $modulesets->{$module} = 1 if $alwaysuse;
    if ( ! exists $modulesets->{$module} ) {
      # Check to see if module is a network module
      # Only do this the first time we encounter a module
      my @modulepaths = `/sbin/modprobe -l $module.o*` ;
      chomp ( my $modulepath = $modulepaths[0] );
      if ( $modulepath ) {
	if ( $modulepath =~ /.o.gz$/ ) {
	  system ( "zcat $modulepath > $tempmodule" );
	} else {
	  system ( "cp $modulepath $tempmodule" );
	}
	$modulesets->{$module} = 0;
	foreach ( `nm $tempmodule` ) {
	  chomp;
	  $modulesets->{$module} = 1 if /(ether|wlan)/ ;
	}
	unlink $tempmodule;
      } else {
	print STDERR "Cannot locate module $module specified in $configfile\n";
      }
    }
    if ( $modulesets->{$module} ) {
      if ( $vendor ) {
	print "$module ($vendor,$device) listed in $configfile\n" if $verbosity >= 2;
	printf $dhcpfh ( "    if option etherboot.nic-dev-id = %02x:%02x:%02x:%02x:%02x { option etherboot.kmod \"%s\"; }\n",
			 EB_PCI_DEVICE,
			 ( hex($vendor) >> 8 ) & 0xff, hex($vendor) & 0xff,
			 ( hex($device) >> 8 ) & 0xff, hex($device) & 0xff,
			 $module );
      } else {
	print "$module (without PCI IDs) listed in $configfile\n" if $verbosity >= 2;
      }
    }
  }
  close CF;
  print $dhcpfh "\n";
  chdir $cwd;
}

my $conffile = '/etc/mknbi-set.conf';
my $mkinitrd_net = 'mkinitrd-net';
my $mknbi = 'mknbi-linux';
my $output_dir = '/var/lib/tftpboot';
my $dhcpfile = '/etc/dhcpd.conf.etherboot-pcimap.include';
my $use_local;
our $verbosity = 1;
my $modulesets = {};
my $kernel = '';
my @kernels = ();

my $usage="Usage: $0 [-l|--local] [-q] [-v] [-r|--refresh module[,module...]] [--help]";

# Parse command-line options
while ( $_ = shift ) {
  if ( /-l|--local/ ) {
    $conffile = 'mknbi-set.conf';
    $mkinitrd_net = './mkinitrd-net';
    $mknbi = './mknbi-linux --format=nbi --target=linux';
    $output_dir = 'tftpboot';
    $dhcpfile = 'tftpboot/dhcpd.conf.etherboot-pcimap.include';
    $use_local = 1;
  } elsif ( /-r|--refresh/ ) {
    my $moduleset = shift;
    $modulesets->{$moduleset} = 1;
  } elsif ( /-k|--kernel/ ) {
    $kernel = shift;
  } elsif ( /-v|--verbose/ ) {
    $verbosity++;
  } elsif ( /-q|--quiet/ ) {
    $verbosity--;
  } elsif ( /--help/ ) {
    die "$usage\n".
	"  -k, --kernel   Build NBIs for a particular kernel\n".
        "  -l, --local    Run locally from CVS (for developers only)\n".
	"  -r, --refresh  Refresh NBI for a particular module\n".
	"  -v, --verbose  Be more verbose\n".
	"  -q, --quiet    Be less verbose\n";
  } else {
    die "$usage\n";
  }
}

# Get set of current kernels
if ($kernel) {
  @kernels = ( $kernel );
} else {
  @kernels = glob('/boot/vmlinuz*');
}
die "Could not find any kernels in /boot\n" unless @kernels;

# If modules have been specified via --refresh, do not scan for modules or rewrite the
# dhcpd.conf fragment file
unless ( %$modulesets ) {
  # Write dhcpd.conf fragment file
  open my $dhcpfh, ">$dhcpfile" or die "Could not open $dhcpfile for writing: $!\n";
  print $dhcpfh "# Etherboot PCI ID -> Linux kernel module mapping file\n";
  print $dhcpfh "# Generated by mknbi-set on ".(scalar localtime)."\n";
  print $dhcpfh "#\n";
  print $dhcpfh "if substring ( option vendor-class-identifier, 0, 9 ) = \"Etherboot\" {\n";
  print $dhcpfh "  if exists etherboot.nic-dev-id {\n";
  print $dhcpfh "    \# Legacy nic-dev-id mechanism: there are some DLink DFE538 cards in circulation that\n";
  print $dhcpfh "    \# predated the change to the new nic-dev-id binary structure\n";
  print $dhcpfh "    if option etherboot.nic-dev-id = \"PCI:1186:1300\" { option etherboot.kmod \"8139too\"; }\n";
  print $dhcpfh "\n";

  # Get set of network modules to build NBIs for
  # Read explicitly-specified module sets from $conffile
  read_config_file($conffile, $modulesets, $dhcpfh, 1);
  # Obtain list of all network modules from pcimap file
  my $pcimap;
  foreach ( `/sbin/modprobe -c` ) {
    $pcimap = $1 if /^pcimap.*?=(.*)$/;
  }
  if ( $pcimap ) {
    read_config_file($pcimap, $modulesets, $dhcpfh, 0);
  } else {
    print STDERR "Could not identify pcimap file\n";
  }
  # Finish off dhcpd.conf fragment file
  print $dhcpfh "  }\n}\n";
  close $dhcpfh;
}

# Build initrd and nbi for each kernel-moduleset combination
foreach my $moduleset ( sort keys %$modulesets ) {
  next unless $modulesets->{$moduleset}; # Ignore if value is 0
  print "Building NBIs for module set $moduleset\n" if $verbosity >= 1;
  foreach my $kernel ( @kernels ) {
    ( my $kernel_ver, my $output_id ) = calc_output_id ( $kernel, $moduleset );
    if ( -l $kernel ) {
      # Symbolic link; create matching symlink
      my $real_kernel = readlink ( $kernel );
      ( my $real_kernel_ver, my $real_output_id ) = calc_output_id ( $real_kernel, $moduleset );
      print "Symlinking $output_id to $real_output_id\n" if $verbosity >= 2;
      my $initrd_file = "$output_dir/initrd-$output_id.img";
      unlink ( $initrd_file ) if -l $initrd_file;
      system ( "ln -s initrd-$real_output_id.img $initrd_file" ) == 0 or print STDERR "Could not symlink $initrd_file to initrd-$real_output_id.img: $!\n";
      my $nbi_file = "$output_dir/boot-$output_id.nbi";
      unlink ( $nbi_file ) if -l $nbi_file;
      system ( "ln -s boot-$real_output_id.nbi $nbi_file" ) == 0 or print STDERR "Could not symlink $nbi_file to boot-$real_output_id.nbi: $!\n";
    } else {
      # Real file: create initrd and nbi
      print "Creating initrd and nbi for $output_id\n" if $verbosity >= 2;
      ( my $moduleset_spaces = $moduleset ) =~ tr/,/ /;
      my $initrd_cmd = "$mkinitrd_net --nolink ".
	  ( $use_local ? "--local " : "" ).
	      ( $kernel_ver ? "--kernel $kernel_ver " : "" ).
		  ( $verbosity >= 2 ? "" : "-q " ).
		      $moduleset_spaces;
      print "$initrd_cmd\n" if $verbosity >= 3;
      if ( system ( $initrd_cmd ) == 0 ) {
	my $mknbi_cmd = "$mknbi $kernel $output_dir/initrd-$output_id.img > $output_dir/boot-$output_id.nbi";
	print "$mknbi_cmd\n" if $verbosity >= 3;
	system ( $mknbi_cmd ) == 0 or print STDERR "mknbi failed: $!\n";
      } else {
	print STDERR "$initrd_cmd failed: $!\n";
      }
    }
  }
}