#! /usr/bin/perl -w # m0n0-inst-modules (http://www.xs4all.nl/~fredmol/m0n0) # Installs modules for m0n0wall (http://m0n0.ch/wall) # # Copyright (C) 2004 Fred Mol . # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. use Cwd 'abs_path'; use File::Basename; use File::Copy; sub usage() { die "Usage: $0 [-m mfsroot-size] m0n0wall-iso name-of-new-iso module1 [module2 ...]\n" } # Mounts the file-system-in-a-file $mfsroot as $mnt. sub mountmfs() { system "mdconfig -a -t vnode -f $mfsroot -u 0"; system "mount /dev/md0c $mnt"; } # Unmounts the file-system-in-a-file mounted at $mnt sub unmount() { system "umount $mnt"; system "mdconfig -d -u md0"; } if (@ARGV >= 5 && $ARGV[0] eq "-m") { $mfsrootSize = $ARGV[1]; die "unkown size specification: $mfsrootSize\n" if ($mfsrootSize !~ m/^\d+[bkmgw]?$/); splice @ARGV, 0, 2; } usage() if (@ARGV < 3); $srcIso = $ARGV[0]; $tgtIso = $ARGV[1]; splice @ARGV, 0, 2; # Check the arguments. die "$srcIso: $!\n" if (! -f $srcIso); $kind = `file -bz $srcIso`; if ($kind =~ m/9660/) { $iso = 1; } elsif ($kind !~ m/x86 boot.*BSD/) { die "$srcIso is not an iso9660 or x86 bootable image\n"; } foreach $module (@ARGV) { die "$module: $!\n" if (! -d $module && ! -f $module); if (-f $module) { $kind = `file -bz $module`; die "$module is not a (gzipped) tar archive\n" if ($kind !~ m/tar/); } } # Create tmp directory to work in. $tmpDir = "/tmp/m0n0install$$"; die "$tmpDir exists, please remove it first\n" if (-e $tmpDir); mkdir($tmpDir, 0755); # Create directory to mount file-system-in-a-files at. $mnt = "$tmpDir/mnt"; mkdir($mnt, 0755); # Temporary module area. $tmpModule = "$tmpDir/module"; print "Copying contents of $srcIso...\n"; if ($iso) { system "mdconfig -a -t vnode -f $srcIso -u 0"; system "mount -t cd9660 -o ro /dev/md0c $mnt"; } else { $imageBin = "$tmpDir/image.bin"; system "gzip -c -d $srcIso > $imageBin"; system "mdconfig -a -t vnode -f $imageBin -u 0"; system "mount /dev/md0c $mnt"; } $imageCopy = "$tmpDir/imageCopy"; mkdir($imageCopy, 0755); system "cd $mnt; find . -print | cpio -pdmu --quiet $imageCopy"; $mfsroot = "$imageCopy/mfsroot"; if (!$iso) { # Determine free space on original image. unlink "$mnt/mfsroot.gz"; open(IN, "df -k $mnt|"); $_ = ; $_ = ; ($dum, $dum, $dum, $freeImageSpace) = split; close(IN); } unmount(); print "Extracting root file system...\n"; system "gzip -d $mfsroot.gz"; mountmfs(); if ($mfsrootSize) { print "Creating new root file system...\n"; system "cd $mnt; tar cf ../mfsroot.tar ."; unmount(); system "dd if=/dev/zero of=$mfsroot bs=1k count=$mfsrootSize"; system "mdconfig -a -t vnode -f $mfsroot -u 0"; system "disklabel -rw md0 auto"; system "newfs -b 8192 -f 1024 /dev/md0c"; system "mount /dev/md0c $mnt"; system "cd $mnt; tar xpf ../mfsroot.tar"; } # Grab version. open(IN, "$mnt/etc/version"); $m0n0Version = ; chop $m0n0Version; $m0n0Version .= " (with modules"; close(IN); # Install modules. foreach $module (@ARGV) { $module =~ s|/$||; $moduleName = basename($module); $moduleName =~ s/(\.tar)|(\.tgz)|(\.tar\.gz)//; print "Installing module $module...\n"; $m0n0Version .= " $moduleName,"; mkdir($tmpModule, 0755); if (-d $module) { system "cd $module && find . -print | cpio -pdm --quiet $tmpModule"; } else { $module = abs_path(dirname($module)) . "/" . basename($module); $kind = `file -bz $module`; if ($kind =~ m/gzip/) { $z = "z"; } else { $z = ""; } system "cd $tmpModule && tar xpf$z $module"; } $installRoot = "$tmpModule/install-root"; if (-d $installRoot) { $pre = "$tmpModule/pre-install"; system "$pre $mnt" if (-x $pre) ; $post = "$tmpModule/post-install"; system "$post $mnt" if (-x $post); $copyRoot = $installRoot; } else { $copyRoot = $tmpModule; } $errors = `cd $copyRoot && find . -print | cpio -pdmu --quiet $mnt 2>&1`; if (length($errors)) { chomp $errors; print "Error installing module $module: '$errors'\n"; if ($errors =~ m/no space/i) { print "Try the '-m size' option with size > ", ($mfsrootSize ? $mfsrootSize : "10k"), "\n"; } unmount(); print "Cleaning up...\n"; system "rm -r '$tmpDir'"; print "Module installation failed.\n"; exit 1; } system "rm -fr $tmpModule"; } # Write version. chop $m0n0Version; open(OUT, ">$mnt/etc/version"); print OUT $m0n0Version, ")\n"; close(OUT); # Update version.buildtime. open(IN, "$mnt/etc/version.buildtime"); $buildTime = ; close(IN); chomp $buildTime; open(OUT, ">$mnt/etc/version.buildtime"); print OUT $buildTime, " (modules added at ", scalar(localtime), ")\n"; close(OUT); print "Packaging $tgtIso...\n"; unmount(); system "gzip -9 $mfsroot"; if ($iso) { system "mkisofs -l -r -L -o $tgtIso -b boot/cdboot -no-emul-boot $imageCopy"; } else { $mfsrootSize = (-s "$mfsroot.gz") / 1024; if ($mfsrootSize > $freeImageSpace) { # 10% extra for fs overhead, rounded up to 128k + 128k free. $extra = 128 * (int(($mfsrootSize - $freeImageSpace) * 1.1 / 128) + 2); } else { $extra = 0; } $newImageSize = (-s $imageBin) / 1024 + $extra; $newImageSizeBlocks = 2 * $newImageSize; print "Recreating image with size ${newImageSize}k\n"; # Create disklabel by modifying original. $label = "$tmpDir/label.proto"; open(IN, "disklabel -f $imageBin|"); open(OUT, ">$label"); while () { if (m|^\s*sectors/unit:\s*(\d+)|) { $labelOrigSize = $1; s/\d+/$newImageSizeBlocks/; } if (defined($labelOrigSize) && m/^\s*[a-h]:\s*$labelOrigSize/) { s/$labelOrigSize/$newImageSizeBlocks/; } print OUT $_; } close(IN); close(OUT); # Create the file system. system "dd if=/dev/zero of=$imageBin bs=1k count=$newImageSize"; system "mdconfig -a -t vnode -f $imageBin -u 0"; system "disklabel -BR -b $imageCopy/boot/boot1 -s $imageCopy/boot/boot2 md0 $label"; system "newfs -b 8192 -f 1024 /dev/md0c"; system "mount /dev/md0c $mnt"; system "cd $imageCopy; find . -print | cpio -pdmu --quiet $mnt"; unmount(); system "gzip -c -9 $imageBin > $tgtIso"; } print "Cleaning up...\n"; system "rm -r '$tmpDir'"; print "Created new image '$tgtIso'\n";