#! /bin/sh -
#
# Boot-time system initialization script
#
# Copyright (c) 2017-2022 Matias Fonzo, <selk@dragora.org>.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

RC="[${0##*/}]"	# To reflect the name of the script between [].
umask 022
IFS='
 	'
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:/bin:/usr/bin
LC_ALL=C

### Mount virtual file systems

echo "${RC}: Mounting kernel based file systems"
mount -v -n -o nosuid,noexec,nodev -t proc proc /proc
mount -v -n -o nosuid,noexec,nodev -t sysfs sysfs /sys
if ! mountpoint -q /dev
then
    mount -v -n -t devtmpfs devtmpfs /dev
fi
mkdir -p /dev/pts /dev/shm /run
mount -v -n -o mode=0620,gid=5 -t devpts devpts /dev/pts
mount -v -n -o defaults -t tmpfs devshm /dev/shm
mount -v -n -o defaults -t tmpfs tmpfs /run
mkdir -p /run/lock
chmod 1777 /run/lock /dev/shm

### ^ End Of 'Mount virtual file systems'

### Start the dynamic device management

echo "${RC}: Starting event managing daemon: udevd --daemon"
udevd --daemon

# Request device uevents to replay events at system coldplug
udevadm trigger --type=subsystems --action=add
udevadm trigger --type=devices    --action=add
udevadm trigger --type=devices    --action=change

echo "${RC}: Waiting for pending udev events: udevadm settle --timeout=80"
udevadm settle --timeout=80

### ^ End Of 'Start the dynamic device management'

# Re-mount the root filesystem in *read-only mode*, if needed
if test -w /
then
    echo "${RC}: Remounting root filesystem in read-only mode"
    mount -v -n -o remount,ro /
fi

### Mount control groups

if grep -q cgroup /proc/filesystems
then
    if test -d /sys/fs/cgroup
    then
        echo "${RC}: Mounting Cgroup controllers (version 1)"

        # Cgroup controllers v1 must be mounted against a tmpfs(5)
        mount -v -n -t tmpfs cgroup_root /sys/fs/cgroup

        # Comount all v1 controllers against the same hierarchy
        mount -v -n -t cgroup cgroup /sys/fs/cgroup
    fi
fi

### ^ End Of 'Mount control groups'

### Initialization of the Logical Volume Manager (version 2)

if lvm version
then
    echo "${RC}: LVM2: Scanning for new volume groups"
    modprobe dm-mod
    vgscan --mknodes --ignorelockingfailure 2> /dev/null && \
    vgchange --ignorelockingfailure -a y
fi

### ^ End Of 'Initialization of the Logical Volume Manager (version 2)'

# Activate swap partition if any available
swapon -a

### Set system clock (1/2)

# Avoid unnecessary fsck runs at boot time, especially for
# already existing (multiple) systems not designed for UTC
hwclock --hctosys --localtime

### Perform file system checks

echo "${RC}: Performing file system checks"
if test ! -f /fastboot
then
    # Limit number of filesystem checkers using the max. number of processors
    FSCK_MAX_INST=$(nproc 2> /dev/null) || FSCK_MAX_INST=1

    # Force checks if the regular file '/forcefsck' exists
    test -f /forcefsck && forcefsck_flag=-f

    echo "*** Checking root file system"
    fsck $forcefsck_flag -a -C
    status=$?

    if test $status -gt 1
    then
        if test $status -eq 32
        then
            echo "fsck(8) canceled by user request." 1>&2
        else
            if test $status -ge 4
            then
                printf '%s' 1>&2 \
"^ Return status = $status
"'A maintenance shell will be started ...

If you do not know how to correct the file system errors,
please see the filesystem-specific checker manual pages for
further details.
'
                unset -v FSCK_MAX_INST forcefsck_flag status
                sulogin || {
                    echo "Executing \`/bin/sh' as emergency shell ..." 1>&2
                    /bin/sh -
                }
            fi
            echo "The system will be restarted now."
            umount -v -n -a -r
            mount -v -n -o remount,ro /
            reboot -n -f;
            exit 99
        fi
    fi
    unset -v status

    echo "*** Checking local file system(s)"
    fsck $forcefsck_flag -A -C -R -T -a

    unset -v FSCK_MAX_INST forcefsck_flag
else
    echo "WARNING: /fastboot is present: Ignoring checks" 1>&2
fi

### ^ End Of 'Perform file system checks'

### Parse config file

# The given values on /proc/cmdline have priority
# over those present at "/etc/rc.conf"
if grep -q -m 1 RC_ /proc/cmdline
then
    tr -s '[:space:]' '[\n*]' < /proc/cmdline | \
     while IFS='=' read -r variable value
     do
         case $variable in
         RC_?*)
             # Set variable value avoiding possible code execution
             eval "$variable=\${value}"
             ;;
         esac
     done
fi
if test -f /etc/rc.conf
then
    while IFS='=' read -r variable value
    do
        case $variable in
        RC_?*)
            if test -z "$variable"
            then
                eval "$variable=\${value}"
            fi
            ;;
        esac
    done < /etc/rc.conf
fi

# Use default values if the following variables are not defined

RC_HWCLOCK="${RC_HWCLOCK:-utc}"
RC_HOSTNAME="${RC_HOSTNAME:-dragora}"
RC_ESPEAKUP="${RC_ESPEAKUP:-off}"
RC_EVOICE="${RC_ESPEAKUP_VOICE:-default}"
RC_KEYMAP="${RC_KEYMAP:-qwerty/us}"
RC_BLANKTIME="${RC_BLANKTIME:-15}"

### ^ End Of 'Parse config file'

### Set system clock (2/2)

if test "$RC_HWCLOCK" = utc
then
    echo "${RC}: Setting system time from the hardware clock to UTC"
    hwclock --hctosys --utc
else
    echo "${RC}: The system time was already set to local time" 1>&2
fi
unset -v RC_HWCLOCK

### ^ End Of 'Set system clock'

# Re-mount the root filesystem in *read-write mode*

echo "${RC}: Remounting root filesystem in read-write mode ..."
if ! test -w /
then
    mount -v -n -o remount,rw /
else
    echo "    The root filesystem is already in read-write mode." 1>&2
fi

# Mount local file systems only

echo "${RC}: Mounting of local file systems declared in /etc/fstab"
mount -v -a -t no,proc,sysfs,devtmpfs,shm,devpts,usbfs,cifs,smbfs,nfs

# Activate swap partitions or swap files

echo "${RC}: Activating swap devices, if any ..."
swapon -v -a

# Update kernel modules

if test -s "/lib/modules/$(uname -r)/modules.dep"
then
    echo "${RC}: Refreshing kernel module list: depmod --quick"
    depmod --quick
else
    echo "${RC}: Generating kernel module dependency list: depmod --all"
    depmod --all
fi

# Re-create hierarchy for temporary files (if needed)

mkdir -p /tmp /tmp/.font-unix /tmp/.ICE-unix /tmp/.X11-unix
chmod 1777 /tmp /tmp/.font-unix /tmp/.ICE-unix /tmp/.X11-unix

# Re-create file records for logins and logouts
touch \
 /var/log/btmp /var/log/lastlog /var/log/faillog /var/log/wtmp /var/run/utmp
chgrp utmp /var/log/lastlog /var/run/utmp
chmod 664 /var/log/lastlog /var/run/utmp
chmod 600 /var/log/btmp

# Seek and destroy temporary files

rm -f /etc/nologin /fastboot /forcefsck

# Set default system host name

echo "$RC_HOSTNAME" > /proc/sys/kernel/hostname
unset -v RC_HOSTNAME

# Set kernel parameters at runtime

echo "${RC}: Setting kernel parameters: sysctl -q --system"
sysctl -q --system

# Start the loopback device

echo "${RC}: Starting loopback device ..."
if ip -Version
then
    ip addr del 127.0.0.1/8 dev lo 2> /dev/null
    ip addr add 127.0.0.1/8 dev lo brd + scope host
    ip link set lo up
elif ifconfig --version
then
    ifconfig lo up
fi

# Initialize the random number generator, see random(4)

echo "${RC}: Initializing random number generator"
echo "    /etc/random-seed <-> /dev/urandom ..."

touch /etc/random-seed
if test -s /etc/random-seed
then
    cat /etc/random-seed > /dev/urandom
fi

# Get the pool size from /proc or use a default value
size="$(cat /proc/sys/kernel/random/poolsize)" || size=512

# Save seed file containing the whole entropy pool
dd if=/dev/urandom of=/etc/random-seed count=1 bs=$size
chmod 600 /etc/random-seed; unset -v size

### Configure ISA Plug-and-Play devices (legacy)

if command -v isapnp > /dev/null && command -v pnpdump > /dev/null
then
    if test -e /etc/isapnp.conf
    then
        echo "${RC}: Setting ISA PnP devices using /etc/isapnp.conf"
        isapnp /etc/isapnp.conf
    else
        echo "${RC}: Creating resource information for ISA PnP devices"
        pnpdump > /etc/isapnp.conf

        echo "${RC}: Setting devices from the new /etc/isapnp.conf"
        isapnp /etc/isapnp.conf
    fi
fi

### ^ End Of 'Configure ISA Plug-and-Play devices (legacy)'

# Start sound or speech engine

if test -x /etc/rc.d/rc.alsa
then
    /etc/rc.d/rc.alsa start
fi
if test "$RC_ESPEAKUP" != off
then
    echo "${RC}: Activating software synthesizer: espeakup -V $RC_ESPEAKUP_VOICE"
    modprobe speakup_soft
    espeakup -V "$RC_ESPEAKUP_VOICE"
fi
unset -v RC_ESPEAKUP RC_ESPEAKUP_VOICE

# Load keyboard map, set terminal attributes

echo "${RC}: Loading keyboard map: loadkeys $RC_KEYMAP"
loadkeys "$RC_KEYMAP"
unset -v RC_KEYMAP

echo "${RC}: Setting screen blanking interval: setterm -blank $RC_BLANKTIME"
setterm -blank "$RC_BLANKTIME"
unset -v RC_BLANKTIME

