#!/bin/sh # Functions library :: for Linux Live Kit scripts # Author: Tomas M. # # ================================================================= # debug and output functions # ================================================================= debug_start() { if grep -q debug /proc/cmdline; then DEBUG_IS_ENABLED=1 else DEBUG_IS_ENABLED= fi } debug_log() { if [ "$DEBUG_IS_ENABLED" ]; then echo "- debug: $*" >&2 log "- debug: $*" fi } # header # $1 = text to show # header() { echo """$@""" } # echo green star # echo_green_star() { echo -ne """* """ } # log - store given text in /var/log/livedbg log() { echo "$@" 2>/dev/null >>/var/log/livedbg } echolog() { echo "$@" log "$@" } # show information about the debug shell show_debug_banner() { echo echo "=====" echo ": Debugging started. Here is the root shell for you." echo ": Type your desired commands or hit Ctrl+D to continue booting." echo } # debug_shell # executed when debug boot parameter is present # debug_shell() { if [ "$DEBUG_IS_ENABLED" ]; then show_debug_banner setsid sh -c 'exec sh < /dev/tty1 >/dev/tty1 2>&1' echo fi } fatal() { echolog header "Fatal error occured - $1" echolog "Something went wrong and we can't continue. This should never happen." echolog "Please reboot your computer with Ctrl+Alt+Delete ..." echolog setsid sh -c 'exec sh < /dev/tty1 >/dev/tty1 2>&1' } # test if the script is started by root user. If not, exit # allow_only_root() { if [ "0$UID" -ne 0 ]; then echo "Only root can run $(basename $0)"; exit 1 fi } # Create bundle # call mksquashfs with apropriate arguments # $1 = directory which will be compressed to squashfs bundle # $2 = output file # $3..$9 = optional arguments like -keep-as-directory or -b 123456789 # create_bundle() { debug_log "create_module" "$*" rm -f "$2" # overwrite, never append to existing file mksquashfs "$1" "$2" -comp xz -b 512K $3 $4 $5 $6 $7 $8 $9>/dev/null } # Move entire initramfs tree to ramfs mount. # It's a bit tricky but is necessray to enable pivot_root # even for initramfs boot image # transfer_initramfs() { if [ ! -r /lib/initramfs_escaped ]; then echo "switch root from initramfs to ramfs" SWITCH=/m # one letter directory mkdir -p $SWITCH mount -t ramfs ramfs $SWITCH cp -a /??* $SWITCH 2>/dev/null # only copy two-and-more-letter directories cd $SWITCH echo "This file indicates that we successfully escaped initramfs" > $SWITCH/lib/initramfs_escaped exec switch_root -c /dev/console . $0 fi } # mount virtual filesystems like proc etc # init_proc_sysfs() { debug_log "vfs_mount_init" mount -n -t proc proc /proc echo "0" >/proc/sys/kernel/printk mount -n -t sysfs sysfs /sys mount -n -o remount,rw rootfs / ln -sf /proc/mounts /etc/mtab } # make sure some devices are there init_devs() { echo /sbin/mdev > /proc/sys/kernel/hotplug mdev -s modprobe zram 2>/dev/null modprobe loop 2>/dev/null modprobe squashfs 2>/dev/null modprobe fuse 2>/dev/null } # Activate zram (auto-compression of RAM) # Compressed RAM consumes 1/2 or even 1/4 of original size # Setup static size of 500MB # init_zram() { debug_log "init_zram" echo_green_star echo "Setting dynamic RAM compression using ZRAM" echo 536870912 > /sys/block/zram0/disksize # 512MB mkswap /dev/zram0 >/dev/null swapon /dev/zram0 -p 32767 echo 100 > /proc/sys/vm/swappiness } # load the AUFS kernel module if needed # init_aufs() { debug_log "init_aufs" # TODO maybe check here if aufs support is working at all # and procude useful error message if user has no aufs modprobe aufs 2>/dev/null } # Setup empty union # $1 = memory directory (tmpfs will be mounted there) # $2 = union directory where to mount the union # init_union() { debug_log "init_union" echo_green_star echo "Setting up union using AUFS 3" mkdir -p "$1" mkdir -p "$2" mount -t aufs -o xino="$1/.xino",br="$1" none "$2" } # Return device mounted for given directory # $1 = directory # mounted_device() { debug_log "mounted_device" local MNT TARGET MNT="$1" while [ "$MNT" != "/" -a "$MNT" != "." -a "$MNT" != "" ]; do TARGET="$(grep -F " $MNT " /proc/mounts | cut -d " " -f 1)" if [ "$TARGET" != "" ]; then echo "$TARGET:$MNT" return fi MNT="$(dirname $MNT)" done } # Make sure to mount FAT12/16/32 using vfat # in order to support long filenames # $1 = device # device_bestfs() { debug_log "device_bestfs" local FS FS="$(blkid "$1" | sed -r "s/.*TYPE=//" | tr -d '"' | tr [A-Z] [a-z])" if [ "$FS" = "msdos" -o "$FS" = "fat" -o "$FS" = "vfat" ]; then FS="vfat" elif [ "$FS" = "ntfs" ]; then FS="ntfs-3g" fi echo "-t $FS" } # Find LIVEKIT data by mounting all devices # If found, keep mounted, else unmount # $1 = data directory target (mount here) # find_data_try() { debug_log "find_data_try" local DEVICE FS MNT mkdir -p "$1" blkid | sort | cut -d: -f 1 | grep -E -v "/loop|/ram|/zram" | while read DEVICE; do FS="$(device_bestfs "$DEVICE")" mount -r "$DEVICE" "$1" $FS 2>/dev/null if [ -d "$1/$LIVEKITNAME" ]; then mount -o remount,rw "$DEVICE" "$1" 2>/dev/null echo "$1/$LIVEKITNAME" return fi umount "$1" 2>/dev/null done } # Retry finding LIVEKIT data several times, # until timeouted or until data is found # $1 = timeout # $2 = data directory target (mount here) # find_data() { debug_log "find_data" local DATA echo_green_star >&2 echo -n "Looking for $LIVEKITNAME data .." >&2 for timeout in $(seq 1 $1); do echo -n "." >&2 DATA="$(find_data_try "$2")" if [ "$DATA" != "" ]; then echo "" >&2 echo "* Found in $(mounted_device "$DATA" | cut -d : -f 1)" >&2 echo "$DATA" return fi sleep 1 done echo "" >&2 if [ "$DATA" = "" ]; then fatal "$LIVEKITNAME data not found" fi } # Copy data to RAM if requested # $1 = live data directory # copy_to_ram() { local DM RAM if grep -vq toram /proc/cmdline; then echo "$1" return fi DM="$(mounted_device "$1" | cut -d : -f 2-)" RAM="$DM.ram" echo "* Copying $LIVEKITNAME data to RAM..." >&2 mkdir -p "$RAM" cp -a "$DM/$LIVEKITNAME" "$RAM" echo "$RAM/$LIVEKITNAME" umount "$DM" } # Mount squashfs filesystem bundles # and add them to union # $1 = directory where to search for bundles # $2 = directory where to mount bundles # $3 = directory where union is mounted # union_append_bundles() { debug_log "union_append_bundles" echo_green_star echo "Adding bundles to union" ls -1 "$1" | grep '.'$BEXT'$' | sort | while read BUNDLE; do echo "* $BUNDLE" mkdir -p "$2/$BUNDLE" mount -o loop -t squashfs "$1/$BUNDLE" "$2/$BUNDLE" mount -o remount,add:1:"$2/$BUNDLE" none "$3" done } # Create empty fstab properly # $1 = root directory # fstab_create() { local FSTAB FSTAB="$1/etc/fstab" echo aufs / aufs defaults 0 0 > $FSTAB echo proc /proc proc defaults 0 0 >> $FSTAB echo sysfs /sys sysfs defaults 0 0 >> $FSTAB echo devpts /dev/pts devpts gid=5,mode=620 0 0 >> $FSTAB echo tmpfs /dev/shm tmpfs defaults 0 0 >> $FSTAB } # Change root and execute init # $1 = where to change root # change_root() { umount /proc umount /sys rm -Rf /lib/modules # this will no longer be needed at all cd "$1" # make sure important devices are in union if [ ! -e dev/console ]; then mknod dev/console c 5 1; fi if [ ! -e dev/null ]; then mknod dev/null c 1 3; fi # find chroot and init if [ -x bin/chroot ]; then CHROOT=bin/chroot; fi if [ -x sbin/chroot ]; then CHROOT=sbin/chroot; fi if [ -x usr/bin/chroot ]; then CHROOT=usr/bin/chroot; fi if [ -x usr/sbin/chroot ]; then CHROOT=usr/sbin/chroot; fi if [ "$CHROOT" = "" ]; then fatal "Can't find executable chroot command"; fi if [ -x bin/init ]; then INIT=bin/init; fi if [ -x sbin/init ]; then INIT=sbin/init; fi if [ "$INIT" = "" ]; then fatal "Can't find executable init command"; fi mkdir -p mnt/live mount -n -o remount,ro aufs . pivot_root . mnt/live exec $CHROOT . $INIT < dev/console > dev/console 2>&1 }