#	$OpenBSD: rc,v 1.486 2016/07/10 09:08:18 rpe Exp $

# System startup script run by init on autoboot or after single-user.
# Output and error are redirected to console by init, and the console is the
# controlling terminal.

# Turn off Strict Bourne shell.
set +o sh

# Subroutines (have to come first).


# Strip in- and whole-line comments from a file.
# Strip leading and trailing whitespace if IFS is set.
# Usage: stripcom /path/to/file
stripcom() {
	local _file=$1 _line

	[[ -s $_file ]] || return

	while read _line ; do
		_line=${_line%%#*}
		[[ -n $_line ]] && print -r -- "$_line"
	done <$_file
}

# Update resource limits based on login.conf settings.
# Usage: update_limit -flag capability
update_limit() {
	local _flag=$1		# ulimit flag
	local _cap=$2 _val	# login.conf capability and its value
	local _suffix

	for _suffix in {,-cur,-max}; do
		_val=$(getcap -f /etc/login.conf -s ${_cap}${_suffix} daemon 2>/dev/null)
		[[ -n $_val ]] || continue
		[[ $_val == infinity ]] && _val=unlimited

		case $_suffix in
		-cur)	ulimit -S $_flag $_val
			;;
		-max)	ulimit -H $_flag $_val
			;;
		*)	ulimit $_flag $_val
			return
			;;
		esac
	done
}

# Apply sysctl.conf(5) settings.
sysctl_conf() {
	stripcom /etc/sysctl.conf |
	while read _line; do
		sysctl "$_line"

		case $_line in
		kern.maxproc=*)
			update_limit -p maxproc;;
		kern.maxfiles=*)
			update_limit -n openfiles;;
		esac
	done
}

# Apply mixerctl.conf(5) settings.
mixerctl_conf() {
	stripcom /etc/mixerctl.conf |
	while read _line; do
		mixerctl -q "$_line" 2>/dev/null
	done
}

# Apply wsconsctl.conf(5) settings.
wsconsctl_conf() {
	[[ -x /sbin/wsconsctl ]] || return

	stripcom /etc/wsconsctl.conf |
	while read _line; do
		eval "wsconsctl $_line"
	done
}

random_seed() {
	# push the old seed into the kernel
	dd if=/var/db/host.random of=/dev/random bs=65536 count=1 status=none
	chmod 600 /var/db/host.random
	# ... and create a future seed
	dd if=/dev/random of=/var/db/host.random bs=65536 count=1 status=none
	# and create a seed file for the boot-loader
	dd if=/dev/random of=/etc/random.seed bs=512 count=1 status=none
	chmod 600 /etc/random.seed
}

# Populate net.inet.(tcp|udp).baddynamic with the contents of /etc/services so
# as to avoid randomly allocating source ports that correspond to well-known
# services.
# Usage: fill_baddynamic tcp|udp
fill_baddynamic() {
	local _service=$1
	local _sysctl="net.inet.${_service}.baddynamic"

	stripcom /etc/services |
	{
		_ban=
		while IFS=" 	/" read _name _port _srv _junk; do
			[[ $_srv == $_service ]] || continue

			_ban="${_ban:+$_ban,}+$_port"

			# Flush before argv gets too long
			if ((${#_ban} > 1024)); then
				sysctl -q "$_sysctl=$_ban"
				_ban=
			fi
		done
		[[ -n $_ban ]] && sysctl -q "$_sysctl=$_ban"
	}
}

# Start daemon using the rc.d daemon control scripts.
# Usage: start_daemon daemon1 daemon2 daemon3
start_daemon() {
	local _daemon

	for _daemon; do
		eval "_do=\${${_daemon}_flags}"
		[[ $_do != NO ]] && /etc/rc.d/${_daemon} start
	done
}

# Generate keys for isakmpd, iked and sshd if they don't exist yet.
make_keys() {
	local _isakmpd_key=/etc/isakmpd/private/local.key
	local _isakmpd_pub=/etc/isakmpd/local.pub
	local _iked_key=/etc/iked/private/local.key
	local _iked_pub=/etc/iked/local.pub

	if [[ ! -f $_isakmpd_key ]]; then
		echo -n "openssl: generating isakmpd/iked RSA keys... "
		if openssl genrsa -out $_isakmpd_key 2048 >/dev/null 2>&1 &&
			chmod 600 $_isakmpd_key &&
			openssl rsa -out $_isakmpd_pub -in $_isakmpd_key \
			    -pubout >/dev/null 2>&1; then
			echo done.
		else
			echo failed.
		fi
	fi

	if [[ ! -f $_iked_key ]]; then
		# Just copy the generated isakmpd key
		cp $_isakmpd_key $_iked_key
		chmod 600 $_iked_key
		cp $_isakmpd_pub $_iked_pub
	fi

	ssh-keygen -A
}

# Re-link libraries, placing the objects in a random order.
reorder_libs() {
	local _l _liba _libas _tmpdir _remount=false _error=false
	local _dkdev=$(df /usr/lib | sed '1d;s/ .*//')
	local _mp=$(mount | grep "^$_dkdev")

	# Skip if /usr/lib is on a nfs mounted filesystem.
	[[ $_mp == *' type nfs '* ]] && return

	echo -n 'reordering libraries:'

	# Only choose the latest version of the libraries.
	for _liba in /usr/lib/libc.so.*.a; do
		_liba=$(ls ${_liba%%.[0-9]*}*.a | sort -V | tail -1)
		for _l in $_libas; do
			[[ $_l == $_liba ]] && continue 2
		done
		_libas="$_libas $_liba"
	done

	# Remount read-write, if /usr/lib is on a read-only ffs filesystem.
	if [[ $_mp == *' type ffs '*'read-only'* ]]; then
		if mount -u -w $_dkdev; then
			_remount=true
		else
			echo ' failed.'
			return
		fi
	fi

	for _liba in $_libas; do
		_tmpdir=$(mktemp -dq /tmp/_librebuild.XXXXXXXXXXXX) && (
			set -o errexit
			_lib=${_liba#/usr/lib/}
			_lib=${_lib%.a}
			cd $_tmpdir
			ar x ${_liba}
			cc -shared -o $_lib $(ls *.so | sort -R) $(cat .ldadd)
			[[ -s $_lib ]] && file $_lib | fgrep -q 'shared object'
			LD_BIND_NOW=1 LD_LIBRARY_PATH=$_tmpdir awk 'BEGIN {exit 0}'
			install -F -S -o root -g bin -m 0444 $_lib /usr/lib/$_lib
		) || { _error=true; break; }
	done

	rm -rf /tmp/_librebuild.*

	# Restore previous mount state if it was changed.
	if $_remount; then
		mount -u -r $_dkdev || _error=true
	fi

	if $_error; then
		echo ' failed.'
	else
		echo ' done.'
	fi
}

# Run rc.* script and email output to root.
# Usage: run_upgrade_script firsttime|sysmerge
run_upgrade_script() {
	local _suffix=$1
	[[ -n $_suffix ]] || return 1
	if [[ -f /etc/rc.$_suffix ]]; then
		mv /etc/rc.$_suffix /etc/rc.$_suffix.run
		. /etc/rc.$_suffix.run 2>&1 | tee /dev/tty |
			mail -Es "$(hostname) rc.$_suffix output" root >/dev/null
	fi
	rm -f /etc/rc.$_suffix.run
}

# Check filesystems, optionally by using a fsck(8) flag.
# Usage: do_fsck [-flag]
do_fsck() {
	fsck -p "$@"
	case $? in
	0)	;;
	2)	exit 1
		;;
	4)	echo "Rebooting..."
		reboot
		echo "Reboot failed; help!"
		exit 1
		;;
	8)	echo "Automatic file system check failed; help!"
		exit 1
		;;
	12)	echo "Boot interrupted."
		exit 1
		;;
	130)	# Interrupt before catcher installed.
		exit 1
		;;
	*)	echo "Unknown error; help!"
		exit 1
		;;
	esac
}

# End subroutines.

stty status '^T'

# Set shell to ignore SIGINT (2), but not children; shell catches SIGQUIT (3)
# and returns to single user after fsck.
trap : 2
trap : 3	# Shouldn't be needed.

export HOME=/
export INRC=1
export PATH=/sbin:/bin:/usr/sbin:/usr/bin

# Must set the domainname before rc.conf, so YP startup choices can be made.
if [[ -s /etc/defaultdomain ]]; then
	domainname "$(stripcom /etc/defaultdomain)"
fi

# Need to get local functions from rc.subr.
FUNCS_ONLY=1 . /etc/rc.d/rc.subr

# Load rc.conf into scope.
_rc_parse_conf

if [[ $1 == shutdown ]]; then
	if echo 2>/dev/null >>/var/db/host.random || \
	    echo 2>/dev/null >>/etc/random.seed; then
		random_seed
	else
		echo warning: cannot write random seed to disk
	fi

	# If we are in secure level 0, assume single user mode.
	if (($(sysctl -n kern.securelevel) == 0)); then
		echo 'single user: not running shutdown scripts'
	else
		pkg_scripts=${pkg_scripts%%*( )}
		if [[ -n $pkg_scripts ]]; then
			echo -n 'stopping package daemons:'
			while [[ -n $pkg_scripts ]]; do
				_d=${pkg_scripts##* }
				pkg_scripts=${pkg_scripts%%*( )$_d}
				[[ -x /etc/rc.d/$_d ]] && /etc/rc.d/$_d stop
			done
			echo '.'
		fi

		[[ -f /etc/rc.shutdown ]] && sh /etc/rc.shutdown
	fi

	# Bring carp interfaces down gracefully.
	ifconfig | while read _if _junk; do
		[[ $_if == carp+([0-9]): ]] && ifconfig ${_if%:} down
	done

	exit 0
fi

# Add swap block-devices.
swapctl -A -t blk

if [[ -e /fastboot ]]; then
	echo "Fast boot: skipping disk checks."
elif [[ $1 == autoboot ]]; then
	echo "Automatic boot in progress: starting file system checks."
	do_fsck
fi

trap "echo 'Boot interrupted.'; exit 1" 3

umount -a >/dev/null 2>&1
mount -a -t nonfs,vnd
mount -uw /		# root on nfs requires this, others aren't hurt.
rm -f /fastboot		# XXX (root now writeable)

# Set flags on ttys.  (Do early, in case they use tty for SLIP in netstart.)
echo 'setting tty flags'
ttyflags -a

# Set keyboard encoding.
if [[ -x /sbin/kbd && -s /etc/kbdtype ]]; then
	kbd "$(cat /etc/kbdtype)"
fi

wsconsctl_conf

# Set initial temporary pf rule set.
if [[ $pf != NO ]]; then
	RULES="block all"
	RULES="$RULES\npass on lo0"
	RULES="$RULES\npass in proto tcp from any to any port ssh keep state"
	RULES="$RULES\npass out proto { tcp, udp } from any to any port domain keep state"
	RULES="$RULES\npass out inet proto icmp all icmp-type echoreq keep state"
	RULES="$RULES\npass out inet proto udp from any port bootpc to any port bootps"
	RULES="$RULES\npass in inet proto udp from any port bootps to any port bootpc"
	if ifconfig lo0 inet6 >/dev/null 2>&1; then
		RULES="$RULES\npass out inet6 proto icmp6 all icmp6-type neighbrsol"
		RULES="$RULES\npass in inet6 proto icmp6 all icmp6-type neighbradv"
		RULES="$RULES\npass out inet6 proto icmp6 all icmp6-type routersol"
		RULES="$RULES\npass in inet6 proto icmp6 all icmp6-type routeradv"
		RULES="$RULES\npass out inet6 proto udp from any port dhcpv6-client to any port dhcpv6-server"
		RULES="$RULES\npass in inet6 proto udp from any port dhcpv6-server to any port dhcpv6-client"
	fi
	RULES="$RULES\npass in proto carp keep state (no-sync)"
	RULES="$RULES\npass out proto carp !received-on any keep state (no-sync)"
	if [[ $(sysctl vfs.mounts.nfs 2>/dev/null) == *[1-9]* ]]; then
		# Don't kill NFS.
		RULES="set reassemble yes no-df\n$RULES"
		RULES="$RULES\npass in proto { tcp, udp } from any port { sunrpc, nfsd } to any"
		RULES="$RULES\npass out proto { tcp, udp } from any to any port { sunrpc, nfsd } !received-on any"
	fi
	print -- "$RULES" | pfctl -f -
	pfctl -e
fi

# Fill net.inet.(tcp|udp).baddynamic lists from /etc/services.
fill_baddynamic udp
fill_baddynamic tcp

sysctl_conf

echo 'starting network'

# Set carp interlock by increasing the demotion counter.
# Prevents carp from preempting until the system is booted.
ifconfig -g carp carpdemote 128

# Recover resolv.conf in case dhclient died hard.
if [[ -f /etc/resolv.conf.save ]]; then
	mv -f /etc/resolv.conf.save /etc/resolv.conf
	touch /etc/resolv.conf
fi

sh /etc/netstart

dmesg >/dev/random	# Any write triggers a rekey.

# Load pf rules and bring up pfsync interface.
if [[ $pf != NO ]]; then
	if [[ -f /etc/pf.conf ]]; then
		pfctl -f /etc/pf.conf
	fi
	if [[ -f /etc/hostname.pfsync0 ]]; then
		sh /etc/netstart pfsync0
	fi
fi

mount -s /usr >/dev/null 2>&1
mount -s /var >/dev/null 2>&1

random_seed

reorder_libs

# Clean up left-over files.
rm -f /etc/nologin /var/spool/lock/LCK.* /var/spool/uucp/STST/*
(cd /var/run && { rm -rf -- *; install -c -m 664 -g utmp /dev/null utmp; })
(cd /var/authpf && rm -rf -- *)

dmesg >/var/run/dmesg.boot	# Save a copy of the boot messages.

make_keys

echo -n 'starting early daemons:'
start_daemon syslogd ldattach pflogd nsd rebound unbound ntpd
start_daemon iscsid isakmpd iked sasyncd ldapd npppd
echo '.'

# Load IPsec rules.
if [[ $ipsec != NO && -f /etc/ipsec.conf ]]; then
	ipsecctl -f /etc/ipsec.conf
fi

echo -n 'starting RPC daemons:'
start_daemon portmap ypldap
rm -f /var/run/ypbind.lock
if [[ -n $(domainname) ]]; then
	start_daemon ypserv ypbind
fi
start_daemon mountd nfsd lockd statd amd
echo '.'

# Check and mount remaining file systems and enable additional swap.
mount -a
swapctl -A -t noblk
do_fsck -N
mount -a -N

# /var/crash should be a directory or a symbolic link to the crash directory
# if core dumps are to be saved.
if [[ -d /var/crash ]]; then
	savecore $savecore_flags /var/crash
fi

if [[ $check_quotas == YES ]]; then
	echo -n 'checking quotas:'
	quotacheck -a
	echo ' done.'
	quotaon -a
fi

# Build kvm(3) and /dev databases.
kvm_mkdb
dev_mkdb

# Set proper permission for the tty device files.
chmod 666 /dev/tty[pqrstuvwxyzPQRST]*
chown root:wheel /dev/tty[pqrstuvwxyzPQRST]*

# Check the password temp/lock file.
if [[ -f /etc/ptmp ]]; then
	logger -s -p auth.err \
	    'password file may be incorrect -- /etc/ptmp exists'
fi

echo clearing /tmp

# Prune quickly with one rm, then use find to clean up /tmp/[lqv]*
# (not needed with mfs /tmp, but doesn't hurt there...).
(cd /tmp && rm -rf [a-km-pr-uw-zA-Z]*)
(cd /tmp &&
    find . -maxdepth 1 ! -name . ! -name lost+found ! -name quota.user \
	! -name quota.group ! -name vi.recover -execdir rm -rf -- {} \;)

# Create Unix sockets directories for X if needed and make sure they have
# correct permissions.
[[ -d /usr/X11R6/lib ]] && mkdir -m 1777 /tmp/.{X11,ICE}-unix

[[ -f /etc/rc.securelevel ]] && sh /etc/rc.securelevel

# rc.securelevel did not specifically set -1 or 2, so select the default: 1.
(($(sysctl -n kern.securelevel) == 0)) && sysctl kern.securelevel=1


# Patch /etc/motd.
if [[ ! -f /etc/motd ]]; then
	install -c -o root -g wheel -m 664 /dev/null /etc/motd
fi
if T=$(mktemp /tmp/_motd.XXXXXXXXXX); then
	sysctl -n kern.version | sed 1q >$T
	echo "" >>$T
	sed '1,/^$/d' </etc/motd >>$T
	cmp -s $T /etc/motd || cp $T /etc/motd
	rm -f $T
fi

if [[ $accounting == YES ]]; then
	[[ ! -f /var/account/acct ]] && touch /var/account/acct
	echo 'turning on accounting'
	accton /var/account/acct
fi

if [[ -x /sbin/ldconfig ]]; then
	echo 'creating runtime link editor directory cache.'
	[[ -d /usr/local/lib ]] && shlib_dirs="/usr/local/lib $shlib_dirs"
	[[ -d /usr/X11R6/lib ]] && shlib_dirs="/usr/X11R6/lib $shlib_dirs"
	ldconfig $shlib_dirs
fi

echo 'preserving editor files.'; /usr/libexec/vi.recover

# If rc.sysmerge exists, run it just once, and make sure it is deleted.
run_upgrade_script sysmerge

echo -n 'starting network daemons:'
start_daemon ldomd vmd sshd snmpd ldpd ripd ospfd ospf6d bgpd ifstated
start_daemon relayd dhcpd dhcrelay mrouted dvmrpd radiusd eigrpd

if ifconfig lo0 inet6 >/dev/null 2>&1; then
	if (($(sysctl -n net.inet6.ip6.forwarding) == 1)); then
		start_daemon route6d rtadvd
	fi
fi

start_daemon hostapd lpd smtpd slowcgi httpd ftpd
start_daemon ftpproxy ftpproxy6 tftpd tftpproxy identd inetd rarpd bootparamd
start_daemon rbootd mopd spamd spamlogd sndiod
echo '.'

# If rc.firsttime exists, run it just once, and make sure it is deleted.
run_upgrade_script firsttime

# Run rc.d(8) scripts from packages.
if [[ -n $pkg_scripts ]]; then
	echo -n 'starting package daemons:'
	for _daemon in $pkg_scripts; do
		if [[ -x /etc/rc.d/$_daemon ]]; then
			start_daemon $_daemon
		else
			echo -n " ${_daemon}(absent)"
		fi
	done
	echo '.'
fi

[[ -f /etc/rc.local ]] && sh /etc/rc.local

ifconfig -g carp -carpdemote 128	# Disable carp interlock.

mixerctl_conf

echo -n 'starting local daemons:'
start_daemon apmd sensorsd hotplugd watchdogd cron wsmoused xdm
echo '.'

date
exit 0
