diff --git a/bootfiles/build b/bootfiles/build new file mode 100644 index 0000000..7074793 --- /dev/null +++ b/bootfiles/build @@ -0,0 +1,231 @@ +#!/bin/bash + +# Linux Live Kit Improved v1.0 +# Original version: Linux Live Kit version 7 +# Modified/improved by: @johndah4x0r [terencedoesmc12 AT gmail DOT com] + +# Environment path +export PATH="${PATH}:.:./tools:../tools" +source ./.config || exit 1 +source ./livekitlib || exit 1 + +# only root can continue, because only root can read all files from your system +allow_only_root + +# Change directory to build environment +CHANGEDIR="$(dirname $0)" +echo "Changing current directory to $CHANGEDIR" +CWD="$(pwd)" +cd "$CHANGEDIR" + + +# It's building time! +clear + +echo_sign "BUILD SCRIPT" +echo_livekit_msg "build: Doing a self-check..." + +# Start with 0 errors +ERRS=0 + +# Check if mksquashfs supports XZ compression +if [ -z "$(mksquashfs 2>&1 | grep "Xdict-size")" ]; then + echo_err "build: 'mksquashfs': not-avail / not-supported" + (( ERRS+=1 )) +else + echo_livekit_msg "build: 'mksquashfs': avail + supported" +fi + +# Check if either 'mkisofs' or 'genisoimage' exists +MKISOFS="$(which mkisofs 2>/dev/null || \ +which genisoimage 2>/dev/null)" +if [ -z "$MKISOFS" ]; then + echo_err "build: 'mkisofs' / 'genisoimage': not-avail" + + # Hahaha! 'false'?!?!? + MKISOFS="false" +else + echo_livekit_msg "build: '$(basename $MKISOFS)': avail" +fi + +# Check if we have 'zip' +ZIP_CMD="$(which zip 2>/dev/null)" +if [ -z "$ZIP_CMD" ]; then + # since a ZIP archive is the core, we have to go to fatal mode! + echo_fatal "build: 'zip': not-avail" + echo_fatal "build: 'zip': Critically needed!" + (( ERRS+= 2 )) +else + echo_livekit_msg "build: 'zip': avail" +fi + + +if [ $ERRS -eq 0 ]; then + echo_livekit_msg "build: Problems found: $ERRS" + echo_livekit_msg "build: Self-check passed." +else + echo_err "build: Problems found: $ERRS" + echo_err "build: Self-check failed!" + echo_err "build: Please make sure that you have all needed packages installed!" + exit 1 +fi + +echo_livekit_msg "build: Preparing to build..." +clear + +echo " ===========================================" +echo " Linux Live Kit version 2.0 (improved)" +echo " ===========================================" +echo " Live Kit information:" +echo " > Kernel version: $KERNEL" +echo " > Architecture: $ARCH" +echo " > Live Kit name: $LIVEKITNAME" +echo " > Bundle extension: '.$BEXT'" + +read -p "Press Enter to continue or press Ctrl-C to cancel... " junk + +# It's time to rock 'n roll! +clear + +# Generate initramfs image (cpio-xz archive) +echo_livekit_msg "build: Generating initramfs CPIO-XZ image..." + +cd initramfs +INITRAMFS="$(./gen-initramfs "$LIVEKITNAME")" +cd .. + +# Prepare the Live Kit archive +rm -Rf "$LIVEKITDATA" +echo_livekit_msg "build: Preparing boot files..." +BOOT="$LIVEKITDATA"/"$LIVEKITNAME"/boot +mkdir -p "$BOOT" +mkdir -p "$BOOT"/../changes +mkdir -p "$BOOT"/../bundles +mv "$INITRAMFS" $BOOT/initramfs.img +cp bootfiles/* $BOOT + +# do substitution +cat bootfiles/syslinux.cfg | sed -r "s:/boot/:/$LIVEKITNAME/boot/:" | \ +sed -r "s:MyLinux:$LIVEKITNAME:" > $BOOT/syslinux.cfg + +echo_livekit_msg "build: BootInstall.*: Replacing 'MyLinux' with '$LIVEKITNAME'..." +cat bootfiles/BootInstall.bat | sed -r "s:/boot/:/$LIVEKITNAME/boot/:" | \ + sed -r "s:\\\\boot\\\\:\\\\$LIVEKITNAME\\\\boot\\\\:" | fgrep -iv "rem" | \ + sed -r "s:MyLinux:$LIVEKITNAME:" > $BOOT/BootInstall.bat +cat bootfiles/BootInstall.sh | sed -r "s:MyLinux:$LIVEKITNAME:" > $BOOT/BootInstall.sh + +echo_livekit_msg "build: Copying kernel..." +cp $VMLINUZ $BOOT/ + +# Copy files from include_bund/, but +# do not skip bundle creation +# +if [ -n "$(ls -A include_bund/ | grep ".$BEXT")" ]; then + echo_livekit_msg "build: Copying bundles from include_bund/ ..." + ls -A include_bund/ | grep ".$BEXT" | \ + while read BUND; do + cp $BUND "$LIVEKITDATA"/"$LIVEKITNAME"/bundles/ + done +fi + +# create compressed bundles +for i in $MKMOD; do + CMDOPT="$(get_exclude "$EXCLUDE" $i)" + echolog "CMDOPT = $CMDOPT" >&2 + mkbund /$i $LIVEKITDATA/$LIVEKITNAME/00-main-$i.$BEXT \ + -keep-as-directory $CMDOPT +done + +# copy rootcopy folder +if [ -d rootcopy/ ]; then + echo_livekit_msg "build: Copying contents of rootcopy/..." + cp -a rootcopy/ $LIVEKITDATA/$LIVEKITNAME/ +fi + +TARGET=/mnt/z +if [ ! -d $TARGET ]; then + TARGET=/tmp/livekit-build/ +fi + +if [ ! -d $TARGET ]; then + mkdir -p $TARGET &>/dev/null +fi + +# Output file +OUT_FILE="$LIVEKITNAME-$ARCH-$PID" + +# Checksum file +SUM_FILE="$TARGET/CHECKSUMS-${OUT_FILE}.TXT" + +# Go to Live Kit build data +cd "$LIVEKITDATA" + +# Create ISO image +echo_livekit_msg "build: Creating ISO file for CD boot..." + +# How the F@-- can it be more compact than this!?!? +"$MKISOFS" -o "$TARGET/$OUT_FILE.iso" -v -J \ +-R -D -A "$LIVEKITNAME" -V "$LIVEKITNAME" \ +-no-emul-boot -boot-info-table -boot-load-size 4 \ +-b "$LIVEKITNAME"/boot/isolinux.bin -c \ +"$LIVEKITNAME"/boot/isolinux.boot . &>/dev/null +if [ $? -ne 0 ]; then + echo_warn "build: Failed to generate ISO image!" + SCAN= +else + echo_livekit_msg "build: ISO image: $OUT_FILE.iso" + SCAN=1 +fi + +# Substitute 'mylinux' with $LIVEKITNAME +cat "$CWD/bootinfo.txt" | fgrep -v "#" | \ + sed -r "s/mylinux/$LIVEKITNAME/" | sed -r "s/\$//" > readme.txt + +# Create ZIP archive for "universal" use +echo_livekit_msg "build: Creating ZIP for USB boot..." +rm -f "$TARGET/$OUT_FILE.zip" +zip -0 -r "$TARGET/$OUT_FILE.zip" * &>/dev/null +echo_livekit_msg "build: Output file: $OUT_FILE.zip" + +echo_livekit_msg "build: Cleaning up..." +cd .. +rm -Rf "$LIVEKITDATA" + +# just for aesthetics +echo_livekit_msg "build: Process ID: $PID - Your results is in $TARGET" + +# Generate checksum(s) +echo_livekit_msg "build: Generating MD5 checksums: Please wait..." + +if [ "$SCAN" ]; then + MD5_ISO="$(md5sum $TARGET/$OUT_FILE.iso 2>/dev/null | cut -d ' ' -f 1)" +else + MD5_ISO="< FAILED TO GENERATE ISO IMAGE! >" +fi + +MD5_ZIP="$(md5sum $TARGET/$OUT_FILE.zip 2>/dev/null | cut -d ' ' -f 1)" + +cat >"$SUM_FILE" < +# Modified by: 'JohnDaH4x0r' [terencedoesmc12 AT gmail DOT com] + +source ../.config + +INITRAMFS=/tmp/$LIVEKITNAME-initramfs-$PID + +# copy file to initramfs tree, including +# all library dependencies (as shown by ldd) +# $1 = file to copy (full path) +copy_including_deps() +{ + # if source doesn't exist or target exists, do nothing + if [ ! -e "$1" -o -e "$INITRAMFS"/"$1" ]; then + return + fi + + cp -R --parents "$1" "$INITRAMFS" + if [ -L "$1" ]; then + DIR="$(dirname "$1")" + LNK="$(readlink "$1")" + copy_including_deps "$(cd "$DIR"; realpath -s "$LNK")" + fi + + ldd "$1" 2>/dev/null | sed -r "s/.*=>|[(].*//g" | sed -r "s/^\\s+|\\s+\$//" \ + | while read LIB; do + copy_including_deps "$LIB" + done + + for MOD in $(find "$1" -type f | grep .ko); do + for DEP in $(cat /$LMK/modules.dep | fgrep /$(basename $MOD):); do + copy_including_deps "/$LMK/$DEP" + done + done + + shift + if [ "$1" != "" ]; then + copy_including_deps "$@" + fi +} + +rm -Rf $INITRAMFS +mkdir -p $INITRAMFS/{bin,dev,etc,lib,lib64,mnt,proc,root,run,sys,tmp,usr,var/log} +ln -s bin $INITRAMFS/sbin + +cd static +./update +cd .. + +cp static/{busybox,mount.dynfilefs,mount.ntfs-3g,eject} $INITRAMFS/bin +chmod a+x $INITRAMFS/bin/{busybox,mount.*,eject} + +$INITRAMFS/bin/busybox | grep , | grep -v Copyright | tr "," " " | while read LINE; do + for TOOL in $LINE; do + if [ ! -e $INITRAMFS/bin/$TOOL ]; then + ln -s busybox $INITRAMFS/bin/$TOOL + fi + done +done +rm -f $INITRAMFS/{s,}bin/init + +mknod $INITRAMFS/dev/console c 5 1 +mknod $INITRAMFS/dev/null c 1 3 +mknod $INITRAMFS/dev/ram0 b 1 0 +mknod $INITRAMFS/dev/tty1 c 4 1 +mknod $INITRAMFS/dev/tty2 c 4 2 +mknod $INITRAMFS/dev/tty3 c 4 3 +mknod $INITRAMFS/dev/tty4 c 4 4 + +#copy_including_deps /usr/bin/strace +#copy_including_deps /usr/bin/lsof + +copy_including_deps /$LMK/kernel/fs # all filesystems +copy_including_deps /$LMK/kernel/drivers/staging/zsmalloc # needed by zram +copy_including_deps /$LMK/kernel/drivers/block/zram +copy_including_deps /$LMK/kernel/drivers/block/loop.* + +# usb drivers +copy_including_deps /$LMK/kernel/drivers/usb/storage/usb-storage.* +copy_including_deps /$LMK/kernel/drivers/usb/host +copy_including_deps /$LMK/kernel/drivers/usb/common +copy_including_deps /$LMK/kernel/drivers/usb/core +copy_including_deps /$LMK/kernel/drivers/hid/usbhid +copy_including_deps /$LMK/kernel/drivers/hid/hid.* +copy_including_deps /$LMK/kernel/drivers/hid/uhid.* +copy_including_deps /$LMK/kernel/drivers/hid/hid-generic.* + +# disk and cdrom drivers +copy_including_deps /$LMK/kernel/drivers/cdrom +copy_including_deps /$LMK/kernel/drivers/scsi/sr_mod.* +copy_including_deps /$LMK/kernel/drivers/scsi/sd_mod.* +copy_including_deps /$LMK/kernel/drivers/scsi/scsi_mod.* +copy_including_deps /$LMK/kernel/drivers/scsi/sg.* +copy_including_deps /$LMK/kernel/drivers/ata + +# copy all custom-built modules +copy_including_deps /$LMK/updates + +copy_including_deps /$LMK/modules.* + + +find $INITRAMFS -name "*.ko.gz" -exec gunzip {} \; + +# trim modules.order file. Perhaps we could remove it entirely +MODULEORDER="$(cd "$INITRAMFS/$LMK/"; find -name "*.ko" | sed -r "s:^./::g" | tr "\n" "|" | sed -r "s:[.]:.:g")" +cat $INITRAMFS/$LMK/modules.order | sed -r "s/.ko.gz\$/.ko/" | grep -E "$MODULEORDER"/foo/bar > $INITRAMFS/$LMK/_ +mv $INITRAMFS/$LMK/_ $INITRAMFS/$LMK/modules.order + +depmod -b $INITRAMFS $KERNEL + +echo "root::0:0::/root:/bin/bash" >$INITRAMFS/etc/passwd +touch $INITRAMFS/etc/{m,fs}tab + +cp init $INITRAMFS +chmod a+x $INITRAMFS/init +cp cleanup $INITRAMFS/lib +chmod a+x $INITRAMFS/lib/cleanup +ln -s ../init $INITRAMFS/bin/init +cp ../livekitlib $INITRAMFS/lib/ +cp ../.config $INITRAMFS/lib/ + +cd $INITRAMFS +find . -print | cpio -o -H newc 2>/dev/null | xz -f --extreme --check=crc32 >$INITRAMFS.img +echo $INITRAMFS.img + +cd .. +rm -Rf $INITRAMFS +#mv $INITRAMFS.img /tmp/initrfs.img diff --git a/bootfiles/initramfs/init b/bootfiles/initramfs/init new file mode 100644 index 0000000..2c96b56 --- /dev/null +++ b/bootfiles/initramfs/init @@ -0,0 +1,93 @@ +#!/bin/sh +# Initial script for Linux Live Kit / Linux Live Kit Improved +# Modifier: "JohnDaH4x0r" + +# Modify and export/declare new PATH +export PATH=.:/:/usr/sbin:/usr/bin:/sbin:/bin + +# Source Live Kit library script +. /lib/.config +. /lib/livekitlib + +# Declare start of 'init' state +clear +echo_sign "INIT START" + +# Run switch_root: initramfs -> tmpfs +transfer_initramfs + +# Directory variables pointing to /memory +MEMORY=/memory +CHANGES=$MEMORY/changes +UNION=$MEMORY/union +DATAMNT=$MEMORY/data +BUNDLES=$MEMORY/bundles + +# Initliaise /proc, /sys and such +init_proc_sysfs + +# Initialise debugging if requested +debug_start +dbg_shell_start + +# 1st debug shell interval +debug_shell + +# Initialise important kernel modules +init_devs +init_aufs +init_zram + +# Then, modprobe everything +modprobe_everything + +# Find data dir with filesystem bundles +# NEW: +# Only 15 seconds before timeout, to minimize the +# pain of waiting a "whole" minute. +# +DATA="$(find_data 15 "$DATAMNT")" + +# 2nd debug shell interval +debug_shell + +# Setup persistent changes +persistent_changes "$DATA" "$CHANGES" + +# 3rd debug shell interval +debug_shell + +# Copy data to RAM if requested by user +DATA="$(copy_to_ram "$DATA" "$CHANGES")" + +# Setup an empty union +init_union "$CHANGES" "$UNION" + +# 4th debug shell interval +debug_shell + +# Append bundles to union +union_append_bundles "$DATA" "$BUNDLES" "$UNION" + +# 5th debug shell interval +debug_shell + +# Copy contents of 'rootcopy/' +copy_rootcopy_content "$DATA" "$UNION" + +# Generate a basic 'fstab' with the core filesystems +fstab_create "$UNION" + +# 6th and final debug shell interval +debug_shell + +# Declare the end of first 'init' state +clear +echo_sign "INIT END" + +# Change root to main OS and let the 'init' in the +# main OS do the rest... +change_root "$UNION" + +# < ======== NOTHING SHOULD GO OVER THIS LINE! ======== > +fatal "Unknown error!" diff --git a/bootfiles/livekitlib b/bootfiles/livekitlib new file mode 100644 index 0000000..4d8396e --- /dev/null +++ b/bootfiles/livekitlib @@ -0,0 +1,784 @@ +#!/bin/sh + +# Functions library :: for Linux Live Kit scripts +# Original author: Tomas M. +# Modified by: 'JohnDaH4x0r' + +# related to build script +# get_exclude: Generate exclude arguments for 'mksquashfs' +# $1: the exclude list ("$EXCLUDE", if you use .config) + +get_exclude() +{ + EXCLUDE_LIST="$1" + PARENT_DIR="$(echo "$2" | tr -s "/")" + + if [ -z "$PARENT_DIR" ]; then + return 1 + elif [ "$PARENT_DIR" = "/" ]; then + return 1 + fi + + echo "$EXCLUDE_LIST" | tr " " "\n" | grep "$PARENT_DIR" \ + | while read KEY; do + echo -n "-e $KEY " + done +} + +# NOTE: DO NOT TOUCH THE ESCAPE CHARACTERS! +BLUE="" +YELLOW="" +ORANGE="" +GREEN="" +BOLD="" +RED="" +NC="" + +# debug related +debug_start() +{ + if grep -q 'debug' /proc/cmdline; then + DEBUG_IS_ENABLED=1 + else + DEBUG_IS_ENABLED= + fi +} + +dbg_shell_start() +{ + if grep -q 'dbg_shell' /proc/cmdline; then + DBG_SHELL=1 + else + DBG_SHELL= + fi +} + +debug_log() +{ + if [ "$DEBUG_IS_ENABLED" ]; then + echo "[${GREEN}"' debug '"${NC}]: $@" >&2 + log "debug_log: $*" + fi +} + +# echo related functions + +# echo fatal tag +# $1 = text to show +echo_fatal() +{ + echo "[${RED}"' fatal '"${NC}] $@" >&2 +} + +# echo bold +# $1 = text to show +# +echo_bold() +{ + echo "$BOLD""$@""$NC" +} + +# echo livekit tag +echo_livekit() +{ + echo "[${BLUE}"'livekit'"${NC}] " +} + +echo_livekit_msg() +{ + echo "[${BLUE}"'livekit'"${NC}] $@" +} + +# echo error +echo_err() +{ + echo "[${RED}"' error '"${NC}] $@" >&2 +} + +# echo warn +echo_warn() +{ + echo "[${YELLOW}"' warn! '"${NC}] $@" >&2 +} + +# echo signature +echo_sign() +{ + echo "${BOLD}"' Linux Live Kit Improved v1.0 - '"$* ${NC}" +} + +# log - store given text in /var/log/livedbg +log() +{ + echo "$@" 2>/dev/null >>/var/log/livekit-log +} + +echolog() +{ + echo "$@" + log "echolog: $@" +} + +# show information about the debug shell +show_debug_banner() +{ + echo_sign "DEBUGGING MODE" + echo "The root shell is prepared for you!" >&2 + echo "Type your desired commands or press Ctrl-D to continue booting." >&2 + echo >&2 +} + +# debug_shell +# executed when debug boot parameter is present +# +debug_shell() +{ + if [ "$DBG_SHELL" ]; then + show_debug_banner + setsid sh -c 'exec sh < /dev/tty1 >/dev/tty1 2>&1' + echo + fi +} + +fatal() +{ + log "FatalError: $@" + echo_fatal "$@" + echo_sign "EMERGENCY MODE" >&2 + echo_bold "Please consult /var/log/livekit-log for more info." >&2 + 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_err "Only root can run $(basename $0)"; exit 1 + fi +} + +# Make 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 +# +mkbund() +{ + debug_log "mkbund" "$*" + + if [ -e "$2" ]; then + echo_warn "mkbund: $(basename $2): File already exists!" + echo_warn "mkbund: $(basename $2): Deleting file..." + rm -f "$2" + else + echo_livekit_msg "mkbund: $(basename $2): Making new bundle..." + fi + + echo_livekit_msg "mkbund: SquashFS compression: 1024k - XZ" >&2 + echo_livekit_msg "mkbund: $(basename $2): Compressing bundle..." >&2 + mksquashfs "$1" "$2" -comp xz -b 1024k $3 $4 $5 $6 $7 $8 $9 >/dev/null || \ + (echo_err "mkbund: Failed to make bundle!" && exit 1) +} + +# Now, for the Live Kit system-related stuff + +# get value of kernel cmdline parameter $1 +# $1 = parameter to search for +# +cmdline_value() +{ + cat /proc/cmdline | egrep -o "(^|[[:space:]])$1=[^[:space:]]+" | \ + tr -d " " | cut -d "=" -f 2- | tail -n 1 +} + + +# Move entire initramfs tree to tmpfs mount. +# It's a bit tricky but is necessray to enable pivot_root +# even for initramfs boot image +# +transfer_initramfs() +{ + if [ ! -r /lib/esc_initramfs.sgn ]; then + echo_livekit >&2 + echo "Switching root from initramfs to tmpfs..." >&2 + SWITCH=/m # one letter directory + mkdir -p $SWITCH + mount -t tmpfs -o size="100%" tmpfs "$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/esc_initramfs.sgn + exec switch_root -c /dev/console . "$0" + fi +} + +# mount virtual filesystems like proc, sys and such +# +init_proc_sysfs() +{ + debug_log "init_proc_sysfs" "$*" + mkdir -p /proc /sys /etc "$MEMORY" + 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 +} + + +# modprobe all modules found in initial ramdisk +modprobe_everything() +{ + debug_log "modprobe_everything" "$*" + + echo_livekit_msg "Probing for hardware..." >&2 + + find /lib/modules/ | fgrep '.ko' | xargs -n 1 modprobe 2>/dev/null + refresh_devs +} + + +refresh_devs() +{ + debug_log "refresh_devs" "$*" + if [ -r /proc/sys/kernel/hotplug ]; then + echo /sbin/mdev > /proc/sys/kernel/hotplug + fi + mdev -s +} + +# make sure some devices are there +init_devs() +{ + debug_log "init_devs" "$*" + modprobe zram 2>/dev/null + modprobe loop 2>/dev/null + modprobe squashfs 2>/dev/null + modprobe fuse 2>/dev/null + refresh_devs +} + +# 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_livekit_msg "Setting up ZRAM as swap if available..." + if [ -r /sys/block/zram0/disksize ]; then + echo 536870912 > /sys/block/zram0/disksize # 512MB + mkswap /dev/zram0 >/dev/null + swapon /dev/zram0 -p 32767 + echo 100 > /proc/sys/vm/swappiness + fi +} + +# load the AUFS kernel module if needed +# +init_aufs() +{ + debug_log "init_aufs" "$*" + + # Prepare for error messages + echo >>/var/log/livkekit-log + + modprobe aufs 2>/var/log/livekit-log + + # If aufs module failed to load, panic immidiately + if [ $? -ne 0 ]; then + fatal "Failed to load AUFS module!" + fi + + # finally, refresh all devices + refresh_devs +} + +# Setup empty union +# $1 = changes directory (ramfs or persistent changes) +# $2 = union directory where to mount the union +# +init_union() +{ + debug_log "init_union" "$*" + + echo_livekit_msg "Initialising AUFS union..." + mkdir -p "$1" + mkdir -p "$2" + + # Prepare for error messages + echo >>/var/log/livekit-log + + mount -t aufs -o xino="/.xino",trunc_xino,br="$1" aufs "$2" >/var/log/livekit-log 2>&1 + + # If failed to init union, panic + if [ $? -ne 0 ]; then + fatal "Failed to initialise AUFS union!" + fi +} + +# 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" + return + fi + MNT="$(dirname "$MNT")" + done +} + +# Return mounted dir for given directory +# $1 = directory +# +mounted_dir() +{ + debug_log "mounted_dir" "$*" + + local MNT + MNT="$1" + while [ "$MNT" != "/" -a "$MNT" != "." -a "$MNT" != "" ]; do + if mountpoint -q "$MNT" 2>/dev/null; then + echo "$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]" | cut -d ' ' -f 1)" + if [ "$FS" = "msdos" -o "$FS" = "fat" ]; then + FS="vfat" + elif [ "$FS" = "ntfs" ]; then + FS="ntfs-3g" + else + FS="$FS" + fi + echo "-t $FS" +} + +# Filesystem options for mount +# $1 = filesystem or '-t filesystem' +# +fs_options() +{ + debug_log "fs_options" "$*" + + if [ "$1" = "-t" ]; then + shift + fi + if [ "$1" = "vfat" ]; then + echo "-o check=s,shortname=mixed,iocharset=utf8" + fi +} + + +# Modprobe network kernel modules until a working driver is found. +# These drivers are (or used to be) probed in Slackware's initrd. +# The function returns the first device found, yet it doesn't have +# to be a working one, eg. if the computer has two network interfaces +# and ethernet cable is plugged only to one of them. +# +init_network_dev() +{ + debug_log "init_network_dev" "$*" + echo_livekit_msg "Preparing networking device(s)..." >&2 + local MODULE ETH + + for MODULE in 3c59x acenic de4x5 e1000 e1000e e100 epic100 hp100 \ + ne2k-pci pcnet32 8139too 8139cp tulip via-rhine r8169 atl1e yellowfin \ + tg3 dl2k ns83820 atl1 b44 bnx2 skge sky2 tulip depca 3c501 3c503 \ + 3c505 3c507 3c509 3c515 ac3200 at1700 cosa cs89x0 de600 de620 e2100 \ + eepro eexpress eth16i ewrk3 forcedeth hostess_sv11 hp-plus hp ni52 \ + ni65 sb1000 sealevel smc-ultra sis900 smc9194 wd; do + modprobe $MODULE 2>/dev/null + ETH="$(cat /proc/net/dev | grep : | grep -v lo: | cut -d : -f 1 | tr -d " " | head -n 1)" + if [ "$ETH" != "" ]; then + echo "$ETH" + return 0 + fi + rmmod $MODULE 2>/dev/null + done + + # If we are here, none of the above specified modules worked. + # As a last chance, try to modprobe everything. + echo_warn "No networking kernel module found!" + modprobe_everything + cat /proc/net/dev | grep ':' | grep -v 'lo:' | cut -d : -f 1 | tr -d " " | head -n 1 +} + + +# Download data from tftp +# $1 = target (store downloaded files there) +# +download_data_pxe() +{ + debug_log "download_data_pxe" "$*" + local CMD CLIENT SERVER GW MASK PORT ETH PROTOCOL + + mkdir -p "$1/$LIVEKITNAME" + + cmdline_value ip | while IFS=":" read CLIENT SERVER GW MASK PORT; do + echo_livekit_msg "Downloading files from ${SERVER}..." >&2 + + ETH=$(init_network_dev) + if [ "$PORT" = "" ]; then + PORT="7529" + fi + + # set IP address as given by boot paramter + if [ -n "$CLIENT" ] && [ -n "$MASK" ]; then + ifconfig $ETH "$CLIENT" netmask "$MASK" + route add default gw "$GW" + else + # if client ip is unknown, try to get a DHCP lease + udhcpc -i $ETH -f -q + fi + + # well known IP address of Google public DNS service + echo nameserver 8.8.8.8 >> /etc/resolv.conf + + PROTOCOL=http + wget -q -O "$1/PXEFILELIST" "http://$SERVER:$PORT/PXEFILELIST?$(uname -r):$(uname -m)" + if [ $? -ne 0 ]; then + echo_warn "Failed to download from http://${SERVER}:${PORT}!" >&2 + echo_livekit_msg 'Downloading with TFTP protocol...' >&2 + PROTOCOL=tftp + tftp -g -r PXEFILELIST -l "$1/PXEFILELIST" $SERVER + fi + + cat "$1/PXEFILELIST" | while read FILE; do + if [ "$PROTOCOL" = "http" ]; then + echo_livekit_msg "PXE-HTTP: Downloading ${FILE}..." >&2 + wget -O "$1/$LIVEKITNAME/$FILE" "http://$SERVER:$PORT/$FILE" + elif [ "$PROTOCOL" = "tftp" ]; then + echo_livekit_msg "PXE-TFTP: Downloading ${FILE}..." >&2 + tftp -g -r $FILE -l "$1/$LIVEKITNAME/$FILE" $SERVER + fi + done + done + + echo "$1/$LIVEKITNAME" +} + +# Find LIVEKIT data by mounting all devices +# If found, keep mounted, else unmount +# $1 = data directory target (mount here) +# $2 = data directory which contains compressed bundles +# +find_data_try() +{ + debug_log "find_data_try" "$*" + + local DEVICE FS FROM OPTIONS + + mkdir -p "$1" + blkid | sort | cut -d: -f 1 | grep -E -v "/loop|/ram|/zram" | while read DEVICE; do + FROM="$2" + FS="$(device_bestfs "$DEVICE")" + OPTIONS="$(fs_options $FS)" + mount -r "$DEVICE" "$1" $FS $OPTIONS 2>/dev/null + + # if the FROM parameter is actual file, mount it again as loop (eg. iso) + if [ -f "$1/$FROM" ]; then + mount -o remount,rw "$DEVICE" "$1" 2>/dev/null + mkdir -p "$1/../file" + mount -o loop,ro "$1/$FROM" "$1/../file" 2>/dev/null + FROM="../file/$LIVEKITNAME" + fi + + # search for bundles in the mounted directory + if [ "$(find "$1/$FROM" -maxdepth 1 -name "*.$BEXT" 2>/dev/null)" != "" ]; then + # we found at least one bundle/module here + mount -o remount,rw "$DEVICE" "$1" 2>/dev/null + echo "$1/$FROM" | tr -s "/" | sed -r "s:/[^/]+/[.][.]/:/:g" + return + fi + + # unmount twice, since there could be mounted ISO as loop too. If not, it doesn't hurt + umount "$1" 2>/dev/null + 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 FROM + + if [ "$(cmdline_value ip)" != "" ]; then + download_data_pxe "$2" + return + fi + + FROM="$(cmdline_value from)" + if [ "$FROM" = "" ]; then FROM="$LIVEKITNAME"; fi + echo_livekit >&2 + echo "Looking for $LIVEKITNAME data in /$FROM .." | tr -s "/" >&2 + for timeout in $(seq 1 $1); do + echo -n "." >&2 + refresh_devs + DATA="$(find_data_try "$2" "$FROM")" + + # This "was" a bug! + echo "" >&2 + + if [ "$DATA" != "" ]; then + echo_livekit_msg "$LIVEKITNAME data found on $(mounted_device "$2")" >&2 + echo "$DATA" + return + fi + sleep 1 + done + echo "" >&2 + + if [ "$DATA" = "" ]; then + fatal "$LIVEKITNAME data not found" + fi + +} + +# Activate persistent changes +# $1 = data directory +# $2 = target changes directory +# +persistent_changes() +{ + debug_log "persistent_changes" "$*" + + local CHANGES T1 T2 + + CHANGES="$1/$(basename "$2")" + T1="$CHANGES/.perch-test.dat" + T2="${T1}-clone.dat" + + # Setup the directory anyway, it will be used in all cases + mkdir -p "$2" + + # If persistent changes are not requested, end here + if grep -vq 'perch' /proc/cmdline; then + return + fi + + # check if changes directory exists and is writable + touch "$T1" 2>/dev/null && rm -f "$T1" 2>/dev/null + + # if not, simply return back + if [ $? -ne 0 ]; then + echo_warn "Persistent changes not writable or not used." + return + fi + echo_livekit_msg "Testing persistent changes for POSIX compatibility..." >&2 + touch "$T1" && ln -sf "$T1" "$T2" 2>/dev/null && \ + chmod +x "$T1" 2>/dev/null && test -x "$T1" && \ + chmod -x "$T1" 2>/dev/null && test ! -x "$T1" && \ + rm "$T1" "$T2" 2>/dev/null + + if [ $? -ne 0 ]; then + echo_warn "File system is not POSIX-compatible!" >&2 + echo_livekit_msg "Activating DynFileFS persistent changes..." >&2 + rm "$T1" "$T2" 2>/dev/null + + mount.dynfilefs "$CHANGES/changes.dat" 4000 "$2" + if [ "$(device_bestfs "$2/loop.fs" | tr -d " ")" = "-t" ]; then + mke2fs -F "$2/loop.fs" >/dev/null + fi + mount -o loop,sync "$2/loop.fs" "$2" + rmdir "$2/lost+found" 2>/dev/null + else + echo_livekit_msg "Activating native persistent changes..." >&2 + mount --bind "$CHANGES" "$2" + fi +} + +# Copy content of rootcopy directory to union +# $1 = data directory +# $2 = union directory +copy_rootcopy_content() +{ + debug_log "copy_rootcopy_content" "$*" + + if [ "$(ls -1 "$1/rootcopy/" 2>/dev/null)" != "" ]; then + echo_livekit + echo "Copying content of rootcopy directory..." + cp -a "$1"/rootcopy/* "$2" + fi +} + +# Copy data to RAM if requested +# $1 = live data directory +# $2 = changes directory +# +copy_to_ram() +{ + debug_log "copy_to_ram" "$*" + + local MDIR MDEV RAM CHANGES + + if grep -vq 'copy2ram' /proc/cmdline; then + echo "$1" + return + fi + + echo_livekit_msg "Copying $LIVEKITNAME data to RAM..." >&2 + RAM="$(dirname "$2")"/copy_to_ram + mkdir -p "$RAM" + cp -a "$1"/* "$RAM" + echo "$RAM" + + MDIR="$(mounted_dir "$1")" + MDEV="$(mounted_device "$1")" + MDEV="$(losetup $MDEV 2>/dev/null | cut -d " " -f 3)" + umount "$MDIR" 2>/dev/null + + if [ "$MDEV" ]; then # iso was mounted here, try to unmount the FS it resides on too + MDEV="$(mounted_device "$MDEV")" + umount "$MDEV" 2>/dev/null + fi +} + +# load filter +# +filter_load() +{ + local FILTER + FILTER=$(cmdline_value load) + if [ "$FILTER" = "" ]; then + cat - + else + cat - | egrep "$FILTER" + fi +} + +# noload filter +# +filter_noload() +{ + local FILTER + FILTER=$(cmdline_value noload) + if [ "$FILTER" = "" ]; then + cat - + else + cat - | egrep -v "$FILTER" + fi +} + +# sort modules by number even if they are in subdirectory +# +sortmod() +{ + cat - | sed -r "s,(.*/(.*)),\\2:\\1," | sort -n | cut -d : -f 2- +} + +# 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" "$*" + + local BUN + + echo_livekit >&2 + echo "Appending bundles to union..." >&2 + + # Just tell me why! Why be compact?!? + ( ls -1 "$1" | sort -n ; cd "$1" ; find modules/ 2>/dev/null | \ + sortmod | filter_load) | grep '[.]'$BEXT'$' |\ + filter_noload | \ + while read BUNDLE; do + echo_livekit_msg "Appending: $BUNDLE" >&2 + + BUN="$(basename "$BUNDLE")" + mkdir -p "$2/$BUN" + + # It now depends on your kernel! + mount -o loop -t squashfs "$1/$BUNDLE" "$2/$BUN" + mount -o remount,add:1:"$2/$BUN" aufs "$3" + done +} + +# Create empty fstab properly +# $1 = root directory +# +fstab_create() +{ + debug_log "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() +{ + debug_log "change_root" "$*" + echo_livekit_msg "Changing root..." >&2 + umount /proc + umount /sys + + cd "$1" + + # make sure important device files and directories are in union + mkdir -p boot dev proc sys tmp mnt run + chmod 1777 tmp + if [ ! -e dev/console ]; then mknod dev/console c 5 1; fi + if [ ! -e dev/tty ]; then mknod dev/tty c 5 0; fi + if [ ! -e dev/tty0 ]; then mknod dev/tty0 c 4 0; fi + if [ ! -e dev/tty1 ]; then mknod dev/tty1 c 4 1; fi + if [ ! -e dev/null ]; then mknod dev/null c 1 3; fi + if [ ! -e sbin/fsck.aufs ]; then ln -s /bin/true sbin/fsck.aufs; fi + + # find chroot and init + if [ -x bin/chroot -o -L bin/chroot ]; then CHROOT=bin/chroot; fi + if [ -x sbin/chroot -o -L sbin/chroot ]; then CHROOT=sbin/chroot; fi + if [ -x usr/bin/chroot -o -L usr/bin/chroot ]; then CHROOT=usr/bin/chroot; fi + if [ -x usr/sbin/chroot -o -L usr/sbin/chroot ]; then CHROOT=usr/sbin/chroot; fi + if [ "$CHROOT" = "" ]; then fatal "chroot: Executable not found!"; fi + + if [ -x bin/init -o -L bin/init ]; then INIT=bin/init; fi + if [ -x sbin/init -o -L sbin/init ]; then INIT=sbin/init; fi + if [ "$INIT" = "" ]; then fatal "init: Executable not found!"; fi + + mkdir -p mnt/live + mount -n -o remount,ro aufs . + pivot_root . mnt/live + exec $CHROOT . $INIT < dev/console > dev/console 2>&1 +}