#!/bin/sh # turnup # See the help block at the end for documentation. # . /etc/default/functions # # configuration # The following variables control which directories in /var end # up on the rootfs and which end up in a temporary file system. INRAM_MEMSTICK="\ ### SlugOS from-memory-stick boot. d root root 0755 /var/backups none d root root 0755 /var/volatile/cache none l root root 0755 /var/cache /var/volatile/cache d root root 0755 /var/lib none d root root 2755 /var/local none d root root 1777 /var/volatile/lock none l root root 1777 /var/lock /var/volatile/lock d root root 0755 /var/volatile/log none l root root 0755 /var/log /var/volatile/log d root root 0755 /var/volatile/run none l root root 0755 /var/run /var/volatile/run d root root 0755 /var/spool none d root root 1777 /var/volatile/tmp none l root root 1777 /var/tmp /var/volatile/tmp d root root 0755 /var/lock/subsys none d root root 0755 /var/lib/dropbear none d root root 0755 /var/lib/misc none f root root 0664 /var/log/wtmp none f root root 0664 /var/log/lastlog none f root root 0664 /var/run/utmp none" INRAM_NFS="\ ### SlugOS from-NFS boot. d root root 0755 /var/backups none d root root 0755 /var/volatile/cache none l root root 0755 /var/cache /var/volatile/cache d root root 0755 /var/lib none d root root 2755 /var/local none d root root 1777 /var/volatile/lock none l root root 1777 /var/lock /var/volatile/lock d root root 0755 /var/log none d root root 0755 /var/volatile/run none l root root 0755 /var/run /var/volatile/run d root root 0755 /var/spool none d root root 1777 /var/volatile/tmp none l root root 1777 /var/tmp /var/volatile/tmp d root root 0755 /var/lock/subsys none d root root 0755 /var/lib/dropbear none d root root 0755 /var/lib/misc none d root root 0755 /var/lib/opkg none f root root 0664 /var/log/wtmp none f root root 0664 /var/log/lastlog none f root root 0664 /var/run/utmp none" INRAM_DISK="\ ### SlugOS from-disk boot. d root root 0755 /var/backups none d root root 0755 /var/cache none d root root 0755 /var/lib none d root root 2755 /var/local none d root root 1777 /var/lock none d root root 0755 /var/log none d root root 0755 /var/run none d root root 0755 /var/spool none d root root 1777 /var/tmp none d root root 0755 /var/lock/subsys none d root root 0755 /var/lib/dropbear none d root root 0755 /var/lib/misc none d root root 0755 /var/lib/opkg none f root root 0664 /var/log/wtmp none f root root 0664 /var/log/lastlog none f root root 0664 /var/run/utmp none" INRAM_HEADER="\ # This configuration file lists filesystem objects that should get verified # during startup and be created if missing. # # Every line must either be a comment starting with # # or a definition of format: # # where the items are separated by whitespace ! # # : d|f|l : (d)irectory|(f)ile|(l)ink # # A linking example: # l root root 0777 /var/test /tmp/testfile # f root root 0644 /var/test none # # Understanding links: # When populate-volatile is to verify/create a directory or file, it will first # check it's existence. If a link is found to exist in the place of the target, # the path of the target is replaced with the target the link points to. # Thus, if a link is in the place to be verified, the object will be created # in the place the link points to instead. # This explains the order of \"link before object\" as in the example above, where # a link will be created at /var/test pointing to /tmp/testfile and due to this # link the file defined as /var/test will actually be created as /tmp/testfile. #" # # force: override certain checks force= # # pfile: the uuid/partition file pfile=/etc/uuid_by_partition # # fstype new # The type of the file system mounted on "new" Outputs the last # piece of information found, which should be the one for the # currently visible mount! fstype() { local cwd dev mp type options pass freq result cwd="$(cd "$1"; /bin/pwd)" result= while read dev mp type options pass freq do case "$mp" in "$cwd") result="$type";; esac done {mount options} # mount the flash device, writeable, on the given directory get_flash() { local ffsdir ffspart ffsdev ffsdir="$1" shift test -n "$ffsdir" -a -d "$ffsdir" || { echo "$0: $ffsdir: internal error, flash mount point not a directory" >&2 return 1 } case "$(machine)" in nslu2) ffspart="Flashdisk";; *) ffspart="filesystem";; esac ffsdev="$(mtblockdev $ffspart)" [ -n "$ffsdev" ] || \ ffsdev="$(mtblockdev rootfs)" umountflash "$ffsdev" && mountflash "$ffsdev" "$ffsdir" "$@" } # # check_rootfs [-i] # Make sure the candidate rootfs is empty # Environment: rootdev=device or NFS root path check_rootfs() { local fcount case "$1" in -i) shift case "$force" in -f) return 0;; esac fcount="$(find "$1" ! -type d -print | wc -l)" test "$fcount" -eq 0 && return 0 echo "turnup: $rootdev: partition contains existing files, specify -f to overwrite" >&2 return 1;; *) checkmount "$1" && return 0 echo "turnup: $rootdev: partition does not seem to be a valid root partition" >&2 echo " The partition must contain a full operating system. To ensure that" >&2 echo " this is the case it is checked for the following, all of which must" >&2 echo " exist for the bootstrap to work:" >&2 echo echo " 1) A directory /mnt." >&2 echo " 2) A command line interpreter program in /bin/sh." >&2 echo " 3) The program chroot in /sbin or /usr/sbin." >&2 echo " 4) The program init in /sbin, /etc or /bin." >&2 echo echo " One or more of these items is missing. Mount $rootdev on /mnt" >&2 echo " and examine its contents. You can use turnup disk|nfs -i -f" >&2 echo " to copy this operating system onto the disk, but it may overwrite" >&2 echo " files on the disk." >&2 return 1;; esac } # # copy_rootfs old new # Make a copy of the given root file system, copying only the # directories needed. The root must be the flash file system copy_rootfs() { local old new old="$1" new="$2" test -d "$old" -a -d "$new" || { echo "turnup: rootfs: copy $old $new: not a directory" >&2 return 1 } # # There are no problem file names in the flash file system, so # it is possible to use -print, not -print0. The following # files and directories are not copied: # # /dev/* # /boot, /boot/* # /linuxrc* # /var/* echo "turnup: copying root file system" >&2 ( cd "$1" find . -xdev -print | sed '\@^./dev/@d;\@^./boot/@d;\@^./boot$@d;\@^./linuxrc@d;\@^./var/@d' | cpio -p -d -m -u "$2" ) || { echo "turnup: rootfs: cpio $old $new failed" >&2 return 1 } echo "done" >&2 } # # setup_dev new device_table # In flash file systems /dev is in ramfs, in disk systems /dev # can be populated permanently. This is done by creating a # single entry '.noram' in /dev - the devices init script will # then populate the directory without overmounting it. The # devices in the passed in device table are also created, but # note that this is insufficient, /etc/init.d/devices must # also run. setup_dev() { test -n "$1" -a -d "$1"/dev -a -r "$2" || { echo "turnup: setup_dev($1,$2): expected a directory and a file" >&2 return 1 } echo "turnup: initialising dev file system" >&2 # init tries to open the following devices: # /dev/console # /dev/tty0 # /dev/null # syslog, and maybe other things, only work if fd 1 is valid, therefore # we must create these devices here... # (if makedevs is a symlink, it's the busybox version, different syntax) if [ -h /sbin/makedevs ] then makedevs -d "$2" "$1" else makedevs --root="$1" --devtable="$2" fi :>"$1"/dev/.noram return 0 } # # setup_bootdev new device_table # As above but actually uses the supplied device table - this is possible if # the table is just used for boot because the extra setup is not required. setup_bootdev() { test -n "$1" -a -d "$1"/dev -a -r "$2" || { echo "turnup: setup_bootdev($1,$2): expected a directory and a file" >&2 return 1 } # NOTE: this fails silently with 0 return code(!) when a directory # does not exist yet things are created within it. # (if makedevs is a symlink, it's the busybox version, different syntax) if [ -h /sbin/makedevs ] then makedevs -d "$2" "$1" else makedevs -r "$1" -D "$2" fi } # # setup_var new type # Populates /var. # Removes the /var tmpfs entry from /etc/fstab. # Creates links from /var into /media/ram for NFS and Memstick. setup_var() { test -n "$1" -a -d "$1"/var || { echo "turnup: setup_var($1,$2): expected a directory" >&2 return 1 } case "$2" in disk|nfs|memstick);; *) echo "turnup: setup_var($1,$2): expected 'disk', 'nfs' or 'memstick'" >&2 return 1;; esac # populate /var. We just need to create the /var/volatile mount # point, the populate-volatile script does the work at boot time. #QUIET: echo "turnup: ensuring /var/volatile mountpoint exists" >&2 test -d "$1"/var/volatile || mkdir "$1"/var/volatile # we need to put in place the correct configuration file for # the populate-volatile script to use at boot time. The config # file is already in place for the flash boot, and it's the same # file for the ram boot. case "$2" in disk) echo "$INRAM_HEADER" > "$1"/etc/default/volatiles/00_core echo "$INRAM_DISK" >>"$1"/etc/default/volatiles/00_core;; nfs) echo "$INRAM_HEADER" > "$1"/etc/default/volatiles/00_core echo "$INRAM_NFS" >>"$1"/etc/default/volatiles/00_core;; memstick) echo "$INRAM_HEADER" > "$1"/etc/default/volatiles/00_core echo "$INRAM_MEMSTICK" >>"$1"/etc/default/volatiles/00_core;; esac # remove the /var tmpfs entry from the new /etc/fstab, if it is # present in the first place. sed -i '\@[ ]/var[ ][ ]*tmpfs[ ]@d' "$1"/etc/fstab #QUIET: echo "turnup: ensuring tmpfs will not be mounted on /var" >&2 # return 0 } # # setup_syslog new # Moves the syslog to a file - appropriate for disk and nfs types, not # otherwise. setup_syslog() { test -n "$1" -a -d "$1"/etc || { echo "turnup: setup_syslog($1): expected a directory" >&2 return 1 } # # if the syslog is to the buffer redirect it to a file if egrep -q '^DESTINATION="buffer"' "$1"/etc/syslog.conf then if cp "$1"/etc/syslog.conf "$1"/etc/syslog.conf.sav then # the busybox syslog will fail with ROTATESIZE and ROTATEGENS sed -i 's!DESTINATION="buffer"!DESTINATION="file"! /^ROTATESIZE=/d /^ROTATEGENS=/d' "$1"/etc/syslog.conf #QUIET: echo "turnup: /etc/syslog.conf: changed to file buffering" >&2 #QUIET: echo " Old (buffer) version in /etc/syslog.conf.sav" >&2 #QUIET: echo " Log messages will be in /var/log/messages" >&2 else echo "turnup: /etc/syslog.conf: failed to make a copy" >&2 echo " syslog will log to a buffer" >&2 fi fi return 0 } # # setup_rootfs type new device_table # Populates the /dev and /var directories, alters the startup to # not mount or populate them further. Does the right thing according # to the given $type setup_rootfs() { local type new table type="$1" new="$2" table="$3" test -n "$new" -a -d "$new" -a -f "$table" || { echo "turnup: setup_rootfs($type,$new,$table): expected a directory and a file" >&2 return 1 } case "$type" in flash) return 0;; disk) setup_dev "$new" "$table" && setup_var "$new" "$type" && setup_syslog "$new";; memstick) setup_bootdev "$new" "$table" && setup_var "$new" "$type" ;; nfs) setup_dev "$new" "$table" && setup_var "$new" "$type" && setup_syslog "$new";; *) echo "turnup: setup_rootfs: $type: unknown rootfs type" >&2 return 1;; esac # return code of last setup function } # # setup_fstab new fsdev fstype fsoptions # Alters the /etc/fstab entry for / to refer to the correct device and # have the correct type and options. Essential for checkroot to remount # / with the correct options. Writes the initial uuid file. # bad, since sed won't fail even if it changes nothing. setup_fstab() { sed -i '\@^[^ ]*[ ][ ]*/[ ]@s@^.*$@'"$2 / $3 $4 1 1"'@' "$1"/etc/fstab egrep -q "^$2 / $3 $4 1 1\$" "$1"/etc/fstab || { echo "turnup: /etc/fstab: root(/) entry not changed" >&2 echo " you probably need to check the options in /etc/fstab" >&2 echo " to ensure that the root partition is mounted correctly" >&2 return 1 } # # build $pfile uuid_by_partition >"$1""$pfile" || echo "turnup: $pfile: blkid failed (ignored)" >&2 return 0 } # # boot_rootfs ( |) [options] # Change the flash partition (not the current root!) to boot off # the new root file system boot_rootfs() { local type ffs sleep device uuid opt type="$1" ffs="$2" sleep="$3" device="$4" uuid= mduuid= # test this first as the test does not depend on the correctness # of the other arguments test -n "$ffs" -a -d "$ffs" || { echo "turnup: boot_rootfs($type, $ffs, $device): expected directory" >&2 return 1 } test -x "$ffs"/boot/"$type" || { echo "turnup: boot_rootfs($type, $ffs, $device): invalid boot type $type" >&2 return 1 } shift shift case "$type" in disk) test -n "$device" -a -b "$device" || { echo "turnup: boot_rootfs($ffs, $type, $device): expected block device" >&2 return 1 } uuid="$3" if (echo "$device" | grep -q '^/dev/md') then # FIXME: should use awk to do the below extraction mduuid=`mdadm --detail --brief "$device" | sed 's/^.*UUID=//'` fi shift 3;; nfs) shift 2;; flash) ;; ram) ;; *) echo "turnup: boot_rootfs($type, $ffs, $device): unknown type" >&2 return 1;; esac # # The /linuxrc records the correct options to mount the device, # since we have already mounted if correctly with these options # we can be sure (maybe) that the boot will work. If not /boot/disk # falls back to flash. # # This modifies the boot process, until this point no harm has been # done to the system, but at this point the boot rootfs will change rm -f "$ffs"/linuxrc.new || { echo "turnup: boot_rootfs: failed to remove $ffs/linuxrc.new" >&2 return 1 } case "$type" in flash) ln -s "boot/flash" "$ffs"/linuxrc.new || { echo "turnup: boot_rootfs: failed to create $ffs/linuxrc.new" >&2 return 1 };; ram) { echo '#!/bin/sh' echo 'mount -t proc proc /proc' echo 'mount -t sysfs sysfs /sys' echo 'leds beep' echo 'rm -f /linuxrc.new' echo 'ln -s boot/flash /linuxrc.new' echo 'mv /linuxrc.new /linuxrc' echo 'exec /boot/ram /dev/ram0' echo 'exec /boot/flash' } >"$ffs"/linuxrc.new && chmod 744 "$ffs"/linuxrc.new || { echo "turnup: boot_rootfs: failed to write $ffs/linuxrc.new" >&2 return 1 };; *) { echo '#!/bin/sh' echo 'mount -t proc proc /proc' echo 'mount -t sysfs sysfs /sys' echo 'leds beep' test "$sleep" -gt 0 && echo -n "sleep='$sleep' " test -n "$uuid" && echo -n "UUID='$uuid' " test -n "$mduuid" && echo -n "MDUUID='$mduuid' " echo -n "exec '/boot/$type' '$device'" for opt in "$@" do echo -n " '$opt'" done echo echo 'exec /boot/flash' } >"$ffs"/linuxrc.new && chmod 744 "$ffs"/linuxrc.new || { echo "turnup: boot_rootfs: failed to write $ffs/linuxrc.new" >&2 return 1 };; esac rm -f "$ffs"/linuxrc.sav || { echo "turnup: boot_rootfs: failed to remove $ffs/linuxrc.sav" >&2 return 1 } ln "$ffs"/linuxrc "$ffs"/linuxrc.sav || { echo "turnup: boot_rootfs: failed to save /linuxrc.sav" >&2 return 1 } mv -f "$ffs"/linuxrc.new "$ffs"/linuxrc || { echo "turnup: boot_rootfs: failed to install new /linuxrc" >&2 return 1 } return 0 } # # disk [-m] [-i] [-s