linux-live/livekitlib

768 lines
19 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

#!/bin/sh
# Functions library :: for Linux Live Kit scripts
# Original author: Tomas M. <http://www.linux-live.org>
# Modified by: 'JohnDaH4x0r' <terencedoesmc12 AT gmail DOT com>
# 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: `basemane $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/initramfs_escaped ]; 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/initramfs_escaped
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" "$*"
# TODO maybe check here if aufs support is working at all
# and produce useful error message if user has no aufs
modprobe aufs 2>/var/log/livekit-log
# TODO ACCOMPLISHED:
# 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"
mount -t aufs -o xino="/.xino",trunc_xino,br="$1" aufs "$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"
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 [ "$CLIENT" != "" -a "$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, trying TFTP..."
PROTOCOL=tftp
tftp -g -r PXEFILELIST -l "$1/PXEFILELIST" $SERVER
fi
cat "$1/PXEFILELIST" | while read FILE; do
if [ "$PROTOCOL" = "http" ]; then
wget -O "$1/$LIVEKITNAME/$FILE" "http://$SERVER:$PORT/$FILE"
else
echo_livekit_msg "PXE: ${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")"
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
}