#!/bin/ash
# puppy-compatible rc.sysinit - re-written from scratch
# (C) James Budiono, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 except where noted
# License: GNU GPL Version 3 or later
#
# not supported: full-install
# boot time parameters handled: pkeys extrasfs waitdev autoswap

### configuration
PRETTY_NAME='Fatdog64 Linux'
PRELOADED_MODULES=/etc/modules
EXTRASFS_MNT=/tmp/extrasfs/mnt
EXTRASFS_RAM=/tmp/extrasfs/ram
EXTRASFS_COUNT=0
export RC_NETWORK_PID=/tmp/rc.network.pid	# record pid in case we need to wait
export RC_SERVICES_PID=/tmp/rc.services.pid	# record pid in case we need to wait
export BOOTSTATE_PATH=/etc/BOOTSTATE
export PATH=/usr/sbin:/usr/bin:/sbin:/bin   # explicit path - /usr first
[ -r $BOOTSTATE_PATH ] && . $BOOTSTATE_PATH # load initramfs-time configuration

### helpers: declare first here, use later

### setup when running as full install or with foreign initrd
# this is really not necessary when using Fatdog's own initrd ...
setup_common_state() {
	[ "$GENUINE_FATDOG_INITRD" ] && return

	local p
	[ -d /proc/1 ] || mount -t proc proc /proc
	[ -d /sys/devices ] || mount -t sysfs sysfs /sys
	[ $(ls /dev | wc -l) -lt 100 ] && mount -t devtmpfs devtmpfs /dev
	[ -w /dev ] || mount -t tmpfs tmpfs /dev
	[ -e /dev/shm ] || mkdir -p /dev/shm
	[ -e /dev/pts ] || mkdir -p /dev/pts
	[ -e $BOOTSTATE_PATH ] || touch $BOOTSTATE_PATH

	# check full install
	p=$(ls /sys/fs/aufs/ | head -n1)
	if [ -z "$p" ]; then
		> $BOOTSTATE_PATH cat << "EOF"
NOT_USING_AUFS=true
AUFS_ROOT=/aufs
SAVEFILE_MOUNT=/			
EOF
		exec 2> /dev/initrd.err
	else
		sed -i -e '/NOT_USING_AUFS/d' $BOOTSTATE_PATH
		[ "$p" != "$AUFS_ROOT_ID" ] && AUFS_ROOT_ID=$p &&
		sed -i -e "s|AUFS_ROOT_ID=.*|AUFS_ROOT_ID=$p" $BOOTSTATE_PATH
	fi
}

# $1-extra sfs to load (one item)
load_extra_sfs() {
	local SFS="" RAM="" DEV="" MNT="" p

	# parse options	
	OIFS="$IFS"; IFS=":"; set -- $1; IFS="$OIFS"
	case $1 in
		direct) shift     ;; # drop it
		ram) RAM=1; shift ;;
	esac
	case $1 in
		device)
			DEV=/dev/$2
			SFS=$3
			shift 3 ;;
		label|uuid)
			DEV=$(blkid | grep -im 1 $1=\"$2\\b.\*\" | sed 's/:.*//')
			SFS=$3
			shift 3 ;;
		local)
			find_local_device DEV "$2"
			SFS=$2
			shift 2 ;;
		usb)
			find_local_device DEV "$2" usb
			SFS=$2
			shift 2 ;;
		path)
			SFS=$2
			shift 2;;
		/*) # start with a slash - path
			SFS=$1
			shift ;;
		*)  # anything else - assume path
			SFS=$1
			shift ;;
	esac
	
	# do options
	if [ "$DEV" ]; then
		# if already mounted, use existing mount point
		if grep -q "$DEV" /proc/mounts; then
			SFS="$(awk -v dev="$DEV" '$1==dev {print $2; exit}' /proc/mounts)"/$SFS
		else
		# otherwise mount it
			EXTRASFS_COUNT=$((EXTRASFS_COUNT+1))
			MNT=$EXTRASFS_MNT/$EXTRASFS_COUNT
			mkdir -p $MNT
			mount_ro_device $DEV $MNT
			SFS=$MNT/$SFS	
		fi
	fi
	if [ "$RAM" ]; then
		mkdir -p $EXTRASFS_RAM; p=$EXTRASFS_RAM/${SFS##*/}
		echo Copying ${SFS##*/} to RAM
		dd if=$SFS of=$p bs=1M && # dd not cp, because $p can be a block device
		SFS=$p
		if [ "$MNT" ]; then
			umount $MNT
		fi
	fi
	/sbin/load_sfs.sh --load $SFS
}
# $1-device $-mountpoint
mount_ro_device() {
	local fstype=$(guess_fstype $1)
	[ "$DOFSCK" ] && perform_fsck "$fstype" $1
	case "$fstype" in
		ntfs) # use read-only kernel ntfs driver
			mount -t ntfs $1 $2 -o ro
			;;
		vfat)
			mount -t vfat $1 $2 -o ro,utf8,quiet
			;;
		f2fs) # conflicts with xfs, need explicit type
			mount -t f2fs $1 $2 -o ro
			;;
		*)
			mount $1 $2 -o ro
			;;
	esac	
}
# $-fstype, $2 device
perform_fsck() {
	case $1 in
		ext*) e2fsck -y $2 ;;
		vfat) dosfsck -y $2 ;;
	esac
}
### utilities - find local device that contains savefile / basesfs
# find local devices that has basesfs/savefile in it all known block devices
# $1 - name of return variable
# $2 - basesfs / savefile path (cannot be blank)
# $3 - filter based on readlink value of /sys/block
find_local_device() {
	local result dev blockdev partitions
	local prevmount mntpoint mountdev

	result=
	mkdir /mnt/tmp	
	echo -n "Looking in "
	for blockdev in in $(ls /sys/block | grep -vE "^loop|^nbd|^ram|^nand"); do
		partitions=$(ls -d /sys/block/$blockdev/${blockdev}* | sed "s_/sys/block/$blockdev/__")
		partitions=${partitions:-$blockdev}	
		for dev in $partitions; do
			echo -n "$dev "
			if readlink /sys/block/$blockdev | grep -q "$3"; then

				# mount device if not mounted, otherwise use existing mount
				mntpoint=/mnt/tmp mountdev=/dev/$dev
				prevmount=$(awk -v mountdev="$mountdev" '$1 == mountdev { print $2; exit }' /proc/mounts)
				[ $prevmount ] && mntpoint=$prevmount || mount $mountdev $mntpoint 
				
				# mount it to see if the file exist
				if [ -e $mntpoint/$2 ]; then
					result=$mountdev
					echo -n "- found"
					[ -z $prevmount ] && umount -f $mntpoint
					break 2
				fi
				[ -z $prevmount ] && umount -f $mntpoint
			fi
		done
	done
	
	echo
	rmdir /mnt/tmp
	eval "$1=$result"
}


### cmdline processing - load keymaps and console fonts
# load keymaps and console fonts, modified from original by Barry K
# assuming that keymaps are in unicode
process_pkeys() {
	[ -z "$pkeys" ] && [ -e /etc/keymap ] && read -r pkeys < /etc/keymap 
	if [ "$pkeys" ]; then
		# default - empty fontmap, iso-8859-1 codepage
		rm -f /etc/fontmap
		echo ISO-8859-1 > /etc/codepage

		loadkeys "$pkeys" 2>/dev/null && echo Loading keymap "$pkeys" &&
		echo "$pkeys" > /etc/keymap
		case "$pkeys" in 
			us*) # us with intl support
				setfont LatGrkCyr-8x16
				echo LatGrkCyr-8x16 > /etc/fontmap
				;;	
			de*|be*|br*|dk*|es*|fi*|fr*|it*|no*|se*|pt*|uk*)
				echo 850 > /etc/codepage		
				modprobe nls_cp850
				setfont LatGrkCyr-8x16
				echo LatGrkCyr-8x16 > /etc/fontmap
				;;
			cz*|hu*|pl*|ro*|sk*|croat*|slovene*)
				modprobe nls_cp852
				modprobe nls_iso8859-2
				echo 852 > /etc/codepage			
				setfont LatGrkCyr-8x16
				echo LatGrkCyr-8x16 > /etc/fontmap
				;;
			*) 
				setfont "$pkeys"
				echo "$pkeys" > /etc/fontmap
				;;
		esac
	else
		[ -s /etc/fontmap ] && read p < /etc/fontmap && setfont $p
	fi
}

### cmdline processing - waitdev
# delay a specified time before continuing. Useful for slow hardware
# the first-digit is for waitdev, 2nd is for us
process_waitdev() {
	if [ "$waitdev" ]; then
		OIFS="$IFS"; IFS=: ; set -- $waitdev; IFS="$OIFS"
		if [ "$2" ] && [ "$2" -gt 0 ]; then
			echo "rc.sysinit: waiting $2 seconds for devices to be ready..."
			sleep "$2"
		fi
	fi
}



######################## MAIN ###########################

### minimum necessary setup
setup_common_state

# set system clock
HWCLOCKPARM="--localtime" # default
HWCLOCKCONF=/etc/hwclock.conf
[ -e $HWCLOCKCONF ] && . $HWCLOCKCONF # HWCLOCKPARM, HWCLOCKRTC
hwclock --hctosys $HWCLOCKPARM $HWCLOCKRTC
#systz auto > /dev/null # set kernel timezone (systz) - for vfat

# mount devpts, /tmp and /dev/shm and swap (if any)
[ ! -L /etc/mtab ] && { rm -rf /etc/mtab; ln -s /proc/mounts /etc/mtab; }
busybox mount -a 	# as configured in /etc/fstab
busybox swapon -a	# as configured in /etc/fstab
mkdir -p /tmp/lock /tmp/cryptsetup # dynamically create this directory every time

# Check for clean shutdown
if [ -e /etc/booted ] && [ -z "$NOT_USING_AUFS" ]; then
	echo "Running auchk..."
	auchk -w $SAVEFILE_MOUNT
else 
	touch /etc/booted
fi

# setup partition mounts - do this before load_sfs.sh, so it can find
# SFS located in these links/dirs/partitions.
# a. bind-mount for base/save devices to its "proper" /mnt/sdXX mountpoints
if [ "$BASE_SFS_DEV_MOUNT" ] && [ "$BASE_SFS_DEVICE" ]; then
	p="/mnt/${BASE_SFS_DEVICE##*/}"
	mkdir -p "$p"; mount -o bind "$BASE_SFS_DEV_MOUNT" "$p"
fi
if [ "$SAVE_DEV_MOUNT" ] && [ "$SAVEFILE_DEVICE" ]; then
	p="/mnt/${SAVEFILE_DEVICE##*/}"
	mkdir -p "$p"; mount -o bind "$SAVE_DEV_MOUNT" "$p"
fi
# b. setup /mnt/home. This is puppy-compatibility stuff, but we do earlier here for load_sfs.sh
rm -f /mnt/home
[ "$SAVE_DEV_MOUNT" ] && ln -sfT $SAVE_DEV_MOUNT /mnt/home
[ "$MULTI_MOUNT" ] && ln -sfT $MULTI_MOUNT /mnt/home

# load boot-time extra sfs
for p in $(echo "$extrasfs" | sed 's/,/ /g'); do load_extra_sfs $p; done; unset p; 

# load permanent extra sfs
/sbin/load_sfs.sh start

# pre-load modules, if any
[ "$NOT_USING_AUFS" ] ||
for p in $(sed 's/#.*//' $PRELOADED_MODULES); do echo Loading module $p; modprobe $p; done

# pre-load radeon/nouveau: workaround for kernel 3.7/3.8 module load race conditions
#lsmod | grep -q radeon || find /sys/devices/pci* -name uevent | xargs cat 2>/dev/null | 
#grep -q ^MODALIAS=pci:v00001002d0000 && echo Loading radeon module early && modprobe -b radeon
#lsmod | grep -q nouveau || find /sys/devices/pci* -name uevent | xargs cat 2>/dev/null | 
#grep -qE "^MODALIAS=pci:v000012D2d|^MODALIAS=pci:v000010DEd" && echo Loading nouveau module early && modprobe -b nouveau

# start udev
echo > /proc/sys/kernel/hotplug		# disable hotplug leftover from init
if ! [ -e /etc/udev/hwdb.bin ] || [ /etc/udev/hwdb.bin -ot /etc/udev/hwdb.d ]; then
	/sbin/udevadm hwdb --update # rebuild if missing or too old
fi
/sbin/udevd --daemon
/sbin/udevadm trigger --action=add  # prime udev database and load drivers
/sbin/udevadm settle                # wait until most drivers are loaded

# auto-activate swap if asked
grep -q autoswap /proc/cmdline && echo "Loading swap partitions ..." &&
blkid | awk -F: '/TYPE="swap"/ { print $1 }' | xargs swapon 


############### additional stuff ######################

# nls modules for accessing filesystems
modprobe nls_iso8859-1	# default western european codepage
modprobe nls_cp437		# us and canada codepage
modprobe nls_utf8		# joliet cd-roms

# load apm if acpi isn't available
[ ! -d /proc/acpi ] && modprobe apm #v406

#v408 kirk: Check for laptop battery...
modprobe battery
if ! echo /sys/class/power_supply/*/capacity | grep -qF "*"; then
	# battery exists, load other modules
	modprobe ac
	modprobe thermal
	modprobe fan
else
	rmmod battery
fi

# system id
[ -r /etc/os-release ] && . /etc/os-release
echo -e "${PRETTY_NAME}\n`uname -s` `uname -r` [`uname -m` arch]\n\n" > /etc/issue

# clean up empty mountpoints left over by filemnt and label-mount
rmdir /mnt/+*
[ -d /media ] && rmdir /media/*



############## start network and services ##############

### platform adjustments - do it before we configure network and services
[ -x /etc/rc.d/rc.platform ] && . /etc/rc.d/rc.platform

### start network
# set hostname - use unique hostname when nothing has been set
p=""; read p < /etc/hostname
case "$p" in ""|fatdog64-change-me)
	p=fatdog64-$(dmidecode -s system-uuid | md5sum | cut -c -3)
	echo  $p > /etc/hostname
	;;
esac
sed -i'' -e "1 {s/^.*$/127.0.0.1 localhost $p/}" /etc/hosts
hostname -F /etc/hostname

echo 1 > /proc/sys/net/ipv4/ip_dynaddr # for dial-up modem
/etc/rc.d/rc.network start &
echo $! > $RC_NETWORK_PID

### start services
/etc/rc.d/rc.services start &
echo $! > $RC_SERVICES_PID




################# puppy compatibility stuff ################

# device links - cdrom & dvd
rm -f /dev/cdrom /dev/dvd /etc/cdburnerdevice
while read cdrom canwrite isdvd; do
	! [ -e /dev/cdrom ] && ln -s /dev/$cdrom /dev/cdrom
	! [ -e /etc/cdburnerdevice ] && [ "$canwrite" = yes ] && echo $cdrom > /etc/cdburnerdevice
	! [ -e /dev/dvd ] && [ "$isdvd" = yes ] && { ln -s /dev/$cdrom /dev/dvd; hdparm -d 1 /dev/$cdrom; } > /dev/null
done << EOF
$(sed 's/\t\t*/|/g' /proc/sys/dev/cdrom/info | awk -F"|" '
/drive name/ { numdrives=NF-1; for (i=2; i<=NF; i++) drives[i-2]=$i; }
/Can write/ { for (i=2; i<=NF; i++) { capability["can_write" (i-2)]="no"; if ($i == "1") capability["can_write" (i-2)]="yes"; } }
/read DVD/ { for (i=2; i<=NF; i++) { capability["is_dvd" (i-2)]="no"; if ($i == "1") capability["is_dvd" (i-2)]="yes"; } }
END { for (i=0; i<numdrives; i++) print drives[i], capability["can_write" i], capability["is_dvd" i]; }
' | sort )
EOF

# device links - mouse
[ ! -f /etc/mousedevice ] && echo "input/mice" > /etc/mousedevice
read p < /etc/mousedevice && [ "$p" = "psaux" ] && echo "input/mice" > /etc/mousedevice
read p < /etc/mousedevice && ln -sfT /dev/$p /dev/mouse

# mouse drivers - moved here from xwin
grep -q 'ttyS' /etc/mousedevice && modprobe sermouse
grep -q 'input/mice' /etc/mousedevice && modprobe usbhid

### other little things
dmesg > /tmp/bootkernel.log # store initial bootlog
! [ -e /etc/ld.so.cache ] && echo "Generating ld.so.cache (one-time only) ..." && ldconfig

### dump compatibility stuff to /etc/rc.d/PUPSTATE
{
	echo "#  This file exists for compatibility with Puppy apps and scripts."
	echo "#  Fatdog-only applications don't use this file. Look in $BOOTSTATE_PATH instead."
	echo "#  Only important entries are emulated, the rest are left blank."
	
	#PUPMODE original bitmask
	#bit 0 = 1 tmpfs
	#bit 1 = 2 full install
	#bit 2 = 4 have base sfs
	#bit 4 = 8 have savefile
	#bit 6 = 64 multisession
	#common values= 2 (full install), 3 (full install on flash), 5 (first boot),
	# 12 (frugal), 13 (frugal on flash), 6 (full install), 7 (full install on flash), 77 (multisession)
	#
	# fake it - 12 or 13 (frugal install)
	if [ "$NOT_USING_AUFS" ]; then
		PUPMODE=2
		PMEDIA=atahd
	elif  [ "$TMPFS_MOUNT" ]; then # RAM layer is used
		[ "$SAVEFILE_MOUNT" ] && PUPMODE=13 || PUPMODE=5
		PMEDIA=ataflash
	else
		[ "$SAVEFILE_MOUNT" ] && PUPMODE=12
		PMEDIA=atahd
	fi
	echo PUPMODE=$PUPMODE
	
	# -- used during boot-up, we don't use these
	#PDEV1= boot device (where the base.sfs is located) 
	#we use basesfs if exist, otherwise just use save device
	PDEV1=$BASE_SFS_DEVICE; [ -z $PDEV1 ] && PDEV1=$SAVEFILE_DEVICE
	echo PDEV1=${PDEV1##*/}
	#DEV1FS= filesystem of PDEV1
	echo DEV1FS=$(guess_fstype $PDEV1)
	#PUPSFS='sda1,ext3,/fd64/fd64-521.sfs' - location of base.sfs 
	echo PUPSFS=
	#PUPSAVE='sda1,ext3,/fd64/fd64save-james.3fs' - location of savefile
	echo PUPSAVE=
	#PMEDIA='' hints for boot media type in use 
	#pmedia= usbflash|usbhd|usbcd|ataflash|atahd|atacd|atazip|scsihd|scsicd|cd
	echo PMEDIA=$PMEDIA # set when we decide about PUPMODE
	#pfix='noram' - all pfix parameters - used by /etc/profile
	echo pfix=$pfix
	#ATADRIVES= (list of sdx drives), SATADRIVES= (list of hdx drives)
	# - needed by probedisk2 to determine icon types
	echo ATADRIVES=\'$(ls /sys/block | sed '/^sd/ !d')\'

	# -- probably used by other scripts
	#SAVE_LAYER='/pup_rw' - where the savefile is mounted
	echo SAVE_LAYER=$SAVEFILE_MOUNT
	#PUP_LAYER='/pup_ro2' - where base.sfs is mounted
	echo PUP_LAYER=$BASE_SFS_MOUNT
	#PUP_HOME= where fd64save file is (/mnt/home link) without the /initrd part
	echo PUP_HOME=$(readlink /mnt/home) # not 100% compatible but what the heck
	
	# -- management of additional kernel modules sfs - we don't use this
	#ZDRV='' - name of addional kernel module sfs
	echo ZDRV=
	#ZDRVINIT='no' - we don't use it, leave it as no
	echo ZDRVINIT=no
	#PSAVEMARK='' - for ZDRV management, not used
	echo PSAVEMARK=
	#FASTPARTS='sda1|ext3 sda2|ext4 sda3|ntfs sda4|vfat ' - don't know what this is
	echo FASTPARTS=
} > /etc/rc.d/PUPSTATE




################# local bootscripts ################

# do it last, here, so fbcon has time to initialise, hopefully
process_waitdev
/sbin/udevadm settle # and make sure udev is settled once again
process_pkeys

### execute local boot script (rc.local) last so that it can override anything set previously
[ ! -f /etc/rc.d/rc.local.shutdown ] && cat > /etc/rc.d/rc.local.shutdown <<EOF
# this file called from rc.shutdown
# you can edit this file to perform custom shutdown activities
EOF
[ ! -f /etc/rc.d/rc.local ] && cat > /etc/rc.d/rc.local <<EOF
# this file called from rc.sysinit
# you can edit this file to perform custom startup activities
# When firewall is installed, will append lines to this file.
EOF
. /etc/rc.d/rc.local

### next: login shell (which will execute /etc/profile)
echo ==== end of rc.sysinit ==== 1>&2
