#!/bin/sh
### linuxrc script by brokenman <http://www.porteus.org>

export PATH=/bin:/usr/bin:./

# Source functions
. ./finit

# Enable pivot_root in initramfs to let it behave like initrd:
if [ -d /m ]; then
	mount -nt tmpfs tmpfs -o mode=0755 /m
	cp -a ??* /m 2>/dev/null
	exec switch_root /m linuxrc "$@"
fi

mount -nt proc proc /proc
mount -nt sysfs sysfs /sys
echo 0 >/proc/sys/kernel/printk

## Check for debug
if [ `grep -q debug /proc/cmdline` ]; then DBUG=0; fi

clear
echo -e "${BOLD}Porteus. Lightweight, lightning fast, portable.${RST}"
echo

# Variables:
i=`echo "[1;33m""^ ""[0m"`
m=`echo "[1;33m""->""[0m"`
arch=`uname -m`; [ $arch = x86_64 ] || arch=i586
CFG=`value cfgfile`; [ $CFG ] || CFG=porteus-v5.1-$arch.cfg
FROM=`value from`; ISO=`echo $FROM | egrep ".*\.iso( |\$)"`
IP=`value ip | cut -d: -f2`
MOPT=`value mopt`; [ $MOPT ] || MOPT="noatime,nodiratime,suid,dev,exec,async"
CHANGES=`value changes`
RAMSIZE=`value ramsize`; [ $RAMSIZE ] || RAMSIZE=60%
LOAD=`value load | sed 's/;/|/g'`; [ $LOAD ] || LOAD=._null
NOLOAD=`value noload | sed 's/;/|/g'`; [ $NOLOAD ] || NOLOAD=._null
EXTRAMOD=`value extramod | sed 's/;/ /g'`
RAMMOD=`value rammod | sed 's/;/|/g'`
ROOTCOPY=`value rootcopy`
FOLDER=porteus
livedbg=/var/log/livedbg
LOGFILE=/var/log/boot.log

## Let's start!
mount -nt devtmpfs none /dev

# Perform filesystem check:
if param fsck; then
	echo $i"performing linux filesystem check on all available devices:"
	sleep 3
	draw
	for x in `blkid | grep 'TYPE="ext' | cut -d: -f1`; do e2fsck -C 0 -p $x; wait; done
	draw
fi

# Create /etc/fstab and mount devices:
fstab

debug
# Find *.cfg file:
echo $i"searching for $CFG file"
if [ $IP ]; then BOOTDEV=network; CFGDEV=/mnt/nfs
	for x in `lspci | grep 0200: | cut -d: -f3-4 | sed s/:/.*/g | tr a-z A-Z`; do modprobe `grep $x /lib/modules/$(uname -r)/modules.alias | tail -n1 | rev | cut -d" " -f1 | rev` 2>/dev/null; done
	ls /sys/class/net | grep -q eth || { for x in `find /lib/modules/$(uname -r)/kernel/drivers/net -name "*.ko" | sed 's/.ko//g'`; do modprobe `basename $x` 2>/dev/null; ls /sys/class/net | grep -q eth && break || modprobe -r `basename $x` 2>/dev/null; done; }
	mkdir -p /mnt/nfs/porteus /mnt/nfs/storage; udhcpc; modprobe nfsv4; mount -t nfs4 $IP:/srv/pxe/porteus /mnt/nfs/porteus -o ro,nolock 2>/dev/null || { modprobe nfsv3; mount -t nfs $IP:/srv/pxe/porteus /mnt/nfs/porteus -o ro,nolock 2>/dev/null; }
	MAC=`ifconfig | grep eth0 | cut -d: -f5-7 | sed s/://g | cut -d" " -f1`
	if [ "$CHANGES" = /srv/pxe/storage ]; then
		if lsmod | grep -q nfsv3; then
			mount -t nfs $IP:/srv/pxe/storage /mnt/nfs/storage -o rw,nolock 2>/dev/null && { mkdir -p /mnt/nfs/storage/client-$MAC/changes/home; CHANGES="/storage/client-$MAC"; }
		else
			mount -t nfs4 $IP:/srv/pxe/storage /mnt/nfs/storage -o rw,nolock 2>/dev/null && { mkdir -p /mnt/nfs/storage/client-$MAC/changes/home; CHANGES="/storage/client-$MAC"; }
		fi
	fi
elif [ $ISO ]; then CFGDEV=/mnt/isoloop
	locate -e $FROM && { BOOTDEV=/mnt/$DEV; mkdir /mnt/isoloop; mount -o loop /mnt/$DEV/$LPTH /mnt/isoloop; ISOSRC=/mnt/$DEV/$LPTH; }
else
	if [ $FROM ]; then
		locate -e $FROM/porteus/$CFG
		if [ $? -eq 0 ]; then
			DIR=`echo $LPTH | rev | cut -d/ -f3- | rev`; [ $DIR ] && FOLDER=$DIR/porteus
		else
			echo -e "${YELLOW}from= cheatcode is incorrect, press enter to search through all devices${RST}"
			read -s; search -e porteus/$CFG
		fi
	else
		search -e porteus/$CFG || lazy -e porteus/$CFG
	fi
	CFGDEV=/mnt/$DEV
fi

[ -e $CFGDEV/$FOLDER/$CFG ] && PTH=$CFGDEV/$FOLDER || . fatal

# Set some variables to export as environment variables
DELAY=`value delay`; [ $DELAY ] && { echo $i"delaying $DELAY sec (waiting for slow devices to settle)"; sleep $DELAY; }
BOOTDEV=$CFGDEV
MODDIR=$PTH/modules
BASEDIR=${PTH%/*}
PORTDIR=$PTH
PORTCFG=$CFGDEV/$FOLDER/$CFG
echo $i"using Porteus data from $PTH"

# Make all drivers available:
mount -o loop $PTH/base/000-kernel.xzm /opt/000-kernel 2>/dev/null
mount -o bind /opt/000-kernel/lib/modules /lib/modules 2>/dev/null

# Create symlinks used often by porteus scripts:
if [ $CFGDEV = /mnt/nfs -o $CFGDEV = /mnt/isoloop ];then
	ln -sf /mnt/live$PTH/modules  /porteus/modules
	ln -sf /mnt/live$PTH/optional /porteus/optional
else
	ln -sf $PTH/modules  /porteus/modules
	ln -sf $PTH/optional /porteus/optional
fi

debug
# Setup changes:
if [ $CHANGES ]; then
	echo $i"setting up directory for changes"
	CHNEXIT=`echo $CHANGES | cut -d: -f1`; [ $CHNEXIT = EXIT ] && CHANGES=`echo $CHANGES | cut -d: -f2-`
	[ -r $CFGDEV/$CHANGES ] && { DEV=`echo $CFGDEV | sed s@/mnt/@@`; LPTH=$CHANGES; } || locate -r $CHANGES
	if [ $? -eq 0 ]; then
		if [ -d /mnt/$DEV/$LPTH ]; then
			mkdir -p /mnt/$DEV/$LPTH/changes 2>/dev/null && \
			mount -o bind /mnt/$DEV/$LPTH/changes /memory/changes && touch /memory/changes/._test1 2>/dev/null
		else
			if blkid /mnt/$DEV/$LPTH 2>/dev/null | cut -d" " -f3- | grep -q _LUKS; then
				for x in dm_crypt cryptd cbc sha256_generic aes_generic aes_x86_64; do modprobe $x 2>/dev/null; done
				losetup /dev/loop2 /mnt/$DEV/$LPTH
				echo $i"found encrypted .dat container"
				/opt/000-kernel/sbin/cryptsetup luksOpen /dev/loop2 crypt
				fsck_dat /dev/mapper/crypt
				mount /dev/mapper/crypt /memory/changes 2>/dev/null && touch /memory/changes/._test1 2>/dev/null
			else
				fsck_dat /mnt/$DEV/$LPTH
				mount -o loop /mnt/$DEV/$LPTH /memory/changes 2>/dev/null && touch /memory/changes/._test1 2>/dev/null
			fi
		fi
		if [ $? -eq 0 ]; then
			echo $i"testing filesystem on "$CHANGES" for posix compatibility"
			ln -s /memory/changes/._test1 /memory/changes/._test2 2>/dev/null && chmod +x /memory/changes/._test1 2>/dev/null && [ -x /memory/changes/._test1 ] && chmod -x /memory/changes/._test1 2>/dev/null && [ ! -x /memory/changes/._test1 ] && rm -f /memory/changes/._test1 /memory/changes/._test2
			if [ $? -ne 0 ]; then
				rm -f /memory/changes/._test1 /memory/changes/._test2; umount /memory/changes
				echo && echo -e "[1;33m""A Windows filesystem (FAT, NTFS) or other non-posix compatible filesystem\nhas been detected on $CHANGES.\nYour changes cannot be saved directly to the specified storage media with this\nsetup. Please use the '[1;36mPorteus save file manager[1;33m' to create a .dat container\nand use it for saving your changes after your next reboot.""[0m"
				echo "boot will continue in '[1;36mAlways Fresh[0m' mode for this session"
				sleep 10; CHGERR=1; rmdir /mnt/$DEV/$LPTH/changes; fail_chn
			else
				echo $i"filesystem is posix compatible"; CHNDEV=/mnt/$DEV
				rmdir /memory/changes/mnt/* 2>/dev/null
				rm -rf /memory/changes/var/lock/* /var/run/laptop-mode-tools/* /var/spool/cron/cron.??????
				for x in `find /memory/changes/var/run -name "*pid" 2>/dev/null`; do rm $x; done
				if [ $CHNEXIT = EXIT -o "`egrep -o " changes-ro( |\$)" /proc/cmdline`" ]; then
					CHNEXIT=$CHNDEV/$LPTH; echo $CHNEXIT >/tmp/changes-exit
					param changes-ro && echo $i"[1;36m""changes will not be saved for this session""[0m" || echo $i"[1;36m""changes will be saved only during reboot/shutdown""[0m"
					for x in `find /memory/changes -name ".wh.*" | grep -v '.wh..wh..opq' | tr ' ' '@' `; do x=`echo $x | tr '@' ' ' `; cp -a --parents "$x" /var; done
					umount /memory/changes; mount -nt tmpfs -o size=$RAMSIZE tmpfs /memory/changes
					## should not be needed with new busybox ## need to fix busybox bug on 'cp -a' as it does not preserve perms on dirs:
					mv /var/memory/changes/* /memory/changes 2>/dev/null; CHANGES=memory
					#chown -R guest /memory/changes/home/guest 2>/dev/null
				fi
			fi
		else
			echo $i"changes not writable, using memory instead"; CHGERR=2; umount /memory/changes 2>/dev/null; fail_chn
		fi
	else
		CHGERR=3; fail $CHANGES; fail_chn
	fi
else
	 echo $i"changes cheatcode not found, using memory only"; fail_chn
fi

mkdir -p /memory/changes/mnt/live

debug
# Setup aufs:
echo $i"creating live filesystem and inserting modules"
mount -t aufs -o nowarn_perm,xino=/memory/xino/.aufs.xino,br:/memory/changes=rw aufs /union
if [ $? -ne 0 ]; then echo -e "[31m""cant setup union (aufs) - read only filesystem?\nWhen you finish debugging press Ctrl+Alt+Del to reboot.""[0m"; sh; fi

# Check for base modules update
if [ -d $CFGDEV/$FOLDER/updates ]; then
	echo $i"[1;36m""Base updates detected ...""[0m"
	# Check if user has booted over network or from ISO
	if [ $ISO ]||[ $IP ]||[ "$CFGDEV" = /mnt/isoloop ]; then
		echo $i"Booting from ISO or network detected. Skipping updates ..."
	else
		if is_writable $CFGDEV/$FOLDER/base; then
			echo $i"Copying base updates ... please wait"
			basemods=`ls -1 $CFGDEV/$FOLDER/updates | grep "^00[1-3]-[a-z|A-Z]"`
			for x in $basemods; do
				echo "Copying: $x to $CFGDEV/$FOLDER/porteus/"
				cp $CFGDEV/$FOLDER/updates/$x $CFGDEV/$FOLDER/base/ 2>/dev/null
				rm $CFGDEV/$FOLDER/updates/$x
			done
			## Allow time slow devices?
			echo "Pausing, to allow the dust to settle.. please wait"
			sleep 10			
			rm -rf $CFGDEV/$FOLDER/updates && echo "Removed updates folder" || echo "Could not remove updates folder"
		else
			echo $i"Non writable media. Skipping updates ..."
		fi
	fi
fi

# Find modules:
find $PTH/base $PTH/modules -name "*.xzm" 2>/dev/null | egrep -ve "$NOLOAD" | sort >/tmp/modules
find $PTH/optional -name "*.xzm" 2>/dev/null | egrep -e "$LOAD" | sort >>/tmp/modules

if param vga_detect; then
	echo $i"detecting GPU"
	lspci >/tmp/lspci; nv=`grep "0300: 10de:" /tmp/lspci | cut -d":" -f4`; amd=`grep "0300: 1002:" /tmp/lspci | cut -d":" -f4`
	if [ $nv ]; then
		echo $i"$nv chipset found, checking which nvidia driver supports it"
		cd /usr/share/pciids/NVIDIA; NV=`grep $nv * | cut -d: -f1`; cd /
		if [ $NV ]; then
			echo $i"nvidia-$NV.xx driver will be activated -"
			echo $i"if present in $PTH/optional folder"
			find $PTH/optional -name "nvidia-$NV*" 2>/dev/null >>/tmp/modules
		else
			echo $i"latest nvidia driver will be activated -"
			echo $i"if present in $PTH/optional folder"
			find $PTH/optional -name "nvidia-*" 2>/dev/null | egrep -v '96.43.|173.14.|304.' >>/tmp/modules
		fi
	elif [ $amd ]; then
		echo $i"checking if $amd GPU belongs to 'Radeon HD' series"
		cd /usr/share/pciids/AMD; HD=`grep $amd * | cut -d: -f1`; cd /
		if [ $HD ]; then
			echo $i"$HD-xx driver will be activated -"
			echo $i"if present in $PTH/optional folder"
			find $PTH/optional -name "$HD-*" 2>/dev/null >>/tmp/modules
		else
			echo $i"$amd GPU is not supported by amd-catalyst driver - refusing activation"
		fi
	else
		echo $i"could not find any nVidia/AMD GPU on this PC"
	fi
fi

if param base_only; then
	grep base/0 /tmp/modules > /tmp/mod
	mv /tmp/mod /tmp/modules
else
	if [ "$EXTRAMOD" ]; then
	for folder in $EXTRAMOD; do
		echo $i"searching for additional modules in $folder"
		locate -d $folder && { find /mnt/$DEV/$LPTH -name "*.xzm" 2>/dev/null | egrep -ve "$NOLOAD" | sort >>/tmp/modules; } || fail $folder
	done
	fi
fi

# Copy data to RAM:
if param copy2ram; then
	echo $i"copying Porteus data to RAM, this may take some time..."
	[ $RAMMOD ] && { egrep -e "$RAMMOD" /tmp/modules > /tmp/rammod; cpmod /tmp/rammod; } || cpmod /tmp/modules
fi

# Populate aufs with modules:
umount /lib/modules /opt/000-kernel 2>/dev/null
while read line; do
	NAME=`basename "$line"`
	mkdir /memory/images/"$NAME"
	mount -o loop "$line" /memory/images/"$NAME" 2>/dev/null
	if [ $? -eq 0 ]; then
		echo "  $m  $NAME"; mount -no remount,add=1:/memory/images/"$NAME"=rr aufs /union
	else
		echo $i"[1;36m""Cannot read $NAME - corrupted module?""[0m"; rmdir /memory/images/"$NAME"
	fi
done < /tmp/modules
mount -no bind /union/lib/modules /lib/modules 2>/dev/null

# Add "changes on exit" device/file/folder:
if [ -e /tmp/changes-exit ]; then
	mkdir /memory/images/changes
	if [ -d $CHNEXIT ]; then
		mount -o bind $CHNEXIT/changes /memory/images/changes
	elif [ -b /dev/mapper/crypt ]; then
		mount /dev/mapper/crypt /memory/images/changes
	else
		mount -o loop $CHNEXIT /memory/images/changes
	fi
	echo "  $m  changes"; mount -no remount,add=1:/memory/images/changes=ro aufs /union
	echo $CHNEXIT/changes >>/tmp/modules
	param changes-ro && rm /tmp/changes-exit
fi

debug
# Copy /rootcopy folder:
if param norootcopy; then
	ROOTCOPY=none
	echo $i"skipping /rootcopy directory"
else
	if [ $ROOTCOPY ]; then
		locate -d $ROOTCOPY
		if [ $? -eq 0 ]; then echo $i"copying content of $ROOTCOPY directory"; cp -af /mnt/$DEV/$LPTH/. /union/. 2>/dev/null; else fail $ROOTCOPY; ROOTCOPY=none; fi
	else
		ROOTCOPY=none
		echo $i"copying content of $PTH/rootcopy directory"
		cp -af $PTH/rootcopy/. /union/. 2>/dev/null
	fi
fi

uep=/union/etc/profile.d
uepp=$uep/porteus.sh
[ ! -d $uep ] && mkdir -p $uep
#[ ! `grep -o "DISTRO=" $uepp >/dev/null 2>&1` ] && echo "export DISTRO=porteus" >> $uepp
if [ -e $uepp ]; then
    sed -i '/DISTRO/d' $uepp
	sed -i '/BOOTDEV/d' $uepp
	sed -i '/BASEDIR/d' $uepp
	sed -i '/PORTDIR/d' $uepp
	sed -i '/MODDIR/d' $uepp
	sed -i '/CHGERR/d' $uepp
	sed -i '/PORTCFG/d' $uepp
fi
echo "export DISTRO=porteus" >> $uepp
echo "export BOOTDEV=$CFGDEV" >> $uepp
echo "export BASEDIR=${PTH%/*}" >> $uepp
echo "export PORTDIR=$PTH" >> $uepp
echo "export MODDIR=$MODDIR" >> $uepp
echo "export PORTCFG=$PORTCFG" >> $uepp
if [ $CHGERR ]; then echo "export CHGERR=$CHGERR" >> $uepp; fi
chmod +x $uepp

# Collect boot arguments
grep "^[aA0-zZ9]" $PORTCFG > /union/etc/bootcmd.cfg
cat /proc/cmdline | tr ' ' '\n' >> /union/etc/bootcmd.cfg

## Finish:
# Create 7 free loop devices for truecrypt, etc...
#x=`losetup | tail -n1 | cut -d: -f1 | sed 's@/dev/loop@@'`
x=`grep -oE 'loop[0-9]+$' /proc/partitions  | tail -n1 | tr -d [:alpha:]`
let y=x+7
while [ $x -le $y ]; do [ -b /dev/loop$y ] && break || mknod /dev/loop$y b 7 $y; let y=y-1; done

if param nonetwork; then
	echo $i"disabling dhcpcd and NetworkManager services"
	chmod -x /union/etc/rc.d/rc.inet1 /union/etc/rc.d/rc.networkmanager 2>/dev/null
	nma=/union/etc/xdg/autostart/nm-applet.desktop
	test -e $nma && ! grep -q "Hidden=true" $nma && echo "Hidden=true" >> $nma
fi

if [ "$IP" -a -x /union/etc/rc.d/rc.networkmanager ]; then
	#if [ -z "`egrep -o "copy2ram( |\$)" /proc/cmdline`" -o -d /mnt/nfs/storage/client-$MAC ]; then
	if [ -z "`egrep -o "^copy2ram" /union/etc/bootcmd.cfg`" -o -d /mnt/nfs/storage/client-$MAC ]; then
		echo "nameserver $IP" > /union/etc/resolv.conf
		nmc=/union/etc/NetworkManager/NetworkManager.conf; HW=`ifconfig | grep eth0 | cut -dW -f2 | cut -d" " -f2`
		! grep -q "unmanaged-devices=mac:$HW" $nmc && sed -i '/\[keyfile\]/ a\unmanaged-devices=mac:'$HW'' $nmc
	fi
fi

# Start bluetooth if cheat exists
if param bluetooth; then
	echo "Starting bluetooth ..."
	[ -e /union/etc/rc.d/rc.bluetooth ] && chmod +x /union/etc/rc.d/rc.bluetooth
fi

cp -af /dev/console /union/dev
#cat > /union/etc/mtab << EOF
#aufs / aufs rw 0 0
#proc /proc proc rw 0 0
#sysfs /sys sysfs rw 0 0
#devtmpfs /dev devtmpfs rw 0 0
#devpts /dev/pts devpts rw,mode=0620,gid=5 0 0
#EOF

fstab

debug
#if param copy2ram; then
if chk_bootcfg copy2ram; then
echo $i"[1;36m""finished copying to RAM - booting media can be removed safely""[0m"
fi

# Create debug file:
[ -e /tmp/devices ] && { echo "# Recognized devices:" >$livedbg; cat /tmp/devices >>$livedbg; }
[ $BOOTDEV ] && CFGDEV=$BOOTDEV
echo -e "\n# Booting device:\n$CFGDEV\n\n# Porteus data found in:\n$PTH\n\n# Changes are stored in:\n$CHANGES\n\n# Non standard /rootcopy dir:\n$ROOTCOPY\n\n# Modules activated during boot time:" >>$livedbg; cat /tmp/modules >>$livedbg
echo -e "\n# Base module versions:" >>$livedbg; cat /union/etc/porteus/* /etc/porteus/* >>$livedbg
grep "^/mnt/isoloop" $livedbg && echo "" >> $livedbg && echo "ISO=$ISOSRC" >> $livedbg
#if [ $ISO ]; then
#	[ -d /union/mnt/isoloop ] && rmdir /union/mnt/isoloop
#	ln -sf /mnt/live/mnt/isoloop /union/mnt/isoloop
#fi
cp -af $livedbg /union/var/log/porteus-livedbg

## Check for text mode cheat code
chk_bootcfg 3 && export OPTS="3"

echo $i"changing root directory"

# Cleanup mount points at union/mnt for this session
for x in `ls -1 /union/mnt | grep -v "live"`; do
 [ -d /union/mnt/$x ] && rmdir /union/mnt/$x # Will fail if dir not empty!
 [ -L /union/mnt/$x ] && rm -f /union/mnt/$x
done

if chk_bootcfg noauto; then
	for x in `grep /mnt/ /etc/fstab | cut -d/ -f3`; do mkdir -p /union/mnt/$x; umount -n /mnt/$x 2>/dev/null && rmdir /mnt/$x; done
else
	#grep /mnt/ /etc/fstab >> /union/etc/mtab
	for x in `grep /mnt/ /etc/fstab | cut -d/ -f3`; do mkdir -p /union/mnt/$x; mount -n --move /mnt/$x /union/mnt/$x; rmdir /mnt/$x; done
fi

sed -i 's/ ntfs / ntfs-3g /g' /etc/fstab
cp -f /etc/fstab /union/etc 2>/dev/null

# Add all symlinks of all mount points at /mnt to union
for x in `ls -1 /mnt`; do
 [ -d /union/mnt/$x ] && rmdir /union/mnt/$x
 ln -sf /mnt/live/mnt/$x /union/mnt/$x
done

cp -r /etc/porteus /union/etc 2>/dev/null
umount -n /lib/modules 2>/dev/null
rm -r /lib/* /usr/*

debug

echo "[1m""live system is ready now - starting Porteus""[0m"
cp -f /union/sbin/init /bin
if [ $? -eq 0 ]; then
	pivot_root /union  /union/mnt/live
	exec bin/chroot . /mnt/live/bin/init "$@" $OPTS <dev/console >dev/console 2>&1
else
	echo -e "[31m""!!ERROR!!\nSomething went wrong and I cannot continue.\nPress Ctrl+Alt+Del to reboot.""[0m"
	sh
fi
