| #!/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"; |
| } |
| } |
| } |
| } |