#!/bin/ksh # # Copyright (c) 2002-2007 Chris Cappuccio. Pretend that # you see BSD license blurb, clause 1, 2 and warranty disclaimer here. # # This quick and dirty script will setup a flash media with an OpenBSD/i386 # distribution specified by . This script makes many assumptions, # but should work just dandy with most any OpenBSD/i386 system. I've used # it with USB CompactFlash, IDE CF, and hard disks. It probably works # with just about anything. # # This script can be used on OpenBSD/i386 and OpenBSD/amd64. To use it # on other platforms, you can modify or completely remove the fdisk # portion. # # To use this script, you can either: # 1. Figure out disk(flash) geometry on the host system # 2. Input it into the flashdist script during runtime and it will be used as # system wide configuration information # # or: # 1. Use flashdist with the -d flag # 2. Flashdist will completely ignore anything but the kernel's idea of # disk geometry # 3. This should work every time, unless your disk/host adapter driver reports # strange geometry values # 4. I haven't tested it much so I still left manual C/H/S specification as # the default mode # # or: # 1. Hard code all of the disklabel geometry values into the script below # 2. Flashdist will use your specified geometry (This is mostly useless today # as the system uses an LBA boot loader now. However, it may be useful # if you are trying to boot a device that doesn't use 512 bytes per # sector or some other abnormality.) # # If the -d mode does not create a bootable flash, and if you need more control # than the C/H/S mode provides, fill in the values here: # # Hardcode example for SanDisk SDCFB-64 # Value Disklabel name # #totalsize=125440 # "total sectors:" [optional] #bytessec=512 # "bytes/sector:" [optional] #sectorstrack=32 # "sectors/track:" (S) [required] #sectorscylinder=256 # "sectors/cylinder:" [optional] #trackscylinder=8 # "tracks/cylinder:" (H) [required] #cylinders=490 # "cylinders:" (C) [required] # # At your option, you can specify the 3 C/H/S values, and let this script # auto-calculate the other three values. Leave the other values commented # out for this to happen. bytes/sector defaults to 512, you need to # fill that in as well if you are writing to a non-512 media (this default # is very sensible, as non-512 bytes/sec media is still rare) # # System configuration variables you can set in advance: # # encpass (encrypted root password) # password (non-encrypted root password, overridden by encpass) # syslogserver (IP address to send logging data. If set to "none", syslog # will log to the ramdisk.) # etccopyfiles (files to copy over to destination's /etc directory. If set # to "none", no files will be copied. Flashdist default is # listed below.) # bincopyfiles (files to copy over to destination's /bin directory. If set # to "none", no files will be copied. Flashdist default is # listed below.) # # (commented variables are determined interactively while script runs, # uncommented variables are determined right here and now) # #encpass='$2a$06$2p5eSJnVp8P5MEQsjlAx2ecpyU5FyQvDlE7BCebRGt4HWRFJP/djq' #syslogserver="35.42.1.42" #dnsservers="66.39.177.2 66.39.191.18" #ntpservers="pool.ntp.org" # Soekris -- boot.conf sets up console on serial etccopyfiles="rc rc.local rc.conf rc.conf.local ttys fstab syslog.conf inetd.conf newsyslog.conf tabs/root sudoers localtime profile" # Production builds: send the console to the serial port etccopyfiles="$etccopyfiles boot.conf" # VMware -- hostname.vic0 initializes the VMware NIC #etccopyfiles="$etccopyfiles hostname.vic0" bincopyfiles="ro rw" # nshdir="nsh" # where to find nsh (if at all) savesh="save-ro.sh" # which save script to use with nsh # # Command line flags that you can fill in advance: # #device="" # sd0, svnd0, wd0, etc. #kernel="" # /usr/src/sys/arch/i386/compile/GENERIC/bsd # # Let the beatings begin! umask 022 vers="$Revision$" PATH=/usr/bin:/usr/sbin:/sbin:/bin usage() { cat <<-EOF Usage: flashdist.sh [-d] [-g] [-x] [-k] -d >> initialize the media with values derived from a hardware probe of the disk geometry -x >> do not prompt user for OK before overwriting media disk device name >> the base name of your flash media, such as sd0 kernel >> kernel build to install on your flash For example: GEODE would copy the kernel from GEODE-build/bsd example: flashdist.sh sd0 GEODE EOF exit 1 } parse_geometry() { # The argument is expected to be of the form C:H:S G="$1" echo "$G" | egrep -s '^[0-9]+:[0-9]+:[0-9]+$' if [ $? -ne 0 ]; then echo "Geometry <$G> not well-formed!" usage fi cylinders="${G%%:*}" G="${G#$cylinders:}" trackscylinder="${G%:*}" sectorstrack="${G#*:}" } echo flashdist.sh $vers ben@alkaloid.net libspace=0 distspace=0 kernspace=0 args=`getopt dgxkP:G:L: "$@"` if [ $? -ne 0 ]; then usage fi unset dflag xflag set -- $args "$@" while [ $# -ge 0 ]; do case "$1" in -d) dflag="-d"; shift ;; -x) xflag="-x"; shift ;; -P) encpass="$2"; shift; shift ;; -L) syslogserver="$2"; shift; shift ;; -G) parse_geometry "$2"; shift; shift ;; --) shift; break ;; esac done if [ ! -z "$dflag" -a ! -z "$cylinders" ]; then echo You cannot specify geometry manually and specify the -d flag exit 1 fi if [ -z "$1" -a -z "$device" ]; then usage fi if [ -z "$device" ]; then device=$1 shift fi disk=/dev/"$device"c if [ ! -b $disk ]; then echo $device is an invalid disk device, $disk not found or not block device exit 1 else echo Using disk device: $device fi if [ -z "$1" -a -z "$kernel" ]; then usage fi if [ -z "$kernel" ]; then kernel=${1}-build/bsd shift fi if [ ! -f "$kernel" ]; then echo $kernel is an invalid kernel, file not found exit 1 else echo Copying kernel from: $kernel fi islezero() { if [ "$1" -le "0" ]; then echo -n expected a value larger than 0, got \"$1\" if [ ! -z "$2" ]; then echo " for $2" else echo fi exit 1 fi } echo if [ -z "$dflag" -a -z "$cylinders" ]; then echo You did not specify -d and you did not specify a manual geometry. echo Please enter Cylinders/Heads/SectorsPerTrack. echo read cylinders?"Cylinders: " read trackscylinder?"Tracks Per Cylinder(Heads): " read sectorstrack?"Sectors Per Track: " fi if [ -z "$dflag" ]; then islezero "$cylinders" cylinders islezero "$trackscylinder" trackscylinder islezero "$sectorstrack" sectorscylinder if [ -z "$bytessec" ]; then bytessec=512 fi if [ -z "$totalsize" ]; then totalsize=$((cylinders * trackscylinder * sectorstrack)) fi if [ -z "$sectorscylinder" ]; then sectorscylinder=$((totalsize / cylinders)) fi fi cat <<-EOF Please pay attention to any error messages that you may receive from the commands this script is using. If you end up having problems, they could explain why. WARNING: This will erase ALL DATA on the $device disk device! EOF if [ -z "$xflag" ]; then read whatever?'Press enter key to continue or Control-C to abort...' fi echo TA=`mktemp /tmp/flashdist.XXXXXXXXX` TB=`mktemp /tmp/flashdist.XXXXXXXXX` echo Updating MBR and partition table... # MODIFY OR REMOVE FDISK CODE FOR NON-i386/amd64 PLATFORMS if [ ! -z "$dflag" ]; then fdisk -f /usr/mdec/mbr -e $device <<-__EOE >/dev/null reinit update write quit __EOE else fdisk -c $cylinders -h $trackscylinder -s $sectorstrack \ -f /usr/mdec/mbr -e $device <<-__EOC >/dev/null reinit update write quit __EOC fi echo 'Note, you may ignore "sysctl(machdep.bios.diskinfo)" errors if present.' echo echo Setting up disklabel... # Use -d default geometry flag (if specified to flashdist) if ! disklabel $dflag $device > $TA; then echo "Disklabel failed: disklabel $dflag $device > $TA" echo Please fix something. Aborting... echo exit 1 fi echo if [ ! -z "$dflag" ]; then # Use default kernel geometry totalsize=`awk -F: ' /^total sectors:/ {print $2} ' $TA` bytessec=`awk -F: ' /^bytes\/sector:/ {print $2} ' $TA` sectorstrack=`awk -F: ' /^sectors\/track:/ {print $2} ' $TA` sectorscylinder=`awk -F: ' /^sectors\/cylinder:/ {print $2} ' $TA` trackscylinder=`awk -F: ' /^tracks\/cylinder:/ {print $2} ' $TA` cylinders=`awk -F: ' /^cylinders:/ {print $2} ' $TA` else # Use our idea of disk geometry egrep -v "^total sectors:|^bytes/sector:|^sectors/track:|^sectors/cylinder:|^tracks/cylinder:|^cylinders:|^ .:|^#|^. partitions:|^.. partitions:|^$" < $TA > $TB cat >> $TB <<-EOF total sectors: $totalsize bytes/sector: $bytessec sectors/track: $sectorstrack sectors/cylinder: $sectorscylinder tracks/cylinder: $trackscylinder cylinders: $cylinders EOF cat $TB > $TA fi # So, installboot assumes that type: vnd is the same as type: floppy # (in other words, the primary use of vnds is to create floppy images) # and clobbers the MBR if you installboot to a vnd image. Luckily, # it's easy to prevent that from happening. # # Here we just anticipate ESDI through the disklabel which causes # installboot to lseek() past the MBR. This type is irrelevant, # just think of it as "disk" # # Also strip out disklabel noise and existing partitions T=`mktemp /tmp/flashdist.XXXXXXXXX` egrep -v "^type:|^#|^16 partitions:|^$|^ .:" $TA > $T echo "type: ESDI" >> $T # Now we can reuse $TA and $TB temp file names cp /dev/null $TA cp /dev/null $TB # The division is done like this to keep any 32 int from overflowing in ksh totalmbytes=$((((totalsize / 1024) * bytessec) / 1024)) cat <<-EOF The install script is using the following parameters: Total size of media: $totalsize sectors ($totalmbytes MB) Bytes/Sector: $bytessec Sectors/Track: $sectorstrack Sectors/Cylinder: $sectorscylinder Tracks/Cylinder (heads): $trackscylinder Cylinders: $cylinders EOF if [ -z "$xflag" ]; then read whatever?'Press enter key to continue or Control-C to abort...' echo fi echo Checking distribution list... # Evil, but allows ldd to work if your snapshot is different than what # you are running. # #ldconfig -m $distloc/usr/lib $distloc/usr/local/lib mandir=`mktemp -d /tmp/flashdist.XXXXXXXX` manpages=`mktemp /tmp/flashdist.XXXXXXXX` #if [ "`ls -di /usr/lib`" != "`ls -di $distloc/usr/lib`" ]; then # # If the inode numbers are not the same, they are definitely not the same # # directory (e.g. argument 4 was not /) so it's safe to unconfigure them now. # ldconfig -U $distloc/usr/lib $distloc/usr/local/lib #fi rm $TB echo asize=$((totalsize - sectorstrack)) cat >>$T <<-EOF a: $asize $sectorstrack 4.2BSD 1024 8192 16 c: $totalsize 0 unused 0 0 EOF echo Installing disklabel... disklabel -R $device $T echo echo Creating new filesystem... newfs -S $bytessec -q /dev/r"$device"a echo TT=`mktemp -d /tmp/flashdist.XXXXXXXXX` echo Mounting destination to $TT... if ! mount -o async /dev/"$device"a $TT; then echo Mount failed: mount -o async /dev/"$device"a $TT echo Please fix something. Aborting... echo exit 1 fi echo Checking free space on $device... # Calculate disk space diskspace=`df -k $TT | tail -n 1 | awk '{ print $2 }'` # Compress the kernel and calculate its space GZKERN=`mktemp /tmp/flashdist.XXXXXXXXX` gzip -9c $kernel >$GZKERN kernspace=`du -k $GZKERN | awk '{ print $1 }'` echo -n "Copying OpenBSD distribution to media: " for x in base etc man misc; do echo -n "$x " tar zxpf OBSD/${x}??.tgz -C $TT done echo "done." echo "Installing additional packages" cp -r packages $TT chroot $TT pkg_add packages/* rm -rf $TT/packages echo echo Copying bsd kernel, boot blocks, /etc/localtime... cp $TT/usr/mdec/boot $TT/boot cp /etc/localtime $TT/etc/localtime mv $GZKERN $TT/bsd mkdir $TT/etc/tabs echo Installing boot blocks and MBR... /usr/mdec/installboot $TT/boot $TT/usr/mdec/biosboot $device /sbin/fdisk -u -y -f $TT/usr/mdec/mbr $device echo -n Running MAKEDEV... cdir=`pwd` TempDev=`mktemp -d /tmp/flashdist.XXXXXXXXX` chmod 755 $TempDev $TT/dev cp $TT/dev/MAKEDEV $TempDev/ cd $TT/dev sh $TT/dev/MAKEDEV std # cd $TempDev # # Create a /dev on the flash with the bare minimum to boot the system # and a copy of MAKEDEV for rescue operations. Create real device tree # in an archive which gets unpacked into an mfs /dev on boot. # # Try and avoid totally unnecessary use of inodes on limited space mfs by # omitting unnecessary devices. (mount_mfs for /dev in flashdist rc gives us # a maximum of 512 inodes.) # sh ./MAKEDEV tun0 tun1 tun2 tun3 bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 bpf8 bpf9 sh ./MAKEDEV fd1 fd0 random crypto pf pctr systrace sd0 sd1 wd0 wd1 fd sh ./MAKEDEV tty00 tty01 tty02 tty03 ttyc0 ttyc1 ttyc2 ttyc3 apm std sh ./MAKEDEV ttyC0 ttyC1 ttyC2 ttyC3 ttyC4 ttyC5 ttyC6 sh ./MAKEDEV gpio0 gpio1 gpio2 ptm wsmouse wskbd pci pty0 # tar cf $TT/dev.tar . echo done cd $cdir rm -r $TempDev # run syslogd -p /var/run/log from /etc/rc ln -s /var/run/log $TT/dev/log echo Setting up directories and links... # /var is really r-w /tmp mv $TT/var $TT/var.persist ln -s /tmp/var $TT/var getpass() { stty -echo read pw1?"Password:" echo read pw2?"Verify:" echo stty echo if [ "$pw1" == "$pw2" ]; then password="$pw1" fi if [ -z "$password" ]; then echo "Passwords don't match or password was empty. Try again." getpass fi } if [ -z "$encpass" ]; then if [ -z "$password" ]; then echo echo Please assign a root password... getpass echo fi fi if [ -z "$encpass" ]; then encpass=`echo $password | /usr/bin/encrypt -b 6` fi echo -n "root:$encpass" > $TA awk -F : '/^root/ \ { printf(":%s:%s:%s:%s:%s:%s:%s:%s\n", $3, $4, $5, $6, $7, $8, $9, $10) }' \ < $TT/etc/master.passwd >> $TA egrep -v "^root:" < $TT/etc/master.passwd >> $TA cat < $TA > $TT/etc/master.passwd pwd_mkdb -p -d $TT/etc $TT/etc/master.passwd if [ -z "$etccopyfiles" ]; then cat <<-__GANJA Please enter any file name(s), separated by spaces, that you wish to copy over to the install media's /etc directory. Files are assumed to be in the current working directory unless a full path is specified. (Press enter for none) __GANJA read etccopyfiles?'Files to copy: ' fi if [ ! -z "$etccopyfiles" -a "$etccopyfiles" != "none" ]; then echo -n "Copying configuration files to /etc..." for i in $etccopyfiles; do if [ -f etc/$i ]; then echo -n " $i" cp etc/$i $TT/etc/$i else echo echo WARNING: Unable to copy \"etc/$i\" to destination filesystem, must be done manually fi done echo fi echo "FIXME: More graceful handling of permissions on copied files is needed" chmod o-rwx $TT/etc/hostname.* chmod 440 $TT/etc/sudoers chown root:wheel $TT/etc/sudoers if [ ! -z "$bincopyfiles" -a "$bincopyfiles" != "none" ]; then echo -n "Copying scripts to /bin..." for i in $bincopyfiles; do if [ -f bin/$i ]; then echo -n " $i" cp bin/$i $TT/bin/$i else echo echo WARNING: Unable to copy \"bin/$i\" to destination filesystem, must be done manually fi done echo fi if [ -x "$nshdir/nsh" -a -x "$nshdir/$savesh" ]; then echo "Installing NSH ($savesh)..." cp $nshdir/nsh $TT/bin/nsh mkdir -p $TT/usr/local/bin cp $nshdir/$savesh $TT/usr/local/bin/save.sh if [ -x "$nshdir/nwrapper" ]; then cp $nshdir/nwrapper $TT/bin/nwrapper fi echo /bin/nsh >> $TT/etc/shells echo /bin/nwrapper >> $TT/etc/shells fi if [ -f $TT/etc/syslog.conf ]; then if [ -z "$syslogserver" ]; then cat <<-__BUTTER Please enter the hostname or IP address of the central log host which will receive udp syslog packets from this installation. (Press enter for none, and syslog will log to ramdisk) __BUTTER read syslogserver?"Loghost: " fi if [ -z "$syslogserver" -o "$syslogserver" == "none" ]; then sed -e "s/MESSAGES/\/var\/log\/messages/" < $TT/etc/syslog.conf > $TA sed -e "s/AUTHLOG/\/var\/log\/authlog/" < $TA > $TT/etc/syslog.conf else sed -e "s/MESSAGES/@$syslogserver/" < $TT/etc/syslog.conf > $TA sed -e "s/AUTHLOG/@$syslogserver/" < $TA > $TT/etc/syslog.conf if [ -f $TT/etc/newsyslog.conf ]; then egrep -v "var.log.messages|var.log.authlog" $TT/etc/newsyslog.conf > $TT/etc/newsyslog.conf.new mv $TT/etc/newsyslog.conf.new $TT/etc/newsyslog.conf fi fi fi if [ -f $TT/etc/ntpd.conf ]; then if [ ! -z "$ntpservers" ]; then echo Configuring ntpd.conf for servers $ntpservers egrep -v ^server $TT/etc/ntpd.conf > $TT/etc/ntpd.conf.new for i in $ntpservers; do echo servers $i >> $TT/etc/ntpd.conf.new done mv $TT/etc/ntpd.conf.new $TT/etc/ntpd.conf fi fi if [ -z "$dnsservers" ]; then dnsservers=`awk ' /^nameserver/ { print $2 } ' /etc/resolv.conf | tr '\n' ' '` echo Default DNS Servers: $dnsservers read ndnsservers?'DNS Servers: [press enter for default] ' if [ ! -z "$ndnsservers" ]; then dnsservers="$ndnsservers" fi fi if [ ! -z "$dnsservers" ]; then echo Configuring resolv.conf for DNS servers $dnsservers echo "lookup file bind" > $TT/etc/resolv.conf for i in $dnsservers; do echo nameserver $i >> $TT/etc/resolv.conf done else echo Failed to configure nameservers, you must manually configure /etc/resolv.conf fi # Configure more system values: interfaces, default route, etc ???? echo Writing flashdist version $vers for upgrade reference point echo $vers > $TT/etc/.flashdist_version mkdir -p $TT/usr/local/bin $TT/usr/local/lib echo echo Installation finished. echo -n Unmounting filesystem... umount $TT rmdir $TT rm $T $TA $TmpD echo done! exit 0