code:
#!/bin/ksh
#	$OpenBSD: install.sub,v 1.902 2016/07/23 17:55:45 deraadt Exp $
#
# Copyright (c) 1997-2015 Todd Miller, Theo de Raadt, Ken Westerback
# Copyright (c) 2015, Robert Peichaer <rpe@openbsd.org>
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Copyright (c) 1996 The NetBSD Foundation, Inc.
# All rights reserved.
#
# This code is derived from software contributed to The NetBSD Foundation
# by Jason R. Thorpe.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#

# OpenBSD install/upgrade script common subroutines and initialization code

# ------------------------------------------------------------------------------
# Misc functions
# ------------------------------------------------------------------------------

usage() {
	echo "usage: ${0##*/} [-a] [-f filename] [-m install | upgrade]" >&2
	exit 1
}

# Wait for the ftp process to finish, or be killed after the timeout.
mirror_fetch() {
	ftp -Vo http://libertybsd.net/pub/LibertyBSD/6.0/mirrorlist /tmp/httplist
}

# ------------------------------------------------------------------------------
# Utils functions
# ------------------------------------------------------------------------------

# Sort and print unique list of provided arguments.
bsort() {
	local _a=$1 _b _l

	(($#)) && shift || return

	for _b; do
		[[ $_a == "$_b" ]] && continue
		if [[ $_a > $_b ]]; then
			_l="$_a $_l" _a=$_b
		else
			_l="$_b $_l"
		fi
	done

	# Output the smallest value found.
	echo -n "$_a "

	# Sort remaining values.
	bsort $_l
}

# Test the first argument against the remaining ones, return success on a match.
isin() {
	local _a=$1 _b

	shift
	for _b; do
		[[ $_a == "$_b" ]] && return 0
	done
	return 1
}

# Add first argument to list formed by the remaining arguments.
# Adds to the tail if the element does not already exist.
addel() {
	local _a=$1

	shift
	echo -n "$*"
	isin "$_a" $* || echo -n " $_a"
}

# Remove all occurrences of first argument from list formed by the remaining
# arguments.
rmel() {
	local _a=$1 _b

	shift
	for _b; do
		[[ $_a != $_b ]] && echo -n "$_b "
	done
}

# If possible, print the timestamp received from the ftplist.cgi output,
# adjusted with the time elapsed since it was received.
http_time() {
	local _sec=$(cat $HTTP_SEC 2>/dev/null)

	[[ -n $_sec && -n $CGI_TIME ]] &&
		echo $((CGI_TIME + SECONDS - _sec))
}

# Prints the supplied parameters properly escaped for future sh/ksh parsing.
# Quotes are added if needed, so you should not do that yourself.
quote() (
	# Since this is a subshell we won't pollute the calling namespace.
	for a; do
		alias Q=$a; a=$(alias Q); print -rn -- " ${a#Q=}"
	done | sed '1s/ //'
	echo
)

# Show a list (passed via ordered arguments) in column output using ls.
showcols() {
	local _l _cdir=/tmp/cdir _clist

	mkdir -p $_cdir
	rm -rf -- $_cdir/*
	while read _l; do
		[[ -n $_l ]] || continue
		mkdir -p /tmp/cdir/"$_l"
		_clist[${#_clist[*]}]="$_l"
	done
	(cd $_cdir; ls -Cdf "${_clist[@]}")
	rm -rf -- $_cdir
}

# Echo the file $1 to standard output, skipping any lines that begin with a
# '#'. i.e. strip comment lines from the file.
stripcom () {
	local _l

	[[ -f $1 ]] || return

	set -o noglob
	while read _l; do
		[[ -n ${_l%%#*} ]] && echo $_l
	done <$1
	set +o noglob
}

# Create a temporary directory based on the supplied directory name prefix.
tmpdir() {
	local _i=1 _dir

	until _dir="${1?}.$_i.$RANDOM" && mkdir -- "$_dir" 2>/dev/null; do
		((++_i < 10000)) || return 1
	done
	echo "$_dir"
}

# Generate unique filename based on the supplied filename $1.
unique_filename() {
	local _fn=$1 _ufn

	while _ufn=${_fn}.$RANDOM && [[ -e $_ufn ]]; do done
	print -- "$_ufn"
}

# Let rc.firsttime feed file $1 using $2 as subject to whatever mail system we
# have at hand by then.
prep_root_mail() {
	local _fn=$1 _subject=$2 _ufn

	[[ -s $_fn ]] || return

	_ufn=$(unique_filename /mnt/var/log/${_fn##*/})
	cp $_fn $_ufn
	chmod 600 $_ufn
	_ufn=${_ufn#/mnt}

	cat <<__EOT >>/mnt/etc/rc.firsttime
( /usr/bin/mail -s '$_subject' root <$_ufn && rm $_ufn ) >/dev/null 2>&1 &
__EOT
}

# ------------------------------------------------------------------------------
# Device related functions
# ------------------------------------------------------------------------------

# Show device name, label and size for the provided list of devices.
diskinfo() {
	local _d

	for _d; do
		makedev $_d
		echo -n "$_d: "
		disklabel -dpg $_d 2>/dev/null |
		sed	-e '/^label: /{s,,,;s/ *$//;s/^$/<no label>/;h;d;}' \
			-e '/.*# total bytes: \(.*\)/{s//(\1)/;H;}' \
			-e '$!d;x;s/\n/ /'
		rm -f /dev/{r,}$_d?
	done
}

# Create devices passed as arguments.
makedev() {
	[[ -z $(cd /dev && sh MAKEDEV "$@" 2>&1) ]]
}

# Sort and print information from dmesg.boot using sed expression $1.
scan_dmesg() {
	bsort $(sed -n "$1" /var/run/dmesg.boot)
}

# Extract device names from hw.disknames matching sed expression $1.
scan_disknames() {
	local IFS=,

	bsort $(for _n in $(sysctl -n hw.disknames); do echo "${_n%%:*} "; done | sed -n "$1")
}

# Return list of disk devices.
get_dkdevs () {
	echo $(scan_disknames "${MDDKDEVS:-/^[sw]d[0-9][0-9]* /s/ .*//p}")
}

# Return list of CDROM devices.
get_cddevs () {
	echo $(scan_disknames "${MDCDDEVS:-/^cd[0-9][0-9]* /s/ .*//p}")
}

# Return list of disks not yet initialized.
get_dkdevs_uninitialized() {
	local _disks=$(get_dkdevs) _d

	for _d in $DISKS_DONE; do
		_disks=$(rmel "$_d" $_disks)
	done
	bsort $_disks
}

# Return list of network devices. Filter out dynamically created network
# pseudo-devices except vlan.
get_ifs() {
	local _if _iflist=$(rmel vlan $(ifconfig -C))

	for _if in $(ifconfig "$@" 2>/dev/null |
		sed -n 's/^\([^[:space:]]*\):.*/\1/p'); do
		isin "${_if%%+([0-9])}" $_iflist || echo $_if
	done
}

# Return the device name of the device $1, which may be a disklabel UID.
get_dkdev_name() {
	local _dev=${1#/dev/} _d

	_dev=${_dev%.[a-p]}
	((${#_dev} < 16)) && _dev=${_dev%[a-p]}
	local IFS=,
	for _d in $(sysctl -n hw.disknames); do
		[[ $_dev == @(${_d%:*}|${_d#*:}) ]] && echo ${_d%:*} && break
	done
}

# Inspect disk $1 if it has a partition-table of type $2 and optionally
# if it has a partition of type $3.
disk_has() {
	local _disk=$1 _pttype=$2 _part=$3 _cmd _p_pttype _p_part

	[[ -n $_disk && -n $_pttype ]] || exit

	# Commands to inspect disk. Default: "fdisk $_disk"
	local _c_hfs="pdisk -l $_disk"
	local _c_sr="bioctl -q $_disk"

	# Patterns for partition-table-types and partition-types.
	local _p_gpt='Usable LBA:'
	local _p_gpt_openbsd='^[ *]...: OpenBSD '
	local _p_gpt_efisys='^[ *]...: EFI Sys '
	local _p_hfs='^Partition map '
	local _p_hfs_openbsd=' OpenBSD OpenBSD '
	local _p_mbr='Signature: 0xAA55'
	local _p_mbr_openbsd='^..: A6 '
	local _p_mbr_dos='^..: 06 '
	local _p_mbr_dos_active='^\*.: 06 '
	local _p_mbr_linux='^..: 83 '
	local _p_sr='OPENBSD, SR'
	local _p_sr_crypto='OPENBSD, SR CRYPTO'

	eval "_cmd=\"\$_c_${_pttype}\""
	eval "_p_pttype=\"\$_p_${_pttype}\""
	eval "_p_part=\"\$_p_${_pttype}_${_part}\""
	_cmd=${_cmd:-fdisk $_disk}

	[[ -z $_p_pttype ]] && exit
	[[ -n $_part && -z $_p_part ]] && exit

	if [[ -z $_p_part ]]; then
		$_cmd 2>/dev/null | grep -Eq "$_p_pttype"
	else
		$_cmd 2>/dev/null | grep -Eq "$_p_pttype" &&
			$_cmd 2>/dev/null | grep -Eq "$_p_part"
	fi
}

# Handle disklabel auto-layout during interactive installation and
# autopartitioning during unattended installation for the root disk.
# In the latter case, ask for and download autopartitioning template.
# Abort unattended installation if autopartitioning fails.
#
# Parameters:
#
# $1 = disk
# $2 = /path/to/fstab
#
disklabel_autolayout() {
	local _disk=$1 _f=$2 _dl=/disklabel.auto _op

	[[ $_disk != $ROOTDISK ]] && return

	while $AUTO; do
		ask "URL to autopartitioning template for disklabel?" none
		[[ $resp == none ]] && break
		echo "Fetching $resp"
		if ftp -Vo $_dl "$resp" && [[ -s $_dl ]]; then
			disklabel -T $_dl -F $_f -w -A $_disk && return
			echo "Autopartitioning failed"
			exit 1
		else
			echo "No autopartitioning template found."
			exit 1
		fi
	done

	while :; do
		echo "The auto-allocated layout for $_disk is:"
		disklabel -h -A $_disk | egrep "^#  |^  [a-p]:"
		ask "Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout?" a
		case $resp in
		[aA]*)	_op=-w;;
		[eE]*)	_op=-E;;
		[cC]*)	return 0;;
		*)	continue;;
		esac
		disklabel -F $_f $_op -A $_disk
		return
	done
}

configure_disk() {
	local _disk=$1 _fstab=/tmp/fstab.$1

	makedev $_disk || return

	# Deal with disklabels, including editing the root disklabel
	# and labeling additional disks. This is machine-dependent since
	# some platforms may not be able to provide this functionality.
	# /tmp/fstab.$_disk is created here with 'disklabel -F'.
	rm -f /tmp/*.$_disk
	md_prep_disklabel $_disk || return

	# Make sure there is a '/ mount point.
	if ! grep -qs ' / ffs ' /tmp/fstab.$ROOTDISK; then
		echo "'/' must be configured!"
		return 1
	fi

	if [[ -f $_fstab ]]; then
		# Avoid duplicate mount points on different disks.
		while read _pp _mp _rest; do
			if [[ $_mp == none ]]; then
				# Multiple swap partitions are ok.
				echo "$_pp $_mp $_rest" >>/tmp/fstab
				continue
			fi
			# Non-swap mountpoints must be in only one file.
			if [[ $_fstab != $(grep -l " $_mp " /tmp/fstab.*) ]]; then
				_rest=$_disk
				_disk=
				break
			fi
		done <$_fstab

		if [[ -z $_disk ]]; then
			# Duplicate mountpoint.
			# Allow disklabel(8) to read back mountpoint info
			# if it is immediately run against the same disk.
			cat /tmp/fstab.$_rest >/etc/fstab
			rm /tmp/fstab.$_rest
			set -- $(grep -h " $_mp " /tmp/fstab.*[0-9])
			echo "$_pp and $1 can't both be mounted at $_mp."
			return 1
		fi

		# Add ffs filesystems to list after newfs'ing them. Ignore
		# other filesystems.
		while read _pp _mp _fstype _rest; do
			[[ $_fstype == ffs ]] || continue
			_OPT=
			[[ $_mp == / ]] && _OPT=$MDROOTFSOPT
			newfs -q $_OPT ${_pp##/dev/}
			# N.B.: '!' is lexically < '/'. That is
			#	required for correct sorting of
			#	mount points.
			FSENT="$FSENT $_mp!$_pp"
		done <$_fstab
	fi

	return 0
}

# ------------------------------------------------------------------------------
# Functions for the dmesg listener
# ------------------------------------------------------------------------------

# Acquire lock.
lock() {
	while ! mkdir /tmp/lock 2>/dev/null && sleep .1; do done
}

# Release lock.
unlock() {
	rm -d /tmp/lock 2>/dev/null
}

# Add trap to kill the listener process.
retrap() {
	trap 'kill -KILL $CPPID 2>/dev/null; echo; stty echo; exit 0' \
		INT EXIT TERM
}

# ------------------------------------------------------------------------------
# Functions to ask (or auto-answer) questions
# ------------------------------------------------------------------------------

# Log installer questions and answers so that the resulting file can be used as
# response file for an unattended install/upgrade.
log_answers() {
	if [[ -n $1 && -n $2 ]]; then
		print -r -- "${1%%'?'*} = $2" >>/tmp/$MODE.resp
	fi
}

# Fetch response file for autoinstall.
get_responsefile() {
	local _rf _if _mac _mode _lf _hn _path
	AI_MODE=

	[[ -f /auto_upgrade.conf ]] && _rf=/auto_upgrade.conf _mode=upgrade
	[[ -f /auto_install.conf ]] && _rf=/auto_install.conf _mode=install
	[[ -f $_rf ]] && cp $_rf /ai.$_mode.conf && AI_MODE=$_mode && return

	# Select a network interface for initial dhcp request.
	# Ask if multiple were found and system was not netbooted.
	# Extract server ip address, installer mode and response file path
	# from lease file. Prime hostname with host-name option.
	for _if in ''; do
		[[ -x /sbin/dhclient ]] || break
		set -- $(get_ifs netboot)
		(($# == 0)) && set -- $(get_ifs)
		(($# == 1)) && _if=$1
		while (($# > 1)); do
			ask_which "network interface" \
				"should be used for the initial DHCP request" "$*"
			isin "$resp" $* && _if=$resp && break
		done
		[[ -n $_if ]] && dhclient $_if || break
		_lf=/var/db/dhclient.leases.$_if
		export AI_SERVER=$(sed "/^ *next-server /!d;s///;s/;$//;q" $_lf)
		_mode=$(sed -E '/^ *filename "(.*\/)?auto_(install|upgrade)";$/!d;s//\2/;q' $_lf)
		_path=$(sed -E '/^ *filename "(.*\/)[^/]+";$/!d;s//\1/;q' $_lf)
		_hn=$(sed -E '/^ *option host-name "(.*)";$/!d;s//\1/;q' $_lf)
		hostname "$_hn"
	done

	# Fetch response file if server and mode are known, otherwise tell which
	# one was missing. Try to fetch mac-mode.conf, then hostname-mode.conf,
	# and finally mode.conf.
	if [[ -n $AI_SERVER && -n $_mode ]]; then
		_mac=$(ifconfig $_if | sed 's/.*lladdr \(.*\)/\1/p;d')
		for _rf in {$_mac-,${_hn:+$_hn-,}}$_mode; do
			_url="http://$AI_SERVER/$_path$_rf.conf?path=$HTTP_SETDIR"
			echo "Fetching $_url"
			if ftp -Vo "/ai.$_mode.conf" "$_url" 2>/dev/null; then
				AI_MODE=$_mode
				ifconfig $_if delete down 2>/dev/null
				return 0
			fi
		done
	else
		[[ -z $AI_SERVER ]] && echo "Could not determine next-server."
		[[ -z $_mode ]] && echo "Could not determine auto mode."
	fi

	# Ask for url or local path to response file. Provide a default url if
	# server was found in lease file.
	while :; do
		ask "Response file location?" \
			"${AI_SERVER:+http://$AI_SERVER/install.conf}"
		[[ -n $resp ]] && _rf=$resp && break
	done

	# Ask for the installer mode only if auto-detection failed.
	_mode=$(echo "$_rf" | sed -En 's/^.*(install|upgrade).conf$/\1/p')
	while [[ -z $_mode ]]; do
		ask "(I)nstall or (U)pgrade?"
		[[ $resp == [iI]* ]] && _mode=install
		[[ $resp == [uU]* ]] && _mode=upgrade
	done

	echo "Fetching $_rf"
	[[ -f $_rf ]] && _rf="file://$_rf"
	ftp -Vo "/ai.$_mode.conf" "$_rf" 2>/dev/null && AI_MODE=$_mode
	ifconfig $_if delete down 2>/dev/null
	[[ -n $AI_MODE ]]
}

# Search question in $RESPFILE, return answer in $resp.
#
# 1) split question and answer at leftmost =
# 2) strip leading/trailing blanks
# 3) compare questions case insensitive
# 4) ignore empty and comment lines and lines without =
# 5) return default answer if provided and none is found in file
# 6) treat empty/missing/multiple answers as error and exit
#
# Parameters:
#
# $1 = the question to search for
# $2 = the default answer
#
_autorespond() {
	typeset -l _q=$1 _key
	local _def=$2 _l _val

	[[ -f $RESPFILE ]] || return

	# Find a suitable response in /ai.conf and remove it if found.
	mv /ai.conf /ai.conf.tmp
	while IFS=' 	' read -r _l; do
		[[ $_l == [!#=]*=?* ]] || continue
		_key=${_l%%*([[:blank:]])=*}
		_val=${_l##*([!=])=*([[:blank:]])}
		[[ $_q == @(|*[[:blank:]])"$_key"@([[:blank:]?]*|) ]] &&
			resp=$_val && cat && return
		print -r " $_l"
	done </ai.conf.tmp >/ai.conf
	[[ -n $_def ]] && resp=$_def && return
	echo "\nQuestion has no answer in response file."
	exit 1
}

# Issue a read into the global variable $resp. If the dmesg output is
# changed while inside this function, the current read will be aborted
# and the function will return a non-zero value. Normally, the caller
# will then reprint any prompt and call the function again.
#
# Optional parameters:
#
# $1 = the question to ask the user
# $2 = the default answer
#
_ask() {
	local _q=$1 _def=$2 _int _redo=0 _pid

	lock; dmesg >/tmp/update; unlock
	echo -n "${_q:+$_q }${_def:+[$_def] }"
	_autorespond "$_q" "$_def" && echo "$resp" && return
	trap "_int=1" INT
	trap "_redo=1" TERM
	read resp
	lock; rm /tmp/update; unlock
	if ((_redo)); then
		stty raw
		stty -raw
	else
		case $resp in
		!)	echo "Type 'exit' to return to install."
			sh
			_redo=1
			;;
		!*)	eval "${resp#?}"
			_redo=1
			;;
		esac
	fi
	retrap
	((_int)) && kill -INT $$
	: ${resp:=$_def}
	return $_redo
}

# Ask for user input, which is returned in $resp.
# Any parameters are passed on to _ask(), which is called
# repeatedly until it succeds.
ask() {
	while ! _ask "$1" "$2"; do done
	log_answers "$1" "$resp"
}

# Ask the user for a y or n, and insist on 'y', 'yes', 'n' or 'no'.
# Return 'y' or 'n' in $resp.
# Exit code: yes => 0, no => 1
#
# Parameters:
#
# $1 = the question to ask the user
# $2 = the default answer (assumed to be 'n' if empty).
#
ask_yn() {
	local _q=$1 _a=${2:-no}
	typeset -l _resp

	while :; do
		ask "$_q" "$_a"
		_resp=$resp
		case $_resp in
		y|yes)	resp=y; return 0;;
		n|no)	resp=n; return 1;;
		esac
		echo "'$resp' is not a valid choice."
		$AUTO && exit 1
	done
}

# Ask for the user to select one value from a list, or 'done'.
# At exit $resp holds selected item, or 'done'.
#
# Parameters:
#
# $1 = name of the list items (disk, cd, etc.)
# $2 = question to ask
# $3 = list of valid choices
# $4 = default choice, if it is not specified use the first item in $3
#
# N.B.! $3 and $4 will be "expanded" using eval, so be sure to escape them
#       if they contain spooky stuff
ask_which() {
	local _name=$1 _query=$2 _list=$3 _def=$4 _dynlist _dyndef _key _q
	_key=$(echo "$_name" | sed 's/[^[:alnum:]]/_/g')

	while :; do
		eval "_dynlist=\"$_list\""
		eval "_dyndef=\"$_def\""

		# Clean away whitespace and determine the default.
		set -o noglob
		set -- $_dyndef; _dyndef="$1"
		set -- $_dynlist; _dynlist="$*"
		set +o noglob
		(($# < 1)) && resp=done && return

		: ${_dyndef:=$1}
		echo "Available ${_name}s are: $_dynlist."
		_q="Which $_name $_query?"
		echo -n "$_q (or 'done') ${_dyndef:+[$_dyndef] }"
		_autorespond "$_q" "${_dyndef-done}" && echo "$resp" \
			|| _ask || continue
		[[ -z $resp ]] && resp="$_dyndef"

		# Quote $resp to prevent user from confusing isin() by
		# entering something like 'a a'.
		if isin "$resp" $_dynlist done; then
			log_answers "$_q" "$resp"
			break
		fi
		echo "'$resp' is not a valid choice."
		$AUTO && [[ -n $RESPFILE ]] && exit 1
	done
}

# Ask for user input until a non-empty reply is entered.
# Save the user input (or the default) in $resp.
#
# Parameters:
#
# $1 = the question to ask the user
# $2 = the default answer
#
function ask_until {
	resp=

	while true; do
		ask "$1" "$2"
		[[ -n $resp ]] && break
		echo "A response is required."
		$AUTO && exit 1
	done
}

# Ask for a password, saving the input in $resp.
#
# 1) Display $1 as the prompt.
# 2) *Don't* allow the '!' options that ask does.
# 3) *Don't* echo input.
# 4) *Don't* interpret "\" as escape character.
# 5) Preserve whitespace in input
#
askpass() {
	stty -echo
	IFS= read -r resp?"$1 "
	stty echo
	echo
}

# Ask for a password twice, saving the input in $_password.
askpassword() {
	local _q=$1

	if $AUTO; then
		echo -n "$_q "
		_autorespond "$_q"
		echo '<provided>'
		_password=$resp
		return
	fi

	while :; do
		askpass "$_q (will not echo)"
		_password=$resp

		askpass "$_q (again)"
		[[ $resp == "$_password" ]] && break

		echo "Passwords do not match, try again."
	done
}

# ------------------------------------------------------------------------------
# Support functions for donetconfig()
# ------------------------------------------------------------------------------

# Issue a DHCP request to configure interface $1 and add the host-name option to
# /etc/dhclient.conf using $2.
dhcp_request() {
	local _if=$1 _hn=$2

	echo "lookup file bind" >/etc/resolv.conf.tail
	echo "send host-name \"$_hn\";" >/etc/dhclient.conf

	ifconfig $_if group dhcp >/dev/null 2>&1

	dhclient -c /dev/stdin $_if <<__EOT
initial-interval 1;
backoff-cutoff 2;
reboot 5;
timeout 10;
send host-name "$_hn";
__EOT

	# Move configuration files to where they will be copied to the
	# installed system. Overwrites configuration information from
	# last successful dhcp attempt.
	mv /etc/dhclient.conf /tmp/dhclient.conf
	mv /etc/resolv.conf.tail /tmp/resolv.conf.tail
}

# Obtain and output the inet information related to interface $1.
# Should output '<UP/DOWN> <addr> <netmask> <rest of inet line>'.
v4_info() {
	ifconfig $1 inet | sed -n '
		1s/.*<UP,.*/UP/p
		1s/.*<.*/DOWN/p
		/inet/s/netmask//
		/inet/s///p'
}

# Convert a netmask in hex format ($1) to dotted decimal format.
hextodec() {
	set -- $(echo ${1#0x} | sed 's/\(..\)/0x\1 /g')
	echo $(($1)).$(($2)).$(($3)).$(($4))
}

# Create an entry in the hosts file. If an entry with the same symbolic name and
# address family already exists, delete it.
#
# Parameters:
#
# $1 = IP address (v6 if it contains ':', else v4)
# $2 = symbolic name
#
addhostent() {
	local _addr=$1 _name=$2 _delim="."

	[[ -z $_addr || -z $_name ]] && return

	[[ $_addr == *:* ]] && _delim=":"
	sed -i "/^[0-9a-fA-F]*[$_delim].*[ 	]$_name\$/d" \
		/tmp/hosts 2>/dev/null

	echo "$_addr $_name" >>/tmp/hosts
}

# Configure IPv4 interface.
#
# Parameters:
#
# $1 = name of the network device
# $2 = hostname to use for dhcp request
# $3 = /path/to/hostname.if
#
v4_config() {
	local _if=$1 _name=$2 _hn=$3 _prompt _addr _mask

	if ifconfig $_if | grep -q 'groups:.* dhcp'; then
		_addr=dhcp
	else
		set -- $(v4_info $_if)
		if [[ -n $2 ]]; then
			_addr=$2; _mask=$(hextodec $3)
			ifconfig $_if inet $_addr delete
		fi
	fi

	if [[ -x /sbin/dhclient ]]; then
		_prompt="or 'dhcp' "
		# Don't make 'dhcp' the default if dhcp was already used.
		ifconfig dhcp >/dev/null 2>&1 || _addr=dhcp
	fi
	_prompt="IPv4 address for $_if? (${_prompt}or 'none')"

	ask_until "$_prompt" "$_addr"
	case $resp in
	none)	;;
	dhcp)	if [[ ! -x /sbin/dhclient ]]; then
			echo "DHCP not possible - no /sbin/dhclient."
		else
			dhcp_request $_if "$_name"
			echo "dhcp" >>$_hn
		fi
		;;
	*)	_addr=$resp
		ask_until "Netmask for $_if?" "${_mask:=255.255.255.0}"
		ifconfig $_if -group dhcp >/dev/null 2>&1
		if ifconfig $_if inet $_addr netmask $resp up; then
			addhostent "$_addr" "$_name"
			echo "inet $_addr $resp" >>$_hn
		fi
		;;
	esac
}

# Obtain and output the inet6 information related to interface $1.
# Should output '<UP/DOWN> <addr> <prefixlen> <rest of inet line> '.
v6_info() {
	ifconfig $1 inet6 | sed -n '
		1s/.*<UP,.*/UP/p
		1s/.*<.*/DOWN/p
		/scopeid/d
		/inet6/s/prefixlen//
		/inet6/s///p'
}

# Set up IPv6 default route on interface $1.
v6_defroute() {
	local _if=$1 _prompt _resp _routers

	route -n show -inet6 | egrep -q '^default[[:space:]]' && return

	_routers=$(bsort $(ping6 -n -c 2 ff02::2%$_if 2>/dev/null |
		sed -n '/bytes from/{s/^.*from //;s/,.*$//;p;}' |
		sed -n 'G;s/\n/&&/;/^\(.*\n\).*\n\1/d;h;P'))

	_prompt="IPv6 default router?"

	if $AUTO; then
		_autorespond "$_prompt" && _resp=$resp && echo "$_prompt $_resp"
	else
		local PS3="$_prompt (${_routers:+list #, }IPv6 address or 'none'): "
		select _resp in $_routers; do
			[[ ${_resp:=$REPLY} == *:* ]] && break
			[[ $_resp == none ]] && return
		done
	fi

	route -n add -inet6 -host default "$_resp" &&
		echo "$_resp" >>/tmp/mygate
}

# Configure IPv6 interface.
#
# Parameters:
#
# $1 = name of the network device
# $2 = hostname to use for dhcp request
# $3 = /path/to/hostname.if
#
v6_config() {
	local _if=$1 _name=$2 _hn=$3 _addr _prefixlen _prompt

	ifconfig lo0 inet6 >/dev/null 2>&1 || return

	set -- $(v6_info $_if)
	[[ -n $2 ]] && { _addr=$2; _prefixlen=$3; }

	ifconfig $_if inet6 >/dev/null 2>&1 && _prompt="or 'rtsol' "
	_prompt="IPv6 address for $_if? (${_prompt}or 'none')"
	ask_until "$_prompt" "${_addr:-none}"

	case $resp in
	none)	return
		;;
	rtsol)	ifconfig $_if inet6 >/dev/null 2>&1 || { echo "No INET6 support."; return; }
		ifconfig $_if up
		ifconfig $_if inet6 autoconf && echo "up\nrtsol" >>$_hn
		return
		;;
	esac

	_addr=$resp
	ask_until "IPv6 prefix length for $_if?" "${_prefixlen:=64}"
	ifconfig $_if inet6 $_addr prefixlen $resp up || return
	echo "inet6 $_addr $resp" >>$_hn
	addhostent "$_addr" "$_name"

	v6_defroute $_if
}

# Perform an 802.11 network scan on interface $1.
# The result is cached in $WLANLIST.
ieee80211_scan() {
	# N.B. Skipping quoted nwid's for now.
	[[ -f $WLANLIST ]] ||
		ifconfig $1 scan |
		sed -n 's/^		nwid \([^"]\)/\1/p' >$WLANLIST
	cat $WLANLIST
}

# Configure 802.11 interface $1 and append ifconfig options to hostname.if $2.
ieee80211_config() {
	local _if=$1 _hn=$2 _prompt _nwid _haswpa=0 _err

	# Reset 802.11 settings and determine wpa capability.
	ifconfig $_if -nwid -nwkey
	ifconfig $_if -wpa 2>/dev/null && _haswpa=1

	# Empty scan cache.
	rm -f $WLANLIST

	while [[ -z $_nwid ]]; do
		ask_until "Access point? (ESSID, 'any', list# or '?')" "any"
		case "$resp" in
		+([0-9]))
			_nwid=$(ieee80211_scan $_if | sed -n "${resp}s/ .*//p")
			[[ -z $_nwid ]] && echo "There is no line $resp."
			;;
		\?)	ieee80211_scan $_if |
				sed -n 's/^\([^ ]*\) chan .* bssid \([^ ]*\) .*$/	\1 (\2)/p' |
				cat -n | more -c
			;;
		*)	_nwid=$resp
			;;
		esac
	done

	# 'any' implies that only open access points are considered.
	if [[ $_nwid != any ]]; then
		ifconfig $_if nwid "$_nwid"
		quote nwid "$_nwid" >>$_hn

		_prompt="Security protocol? (O)pen, (W)EP"
		((_haswpa == 1)) && _prompt="$_prompt, WPA-(P)SK"
		while :; do
			ask_until "$_prompt" "O"
			case "$_haswpa-$resp" in
			?-[Oo])	break
				;;
			?-[Ww])	ask_until "WEP key? (will echo)"
				# Make sure ifconfig accepts the key.
				if _err=$(ifconfig $_if nwkey "$resp" 2>&1) &&
					[[ -z $_err ]]; then
					quote nwkey "$resp" >>$_hn
					break
				fi
				echo "$_err"
				;;
			1-[Pp])	ask_until "WPA passphrase? (will echo)"
				# Make sure ifconfig accepts the key.
				if ifconfig $_if wpakey "$resp"; then
					quote wpakey "$resp" >>$_hn
					break
				fi
				;;
			*)	echo "'$resp' is not a valid choice."
				;;
			esac
		done
	fi
}

# Set up IPv4 and IPv6 interface configuration.
configure_ifs() {
	local _first _ifs _if _name _hn _vl=0 _vd _vi _p _tags

	# In case of restart, discover last vlan configured.
	while :; do
		_vd=$(ifconfig vlan$_vl 2>&1)
		[[ $_vd == @(*no such interface*) ]] && break
		[[ $_vd == @(*vlan: +([[:digit:]]) parent interface:*) ]] || break
		((_vl++))
	done
	_vd=

	# Always need lo0 configured.
	ifconfig lo0 inet 127.0.0.1/8

	# In case of restart, delete previous default gateway config.
	rm -f /tmp/mygate

	while :; do
		# Create new vlan if possible.
		ifconfig vlan$_vl create >/dev/null 2>&1

		ask_which "network interface" "do you wish to configure" \
			'$(get_ifs)' \
			${_p:-'$( (get_ifs netboot; get_ifs) | sed q )'}
		[[ $resp == done ]] && break

		_if=$resp
		_hn=/tmp/hostname.$_if
		rm -f $_hn

		# If the offered vlan is chosen, ask the relevant
		# questions and bring it up.
		if [[ $_if == vlan+([0-9]) ]]; then
			# Get existing tag for this vlan.
			_vi=$(ifconfig $_if 2>/dev/null |
				sed -n 's/vlan: \([0-9]*\).*/\1/p')
			# Get list of all in-use tags.
			_tags=$(ifconfig vlan 2>/dev/null |
				sed -n 's/vlan: \([0-9]*\).*/\1/p')
			# Current tag is a valid tag for this vlan.
			[[ -n $_tags ]] && _tags=$(rmel "$_vi" $_tags)
			if [[ -z $_vi ]]; then
				_vi=0
				while ((++_vi < 4096)); do
					! isin "$_vi" $_tags && break
				done
			fi
			_ifs=$(get_ifs)
			set -- $_ifs
			while [[ $1 == vlan+([0-9]) ]]; do
				shift
			done
			ask "Which interface:tag should $_if be on?" "${_vd:=$1}:$_vi"
			_vd=${resp%%:*}
			_vi=${resp##*:}

			# Validate that $_vd is a real interface.
			if ! (isin "$_vd" $_ifs && [[ $_vd != vlan+([0-9]) ]]); then
				echo "Invalid interface choice '$_vd'"
				_vd=
				continue
			fi

			# Validate range of $_vi as 1-4095, and $_vi not in use.
			if ((_vi < 1 || _vi > 4095)) || isin "$_vi" $_tags; then
				echo "Invalid or in-use vlan tag '$_vi'"
				continue
			fi

			# hostname.$_vd must say something, anything, to
			# make sure it is up.
			grep -qs "^up" /tmp/hostname.$_vd ||
				echo "up" >>/tmp/hostname.$_vd
			chmod 640 /tmp/hostname.$_vd
			ifconfig $_vd up

			# Make sure a hostname.$_if is created with this info.
			ifconfig $_if destroy >/dev/null 2>&1
			ifconfig $_if vlan $_vi vlandev $_vd
			echo "vlan $_vi vlandev $_vd" >>$_hn
			# Create a new vlan if we just configured the highest.
			[[ ${_if##vlan} == $_vl ]] && ((_vl++))
		fi

		# Test if it is an 802.11 interface.
		ifconfig $_if 2>/dev/null | grep -q "^[[:space:]]*ieee80211:" &&
			ieee80211_config $_if $_hn

		# First interface configured will use the hostname without
		# asking the user.
		resp=$(hostname -s)
		[[ -n $_first && $_first != $_if ]] &&
			ask "Symbolic (host) name for $_if?" $resp
		_name=$resp

		v4_config $_if $_name $_hn
		v6_config $_if $_name $_hn

		if [[ -f $_hn ]]; then
			chmod 640 $_hn
			: ${_first:=$_if}
		fi

		NIFS=$(ls -1 /tmp/hostname.* 2>/dev/null | grep -c ^)
		_p=done
	done
}

# Set up IPv4 default route.
v4_defroute() {
	local _dr

	# Only configure a default route if an IPv4 address was configured.
	[[ -n $(ifconfig | sed -n '/[ 	]inet .* broadcast /p') ]] || return

	# Check routing table to see if a default route ($1) already exists
	# and if it is handled by dhclient ($2).
	set -- $(route -nv show -inet | { set -- $(grep '^default '); print $2 $9; })
	[[ -n $1 ]] && _dr=$1

	# Don't ask if a default route exits and is handled by dhclient.
	[[ -n $_dr && $2 == DHCLIENT && -f /tmp/dhclient.conf ]] && return

	while :; do
		ask_until "Default IPv4 route? (IPv4 address or none)" "$_dr"
		[[ $resp == none ]] && break
		route delete -inet default >/dev/null 2>&1
		route -n add -inet -host default "$resp" && { echo "$resp" >>/tmp/mygate; break; }
		# Put the old default route back. The new one did not work.
		route -n add -inet -host default $_dr >/dev/null 2>&1
	done
}

# Extract fully qualified domain name from current hostname. If none is
# currently set, use 'my.domain'.
get_fqdn() {
	local _dn

	_dn=$(hostname)
	_dn=${_dn#$(hostname -s)}
	_dn=${_dn#.}

	echo "${_dn:=my.domain}"
}

# ------------------------------------------------------------------------------
# Support functions for install_sets()
# ------------------------------------------------------------------------------

# Check that missing required sets were deliberately skipped.
# Care is taken to make sure the return value is correct.
sane_install() {
	local _q=$1 _s

	for _s in $SANESETS; do
		isin "$_s" $DEFAULTSETS || continue
		[[ -n $_q ]] && return 1
		# If sane_install has no argument, harass the user.
		if ! ask_yn "Are you *SURE* your $MODE is complete without '$_s'?"; then
			$AUTO && exit 1 || return 1
		fi
	done
}

# Show list of available sets and let the user select which sets to install.
# Set $resp to list of selected sets.
#
# Parameters:
#
# $1 = available sets
# $2 = already selected sets
#
select_sets() {
	local _avail=$1 _selected=$2 _f _action _col=$COLUMNS

	# account for 4 spaces added to the sets list
	let COLUMNS=_col-8

	cat <<__EOT

Select sets by entering a set name, a file name pattern or 'all'. De-select
sets by prepending a '-' to the set name, file name pattern or 'all'. Selected
sets are labelled '[X]'.
__EOT
	while :; do
		for _f in $_avail; do
			isin "$_f" $_selected && echo "[X] $_f" || echo "[ ] $_f"
		done | showcols | sed 's/^/    /'
		ask "Set name(s)? (or 'abort' or 'done')" done

		set -o noglob
		for resp in $resp; do
			case $resp in
			abort)	_selected=; break 2;;
			done)	break 2;;
			-*)	_action=rmel;;
			*)	_action=addel;;
			esac
			resp=${resp#[+-]}
			[[ $resp = all ]] && resp=*

			for _f in $_avail; do
				[[ $_f = $resp ]] && _selected=$($_action $_f $_selected)
			done
		done
	done

	set +o noglob
	COLUMNS=$_col

	resp=$_selected
}

# Install a user-selected subset of the files in $2 from the source
# named in $1. Display an error message for failed installs so the
# user will know to try again.
install_files() {
	local _src=$1 _files=$2 _f _sets _get_sets _n _col=$COLUMNS \
		_tmpfs _tmpsrc _cfile _fsrc _unver _t _issue _srclocal

	# Initialize _sets to the list of sets found in _src, and initialize
	# _get_sets to the intersection of _sets and DEFAULTSETS.
	#
	# Sets will be installed in the order given in THESETS to ensure proper
	# installation.  So, to minimize user confusion display the sets in the
	# order in which they will be installed.
	for _f in $THESETS; do
		isin "$_f" $_files || continue
		_sets=$(addel $_f $_sets)
		isin "$_f" $DEFAULTSETS "site$VERSION-$(hostname -s).tgz" &&
			_get_sets=$(addel $_f $_get_sets)
	done

	if [[ -z $_sets ]]; then
		echo -n "Looked at $_src "
		echo "and found no $OBSD sets.  The set names looked for were:"

		let COLUMNS=_col-8
		for _n in $THESETS; do echo $_n; done | showcols | sed 's/^/    /'
		COLUMNS=$_col

		$AUTO && exit 1
		echo
		return
	fi

	isin "INSTALL.$ARCH" $_files ||
		ask_yn "INSTALL.$ARCH not found. Use sets found here anyway?" ||
		return

	select_sets "$_sets" "$_get_sets"

	[[ -n $resp ]] || return
	_get_sets=$resp

	# Reorder $_get_sets.
	_get_sets=$(for s in $THESETS; do isin "$s" $_get_sets && echo $s; done)

	# Note which sets didn't verify ok.
	_unver=$_get_sets

	# Try to prefetch and control checksum files.
	# Use dummy for loop as combined assignment and do { ... } while(0).
	for _issue in ''; do
		! isin SHA256.sig $_files &&
			_issue="Directory does not contain SHA256.sig" && break

		# Find a filesystem to store the prefetched sets. Prefer file-
		# systems which are not used during extraction. They need to
		# have 512 MB free space. Otherwise use any other filesystem
		# having 2 GB free space to prevent overflow during extraction.
		_tmpfs=$( (
			for fs in /mnt/{{,var/}tmp,home,usr{/local,}}; do
				df -k $fs 2>/dev/null | grep " $fs\$"
			done
			df -k
		) | (
			while read a a a a m m; do
				[[ $m == /mnt/@(@(|var/)tmp|home|usr/@(src,obj,xobj))@(|/*) ]] &&
					((a > 524288)) && echo $m && exit
				[[ $m == /mnt@(|/*) ]] &&
					((a > 524288 * 4)) && echo $m && exit
			done
		) )

		if [[ -d $_tmpfs ]]; then
			! _tmpsrc=$(tmpdir "$_tmpfs/sets") &&
			_issue="Cannot create prefetch area" && break
		else
			_issue="Cannot determine prefetch area" && break
		fi

		_cfile=$_tmpsrc/SHA256
		_srclocal=false
		_t=Get/Verify
		[[ $_src == file://* ]] && _srclocal=true _t='Verifying '

		# Fetch signature file.
		! ftp -D "$_t" -Vmo "$_cfile.sig" "$_src/SHA256.sig" &&
			_issue="Cannot fetch SHA256.sig" && break

		# Verify signature file with public keys.
		! signify -Vep /etc/signify/openbsd-${VERSION}-base.pub \
			-x "$_cfile.sig" -m "$_cfile" &&
			_issue="Signature check of SHA256.sig failed" && break

		for _f in $_get_sets; do
			rm -f "$_tmpsrc/h" "$_tmpsrc/fail"

			# Fetch set and create checksum by pipe through sha256.
			# Create a flag file in case ftp failed. Sets from net
			# are written to prefetch area, the output of local sets
			# is discarded.
			( ftp -D "$_t" -Vmo - "$_src/$_f" || >"$_tmpsrc/fail" ) |
			( $_srclocal && sha256 >$_tmpsrc/h ||
				sha256 -ph "$_tmpsrc/h" >"$_tmpsrc/$_f" )

			# Handle failed transfer.
			if [[ -f $_tmpsrc/fail ]]; then
				rm -f "$_tmpsrc/$_f"
				if ! ask_yn "Fetching of $_f failed. Continue anyway?"; then
					[[ -d $_tmpsrc ]] && rm -rf "$_tmpsrc"
					$AUTO && exit 1
					return
				fi
				continue
			fi

			# Verify sets by comparing its checksum with SHA256.
			if fgrep -qx "SHA256 ($_f) = $(<$_tmpsrc/h)" "$_cfile"; then
				_unver=$(rmel $_f $_unver)
			else
				if ! ask_yn "Checksum test for $_f failed. Continue anyway?"; then
					[[ -d $_tmpsrc ]] && rm -rf "$_tmpsrc"
					$AUTO && exit 1
					return
				fi
			fi
		done
	done

	[[ -n $_unver ]] && : ${_issue:="Unverified sets:" ${_unver% }}
	if [[ -n $_issue ]] &&
		! ask_yn "$_issue. Continue without verification?"; then
		[[ -d $_tmpsrc ]] && rm -rf "$_tmpsrc"
		$AUTO && exit 1
		return
	fi

	for _f in $_get_sets; do
		_fsrc="$_src/$_f"
		[[ -f $_tmpsrc/$_f ]] && _fsrc="file://$_tmpsrc/$_f"
		case $_fsrc in
		*.tgz)	ftp -D Installing -Vmo - "$_fsrc" | tar -zxphf - -C /mnt
			if [[ $_f == ?(x)base*.tgz && $MODE == install ]]; then
				ftp -D Extracting -Vmo - \
				file:///mnt/var/sysmerge/${_f%%base*}etc.tgz |
				tar -zxphf - -C /mnt
			fi
			;;
		*)	ftp -D Installing -Vmo "/mnt/$_f" "$_fsrc"
			;;
		esac
		if (($?)); then
			if ! ask_yn "Installation of $_f failed. Continue anyway?"; then
				[[ -d $_tmpsrc ]] && rm -rf "$_tmpsrc"
				$AUTO && exit 1
				return
			fi
		else
			DEFAULTSETS=$(rmel $_f $DEFAULTSETS)
		fi
		[[ -d $_tmpsrc ]] && rm -f "$_tmpsrc/$_f"
	done
	[[ -d $_tmpsrc ]] && rm -rf "$_tmpsrc"
}

# Get several parameters from the user, and xfer files from the http server.
install_http() {
	local _file_list _prompt _mirror _url_base

	mirror_fetch

	# N.B.: 'http_proxy' is an environment variable used by ftp(1). DON'T
	# change the name or case!
	ask "HTTP proxy URL? (e.g. 'http://proxy:8080', or 'none')" \
		"${http_proxy:-none}"
	unset http_proxy
	[[ $resp == none ]] || export http_proxy=$resp

	if [[ -s $HTTP_LIST ]]; then
		_prompt="HTTP Server? (hostname, list#, 'done' or '?')"
	else
		echo "(Unable to get list from libertybsd.net, but that is OK)"
		_prompt="HTTP Server? (hostname or 'done')"
	fi

	# Get server IP address or hostname.
	while :; do
		ask_until "$_prompt" "$HTTP_SERVER"
		case $resp in
		done)	return
			;;
		"?")	[[ -s $HTTP_LIST ]] || continue
			cat -n < $HTTP_LIST | more -c
			;;
		+([0-9]))
			# A numeric hostname is ignored. A number is only used
			# as a line number in $HTTP_LIST.
			[[ -s $HTTP_LIST ]] || continue
			set -- $(sed -n "${resp}p" $HTTP_LIST)
			(($# < 1)) && { echo "There is no line $resp."; continue; }
			HTTP_SERVER=${1%%/*}
			# Repeat loop to get user to confirm server address.
			;;
		+([A-Za-z0-9\:.\[\]_-]))
			HTTP_SERVER=$resp
			break
			;;
		*)	echo "'$resp' is not a valid hostname."
			;;
		esac
	done

	# Get directory info from *last* line starting with the server
	# name. This means the last install from a mirror will not keep
	# the specific directory info. But an install from a local
	# server *will* remember the specific directory info.
	set -- $(sed "/^$HTTP_SERVER/x;\$!d;x" $HTTP_LIST 2>/dev/null)
	resp=${1#*/}
	# If there is no directory specified, don't use the server name!
	[[ $resp == "$1" ]] && resp=
	if (($# > 1)); then
		# It's a mirror, since it has location info.
		resp=$resp/$HTTP_SETDIR
		_mirror=yes
	fi

	: ${HTTP_DIR:=pub/OpenBSD/$HTTP_SETDIR}
	ask_until "Server directory?" "${resp:-$HTTP_DIR}"
	HTTP_DIR=$resp
	_url_base="http://$HTTP_SERVER/$HTTP_DIR"

	# Get list of files from the server.
	# Assumes index file is "index.txt" for http (or proxy).
	# We can't use index.html since the format is server-dependent.
	_file_list=$(ftp -Vo - "$_url_base/index.txt" | sed 's/^.* //;s/
//')

	install_files "$_url_base" "$_file_list"

	# Remember where we installed from, to tell the cgi server.
	INSTALL_URL=$_url_base

	# Bake a package path only if we installed from a mirror.
	PACKAGE_PATH=
	if [[ -n $_mirror ]]; then
		PACKAGE_PATH=$(print -r -- "$_url_base" |
			sed -E "/\/(snapshots|[0-9]\.[0-9])\/($ARCH)\/*$/!d
				s!!/\1/packages/$(arch -s)/!;q")
	fi
}

# Ask for the path to the set files on an already mounted filesystem and start
# the set installation.
install_mounted_fs() {
	local _dir

	while :; do
		ask_until "Pathname to the sets? (or 'done')" "$SETDIR"
		[[ $resp == done ]] && return
		# Accept a valid /mnt2 or /mnt relative path.
		[[ -d /mnt2/$resp ]] && { _dir=/mnt2/$resp; break; }
		[[ -d /mnt/$resp ]] && { _dir=/mnt/$resp; break; }
		# Accept a valid absolute path.
		[[ -d /$resp ]] && { _dir=/$resp; break; }
		echo "The directory '$resp' does not exist."
		$AUTO && exit 1
	done

	install_files "file://$_dir" "$(ls $_dir/)"
}

# Install sets from CD-ROM.
install_cdrom() {
	local _drive=$1

	makedev $_drive && mount_mnt2 $_drive || return

	install_mounted_fs
}

# Ask for the disk device containing the set files, mount it and start the set
# installation.
install_disk() {
	if ! ask_yn "Is the disk partition already mounted?"; then
		ask_which "disk" "contains the $MODE media" \
			'$(bsort $(get_dkdevs))' \
			'$(bsort $(rmel $ROOTDISK $(get_dkdevs)))'
		[[ $resp == done ]] && return 1
		makedev $resp && mount_mnt2 $resp || return
	fi

	install_mounted_fs
}

# Ask for the nfs share details, mount it and start the set installation.
install_nfs() {
	local _tcp

	# Get the IP address of the server.
	ask_until "Server IP address or hostname?" "$NFS_ADDR"
	NFS_ADDR=$resp

	# Get the server path to mount.
	ask_until "Filesystem on server to mount?" "$NFS_PATH"
	NFS_PATH=$resp

	# Determine use of TCP.
	ask_yn "Use TCP transport? (requires TCP-capable NFS server)" && _tcp=-T

	# Mount the server.
	mount_nfs $_tcp -o ro -R 5 $NFS_ADDR:$NFS_PATH /mnt2 || return

	install_mounted_fs
}

# Mount filesystem containing the set files on device $1, optionally ask the
# user for the device name.
mount_mnt2() {
	local _dev=$1 _opts _file=/tmp/parts.$1 _parts

	disklabel $_dev 2>/dev/null |
		sed -En '/swap|unused/d;/^  [a-p]: /p' >$_file
	_parts=$(sed 's/^  \(.\): .*/\1/' $_file)
	set -- $_parts
	(($# == 0)) && { echo "No filesystems found on $_dev."; return 1; }

	if isin "c" $_parts; then
		# Don't ask questions if 'c' contains a filesystem.
		resp=c
	elif (($# == 1)); then
		# Don't ask questions if there's only one choice.
		resp=$1
	else
		# Display partitions with filesystems and ask which to use.
		cat $_file
		ask_which "$_dev partition" "has the $MODE sets" \
			'$(disklabel '$_dev' 2>/dev/null |
			sed -En '\''/swap|unused/d;/^  ([a-p]): .*/s//\1/p'\'')'
		[[ $resp == done ]] && return 1
	fi

	# Always mount msdos partitions with -s to get lower case names.
	grep -q "^  $resp: .*MSDOS" $_file && _opts="-s"
	mount -o ro,$_opts /dev/$_dev$resp /mnt2
}

# ------------------------------------------------------------------------------
# Functions used in install.sh/upgrade.sh and it's associates
# ------------------------------------------------------------------------------

# Ask for terminal type if on console, otherwise ask for/set keyboard layout.
set_term() {
	local _layouts
	export TERM=${TERM:-${MDTERM:-vt220}}

	if [[ -n $CONSOLE ]]; then
		ask "Terminal type?" $TERM
		export TERM=$resp
	else
		[[ -x /sbin/kbd ]] || return
		_layouts=$(bsort $(kbd -l | egrep -v "^(user|tables|encoding)"))
		while :; do
			ask "Choose your keyboard layout ('?' or 'L' for list)" "default"
			case $resp in
			[lL\?]) echo "Available layouts: $_layouts";;
			default) return;;
			*)	kbd $resp && { echo $resp >/tmp/kbdtype; return; };;
			esac
		done
	fi
}

# Configure the network.
donetconfig() {
	local _dn _ns _f1 _f2 _f3

	configure_ifs
	v4_defroute

	# As dhclient will populate /etc/resolv.conf, a symbolic link to
	# /tmp/resolv.conf.shadow, mv any such file to /tmp/resolv.conf
	# so it will eventually be copied to /mnt/etc/resolv.conf and will
	# not in the meantime remove the user's ability to choose to use it
	# or not, during the rest of the install.
	if [[ -f /tmp/resolv.conf.shadow ]]; then
		mv /tmp/resolv.conf.shadow /tmp/resolv.conf
		# Get/store nameserver address(es) as a blank separated list
		# and the default fully qualified domain name from *first*
		# domain given on *last* search or domain statement.
		while read -r -- _f1 _f2 _f3; do
			[[ $_f1 == nameserver ]] && _ns="${_ns:+$_ns }$_f2"
			[[ $_f1 == @(domain|search) ]] && _dn=$_f2
		done </tmp/resolv.conf
	fi

	# Get & apply fqdn to hostname. Don't ask if there's only one configured
	# interface and if it's managed by dhclient and if the domain name is
	# configured via dhclient too.
	resp="${_dn:=$(get_fqdn)}"
	if ifconfig dhcp >/dev/null 2>&1 && [[ $NIFS == 1 && -n $_dn ]]; then
		echo "Using DNS domainname $resp"
	else
		ask "DNS domain name? (e.g. 'bar.com')" "$resp"
	fi
	hostname "$(hostname -s).$resp"

	# Get & add nameservers to /tmp/resolv.conf. Don't ask if there's only
	# one configured interface and if it's managed by dhclient and if the
	# nameserver is configured via dhclient too.
	resp="${_ns:=none}"
	if ifconfig dhcp >/dev/null 2>&1 && [[ $NIFS == 1 && -n $_ns ]]; then
		echo "Using DNS nameservers at $resp"
	else
		ask "DNS nameservers? (IP address list or 'none')" "$resp"
	fi

	# Construct appropriate resolv.conf.
	if [[ $resp != none ]]; then
		echo "lookup file bind" >/tmp/resolv.conf
		for _ns in $resp; do
			echo "nameserver $_ns" >>/tmp/resolv.conf
		done
		cp /tmp/resolv.conf /tmp/resolv.conf.shadow
	fi
}

# Ask user about daemon startup on boot, X Window usage and console setup.
# The actual configuration is done later in apply().
questions() {
	local _d _cdef=no

	ask_yn "Start sshd(8) by default?" yes
	sshd=$resp

	aperture=
	resp=
	xdm=
	if [[ -n $DISPLAY ]]; then
		if [[ -n $(scan_dmesg '/^[a-z]*[01]: aperture needed/p') ]]; then
			ask_yn "Do you expect to run the X Window System?" yes &&
				aperture=$MDXAPERTURE
		fi
		if [[ -n $MDXDM && $resp != n ]]; then
			ask_yn "Do you want the X Window System to be started by xdm(1)?"
			xdm=$resp
		fi
	fi

	if [[ -n $CDEV ]]; then
		_d=${CPROM:-$CDEV}
		[[ -n $CONSOLE ]] && _cdef=yes
		ask_yn "Change the default console to $_d?" $_cdef
		defcons=$resp
		if [[ $resp == y ]]; then
			ask_which "speed" "should $_d use" \
				"9600 19200 38400 57600 115200" $CSPEED
			case $resp in
			done)	defcons=n;;
			*)	CSPEED=$resp;;
			esac
		fi
	fi
}

# Gather information for setting up the user later in apply().
user_setup() {
	local _q="Setup a user? (enter a lower-case loginname, or 'no')"

	while :; do
		ask "$_q" no
		case $resp in
		n|no)	return
			;;
		y|yes)	_q="No really, what is the lower-case loginname, or 'no'?"
			continue
			;;
		root|daemon|operator|bin|sshd|uucp|www|nobody|ftp)
			;;
		[a-z]*([a-z0-9_]))
			((${#resp} <= 31)) && break
			;;
		esac
		echo "$resp is not a useable loginname."
	done
	user=$resp
	while :; do
		ask "Full name for user $user?" $user
		case $resp in
		*[:\&,]*)
			echo "':', '&' or ',' are not allowed."
			;;
		*)
			((${#resp} <= 100)) && break
			echo "Too long."
			;;
		esac
	done
	username=$resp

	askpassword "Password for user $user?"
	userpass=$_password

	userkey=
	$AUTO && ask "Public ssh key for user $user" none &&
		[[ $resp != none ]] && userkey=$resp
}

# Ask user whether or not to allow logins to root in case sshd(8) is enabled.
# If no user is setup, show a hint to enable root logins, but warn about risks
# of doing so.
ask_root_sshd() {
	typeset -l _resp

	[[ $sshd == y ]] || return

	if [[ -z $user ]]; then
		echo "Since no user was setup, root logins via sshd(8) might be useful."
	fi
	echo "WARNING: root is targeted by password guessing attacks, pubkeys are safer."
	while :; do
		ask "Allow root ssh login? (yes, no, prohibit-password)" no
		_resp=$resp
		case $_resp in
		y|yes)	sshd_enableroot=yes
			;;
		n|no)	sshd_enableroot=no
			;;
		w|p|without-password|prohibit-password)
			sshd_enableroot=prohibit-password
			;;
		*)	echo "'$resp' is not a valid choice."
			$AUTO && exit 1
			continue
			;;
		esac
		break
	done
}

# Set TZ variable based on zonefile $1 and user selection.
set_timezone() {
	local _zonefile=$1 _zonepath _zsed _zoneroot=/usr/share/zoneinfo

	# If the timezone file is not available,
	# return immediately.
	[[ ! -f $_zonefile ]] && return

	# If configured in a previous call, return immediately.
	[[ -n $TZ ]] && return

	if [[ -h /mnt/etc/localtime ]]; then
		TZ=$(ls -l /mnt/etc/localtime 2>/dev/null)
		TZ=${TZ#*${_zoneroot#/mnt}/}
	fi

	# If neither the base or HTTP_LIST gave a hint, and this is the
	# early question, give up, and ask after the sets are installed.
	[[ $_zonefile = /var/tzlist && -z $TZ ]] && return

	while :; do
		ask "What timezone are you in? ('?' for list)" "$TZ"
		_zonepath=${resp%%*(/)}
		case $_zonepath in
		"")	continue
			;;
		"?")	grep -v /. $_zonefile | showcols
			continue
			;;
		esac

		while isin "$_zonepath/" $(<$_zonefile); do
			ask "What sub-timezone of '$_zonepath' are you in? ('?' for list)"
			_zsed=$(echo $_zonepath/ | sed 's,/,\\/,g')
			resp=${resp%%*(/)}
			case $resp in
			"")	;;
			"?")	sed -n "/^$_zsed/{s/$_zsed//;/\/./!p;}" $_zonefile | showcols;;
			*)	_zonepath=$_zonepath/$resp;;
			esac
		done

		if isin "$_zonepath" $(<$_zonefile); then
			TZ=${_zonepath#$_zoneroot}
			return
		fi

		echo -n "'${_zonepath}'"
		echo " is not a valid timezone on this system."
	done
}

# Get global root information. ie. ROOTDISK, ROOTDEV and SWAPDEV.
get_rootinfo() {
	while :; do
		echo "Available disks are: $(get_dkdevs | sed 's/^$/none/')."
		_ask "Which disk is the root disk? ('?' for details)" \
			$(get_dkdevs | sed 's/ .*//') || continue
		case $resp in
		"?")	diskinfo $(get_dkdevs)
			;;
		'')	;;
		*)	isin "$resp" $(get_dkdevs) && break
			echo "no such disk"
			;;
		esac
	done
	makedev $resp || exit

	ROOTDISK=$resp
	ROOTDEV=${ROOTDISK}a
	SWAPDEV=${ROOTDISK}b
}

# Start interface using the on-disk hostname.if file passed as argument $1.
# Much of this is gratuitously stolen from /etc/netstart.
ifstart () {
	local _hn=$1 if=${1#/mnt/etc/hostname.}

	((NIFS++))
	while :; do
		if [ "$cmd2" ]; then
			# we are carrying over from the 'read dt dtaddr'
			# last time
			set -- $cmd2
			af=$1 name=$2 mask=$3 bcaddr=$4 ext1=$5 cmd2=
			# make sure and get any remaining args in ext2,
			# like the read below
			i=1
			while [ i -lt 6 -a -n "$1" ]; do shift; let i=i+1; done
			ext2="$@"
		else
			# read the next line or exit the while loop
			read af name mask bcaddr ext1 ext2 || break
		fi
		# $af can be "dhcp", "rtsol", an address family,
		# commands, or a comment.
		case "$af" in
		"#"*|"!"*|"bridge"|"")
			# skip comments, user commands, bridges,
			# and empty lines
			continue
			;;
		"dhcp")	[ "$name" = "NONE" ] && name=
			[ "$mask" = "NONE" ] && mask=
			[ "$bcaddr" = "NONE" ] && bcaddr=
			dhcpif="$dhcpif $if"
			cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 down"
			if [[ -x /sbin/dhclient ]]; then
				cmd="$cmd; dhclient $if"
			else
				cmd="$cmd; echo /sbin/dhclient missing - skipping dhcp request."
			fi
			;;
		"rtsol")
			if ifconfig $if inet6 >/dev/null 2>&1; then
				rtsolif="$rtsolif $if"
				cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 up"
			else
				cmd="$cmd; echo no INET6 support - skipping rtsol request."
			fi
			;;
		*)	read dt dtaddr
			if [ "$name" = "alias" ]; then
				# perform a 'shift' of sorts
				alias=$name
				name=$mask
				mask=$bcaddr
				bcaddr=$ext1
				ext1=$ext2
				ext2=
			else
				alias=
			fi
			cmd="ifconfig $if $af $alias $name"
			case "$dt" in
			dest)	cmd="$cmd $dtaddr"
				;;
			*)
				cmd2="$dt $dtaddr"
				;;
			esac
			case $af in
			inet)
				if [ ! -n "$name" ]; then
					echo "/etc/hostname.$if: inet alone is invalid"
					return
				fi
				[ "$mask" ] && cmd="$cmd netmask $mask"
				if [ "$bcaddr" -a X"$bcaddr" != "XNONE" ]; then
					cmd="$cmd broadcast $bcaddr"
				fi
				[ "$alias" ] && rtcmd=";route -qn add -host $name 127.0.0.1"
				;;
			inet6)
				if [ ! -n "$name" ]; then
					echo "/etc/hostname.$if: inet6 alone is invalid"
					return
				fi
				[ "$mask" ] && cmd="$cmd prefixlen $mask"
				cmd="$cmd $bcaddr"
				;;
			*)	cmd="$cmd $mask $bcaddr"
				;;
			esac
			cmd="$cmd $ext1 $ext2$rtcmd" rtcmd=
			;;
		esac
		eval "$cmd"
	done <$_hn
}

# Configure the network during upgrade based on the on-disk configuration.
enable_network() {
	local _f _gw _trunks _svlans _vlans

	# Copy any network configuration files. N.B.: hosts already copied.
	for _f in dhclient.conf resolv.conf resolv.conf.tail; do
		if [[ -f /mnt/etc/$_f ]]; then
			cp /mnt/etc/$_f /etc/$_f
		fi
	done

	# Set the address for the loopback interface. Bringing the
	# interface up, automatically invokes the IPv6 address ::1.
	ifconfig lo0 inet 127.0.0.1/8

	# Configure all of the non-loopback interfaces which we know about.
	# Refer to hostname.if(5)
	for hn in /mnt/etc/hostname.*; do
		# Strip off prefix to get interface name.
		if=${hn#/mnt/etc/hostname.}
		if isin "${if%%+([0-9])}" $(ifconfig -C); then
			# Dynamic interfaces must be done later.
			case ${if%%+([0-9])} in
			trunk)	_trunks="$_trunks $hn";;
			svlan)	_svlans="$_svlans $hn";;
			vlan)	_vlans="$_vlans $hn";;
			esac
		else
			# 'Real' interfaces (if available) are done now.
			ifconfig $if >/dev/null 2>&1 && ifstart $hn
		fi
	done
	# Configure any dynamic interfaces now that 'real' ones are up.
	# ORDER IS IMPORTANT! (see /etc/netstart).
	for hn in $_trunks $_svlans $_vlans; do
		ifstart $hn
	done

	[[ -n $rtsolif ]] && ifconfig $rtsolif inet6 autoconf

	# /mnt/etc/mygate, if it exists, contains the address(es) of my
	# default gateway(s). Use for ipv4 if no interfaces configured via
	# dhcp. Use for ipv6 if no interfaces configured via rtsol.
	[[ -z $dhcpif ]] && stripcom /mnt/etc/mygate | while read _gw; do
		[[ $_gw == @(*:*) ]] && continue
		route -qn delete default >/dev/null 2>&1
		route -qn add -host default $_gw && break
	done
	[[ -z $rtsolif ]] && stripcom /mnt/etc/mygate | while read _gw; do
		[[ $_gw == !(*:*) ]] && continue
		route -qn delete -inet6 default >/dev/null 2>&1
		route -qn add -host -inet6 default $_gw && break
	done

	route -qn add -net 127 127.0.0.1 -reject >/dev/null
}

# Create a skeletal but useful /etc/fstab from /tmp/fstab by stripping all
# comment lines and dropping all filesystems which
#
# 1) can't be mounted (no mount_* command is found),
# 2) have 'xx' in the option field (usually /altroot),
# 3) have 'noauto' in the option field,
# 4) are nfs (since name resolution may not be present),
# 5) are on a vnd device.
#
# In addition,
#
# 1) delete 'softdep' options (no soft updates in ramdisk kernels),
# 2) mount non-ffs filesystems read only,
# 3) prepend '/mnt' to all mount points,
# 4) delete any trailing '/' from the mount point (e.g. root),
#
# If no /etc/fstab is created, do not proceed with install/upgrade.
munge_fstab() {
	local _dev _mp _fstype _opt _rest

	while read _dev _mp _fstype _opt _rest; do
		# Drop irrelevant lines and filesystems.
		[[ $_dev == @(/dev/vnd*|\#*) ||
			$_fstype == nfs ||
			! -f /sbin/mount_$_fstype ||
			$_opt == *noauto* ||
			$_opt == *xx* ]] && continue

		# Remove any softdep options, as soft updates are not
		# available in the ramdisk kernels.
		_opt=$(echo $_opt | sed 's/softdep//')

		# Change read-only ffs to read-write since we'll potentially
		# write to these filesystems.
		# Mount non-ffs filesystems read only.
		if [[ $_fstype == ffs ]]; then
			_opt=$(echo $_opt | sed 's/[[:<:]]ro[[:>:]]/rw/')
		else
			_opt=$(echo $_opt | sed 's/[[:<:]]rw[[:>:]]/ro/')
		fi

		# Write fs entry in fstab.
		# 1) prepend '/mnt' to the mount point.
		# 2) remove a trailing '/' from the mount point (e.g. root).
		echo $_dev /mnt${_mp%/} $_fstype $_opt $_rest

	done </tmp/fstab >/etc/fstab

	# If no /etc/fstab was created, we have nowhere to $MODE to.
	if [[ ! -s /etc/fstab ]]; then
		echo "Unable to create valid /etc/fstab."
		exit
	fi
}

# Preen all filesystems in /etc/fstab that have a /sbin/fsck_XXX and a
# fs_passno > 0, showing individual results, but skipping $ROOTDEV. This was
# already fsck'ed successfully.
#
# Exit if any fsck's fail (but do them all before exiting!).
check_fs() {
	local _dev _dn _mp _fstype _rest _fail _f _passno

	ask_yn "Force checking of clean non-root filesystems?" && _f=f

	while read _dev _mp _fstype _rest _rest _passno _rest; do
		_dn=$(get_dkdev_name "$_dev")
		[[ $ROOTDEV == @(${_dev#/dev/}|$_dn${_dev##*.}) ]] && continue
		[[ -f /sbin/fsck_$_fstype ]] || continue
		# Make sure device exists before fsck'ing it.
		makedev "$_dn" || continue
		((_passno > 0)) || continue
		echo -n "fsck -${_f}p $_dev..."
		if ! fsck -${_f}p $_dev >/dev/null 2>&1; then
			echo "FAILED. You must fsck $_dev manually."
			_fail=y
		else
			echo "OK."
		fi
	done </etc/fstab

	[[ -n $_fail ]] && exit
}

# Must mount filesystems manually, one at a time, so we can make sure the mount
# points exist.
mount_fs() {
	local _async=$1 _dev _mp _fstype _opt _rest _msg _fail

	while read _dev _mp _fstype _opt _rest; do
		# If not the root filesystem, make sure the mount
		# point is present.
		[[ $_mp == /mnt ]] || mkdir -p $_mp

		# Mount the filesystem. Remember any failure.
		_msg=$(mount -v -t $_fstype $_async -o $_opt $_dev $_mp) ||
			_fail="$_fail\n$_mp ($_dev)"
		echo $_msg | sed 's/, ctime=[^,)]*//'
	done </etc/fstab

	if [[ -n $_fail ]]; then
		# One or more mounts failed. Continue or abort?
		echo "\nWARNING! The following filesystems were not properly mounted:$_fail"
		ask_yn "Continue anyway?" || exit
	fi
}

# Feed the random pool some entropy before we read from it.
feed_random() {
	(dmesg; cat $CGI_INFO /*.conf; sysctl; route -n show; df;
		ifconfig -A; hostname) >/dev/random 2>&1
	if [[ -e /mnt/var/db/host.random ]]; then
		dd if=/mnt/var/db/host.random of=/dev/random bs=65536 count=1 \
			status=none
	fi
}

# Ask the user for locations of sets, and then install whatever sets the user
# selects from that location. Repeat as many times as the user needs to get all
# desired sets.
install_sets() {
	local _cddevs=$(get_cddevs) _d=$CGI_METHOD _locs="disk http" _src

	echo

	ifconfig netboot >/dev/null 2>&1 && : ${_d:=http}
	[[ -n $_cddevs ]] && : ${_d:=cd0}
	[[ -x /sbin/mount_nfs ]] && _locs="$_locs nfs"
	: ${_d:=http}

	if ! isin "$_d" $_cddevs $_locs; then
		for _src in http $_cddevs nfs disk; do
			isin "$_src" $_cddevs $_locs && _d=$_src && break
		done
	fi

	echo "Let's $MODE the sets!"
	while :; do
		_cddevs=$(get_cddevs)
		umount -f /mnt2 >/dev/null 2>&1

		ask "Location of sets? (${_cddevs:+$_cddevs }$_locs or 'done')" "$_d"
		case $resp in
		done)	sane_install && return
			;;
		[cC]*)	if [[ -n $_cddevs ]]; then
				set -- $_cddevs
				[[ $resp == [cC]?([dD]) ]] && resp=$1
				install_cdrom $resp && INSTALL_METHOD=$resp
			fi
			;;
		[dD]*)	install_disk && INSTALL_METHOD=disk
			;;
		[hH]*)	isin http $_locs && install_http && INSTALL_METHOD=http
			;;
		[nN]*)	isin nfs $_locs && install_nfs && INSTALL_METHOD=nfs
			;;
		*)	$AUTO && echo "'$resp' is not a valid choice." && exit 1
			;;
		esac

		[[ -n $INSTALL_METHOD ]] && _d=$INSTALL_METHOD
		sane_install quiet || $AUTO && _d=done
	done
}

# Apply configuration settings based on the previously gathered information.
apply() {
	if [[ $sshd == n ]]; then
		echo "sshd_flags=NO" >>/mnt/etc/rc.conf.local
	elif [[ -n $sshd_enableroot ]]; then
		# Only change sshd_config if the user choice is not the default.
		if ! grep -q "^#PermitRootLogin $sshd_enableroot\$" \
				/mnt/etc/ssh/sshd_config; then
			sed -i "s/^#\(PermitRootLogin\) .*/\1 $sshd_enableroot/" \
				/mnt/etc/ssh/sshd_config
		fi
	fi

	[[ -n $aperture ]] &&
		echo "machdep.allowaperture=$aperture # See xf86(4)" \
			>>/mnt/etc/sysctl.conf

	[[ $xdm == y && -x /mnt/usr/X11R6/bin/xdm ]] &&
		echo "xdm_flags=" >>/mnt/etc/rc.conf.local

	if [[ $defcons == y ]]; then
		cp /mnt/etc/ttys /tmp/ttys
		sed	-e "/^$CTTY/s/std.9600/std.${CSPEED}/" \
			-e "/^$CTTY/s/unknown/vt220	/" \
			-e "/$CTTY/s/off.*/on secure/" /tmp/ttys >/mnt/etc/ttys
		[[ -n $CPROM ]] &&
			echo "stty $CPROM $CSPEED\nset tty $CPROM" >>/mnt/etc/boot.conf
	fi

	ln -sf /usr/share/zoneinfo/$TZ /mnt/etc/localtime
}

# Return string suitable for the encrypted password field in master.passwd.
#
# 1) Without argument, return a single '*'.
# 2) Return argument unchanged if it looks like a encrypted password string
#    or if it consists of just 13 asterisks.
# 3) Otherwise return encrypted password string.
#
encr_pwd() {
	local _p=$1

	if [[ -z $_p ]]; then
		echo '*'
	elif [[ $_p == \$2?\$[0-9][0-9]\$* && ${#_p} > 40 ||
		$_p == '*************' ]]; then
		echo "$_p"
	else
		encrypt -b a -- "$_p"
	fi
}

# Store entropy for the next boot.
store_random() {
	dd if=/dev/random of=/mnt/var/db/host.random bs=65536 count=1 \
		status=none
	dd if=/dev/random of=/mnt/etc/random.seed bs=512 count=1 status=none
	chmod 600 /mnt/var/db/host.random /mnt/etc/random.seed
}

# Final steps common for installs and upgrades.
finish_up() {
	local _dev _mp _fstype _rest

	# Mount all known swap partitions.  This gives systems with little
	# memory a better chance at running 'MAKEDEV all'.
	if [[ -x /mnt/sbin/swapctl ]]; then
		/mnt/sbin/swapctl -a /dev/$SWAPDEV >/dev/null 2>&1
		# Can't do chmod && swapctl -A because devices are not yet
		# created on install'ed systems. On upgrade'ed system there
		# is a small chance the device does not exist on the ramdisk
		# and will thus not get mounted.
		while read _dev _mp _fstype _rest; do
			[[ $_fstype == swap ]] &&
				/mnt/sbin/swapctl -a $_dev >/dev/null 2>&1
		done </mnt/etc/fstab
	fi

	# Create or update pkg.conf with the new package path, if any.
	if [[ -n $PACKAGE_PATH ]]; then
		grep -v '^[ 	]*installpath[ 	]*=' /mnt/etc/pkg.conf 2>/dev/null >/tmp/pkgconf
		print -r -- "installpath = $PACKAGE_PATH" >>/tmp/pkgconf
		cp /tmp/pkgconf /mnt/etc/pkg.conf
	fi

	echo -n "Making all device nodes..."
	(cd /mnt/dev; sh MAKEDEV all
		# Make sure any devices we found during probe are created in the
		# installed system.
		for _dev in $(get_dkdevs) $(get_cddevs) $MTDEVS; do
			sh MAKEDEV $_dev
		done
	)
	echo "done."

	# We may run some programs in chroot, and some of them might be
	# dynamic.  That is highly discouraged, but let us play it safe.
	rm -f /mnt/var/run/ld.so.hints

	[[ -x /mnt/$MODE.site ]] && chroot /mnt /$MODE.site

	# In case this is a softraid device, make sure all underlying
	# device nodes exist.
	makedev $(bioctl $ROOTDISK 2>/dev/null | sed -n 's/.*<\(.*\)>$/\1/p')
	md_installboot $ROOTDISK

	if [[ -f /mnt/bsd.mp ]] && ((NCPU > 1)); then
		echo "Multiprocessor machine; using bsd.mp instead of bsd."
		mv /mnt/bsd /mnt/bsd.sp 2>/dev/null
		mv /mnt/bsd.mp /mnt/bsd
	fi

	# Ensure that makewhatis is run on reboot.
	[[ $MODE == upgrade ]] &&
		echo "/usr/sbin/sysmerge -b" >>/mnt/etc/rc.sysmerge
	echo "/usr/sbin/makewhatis" >>/mnt/etc/rc.firsttime

	# Email installer questions and their answers to root on next boot.
	prep_root_mail /tmp/$MODE.resp "$(hostname) $MODE response file"

	store_random

	# Pat on the back.
	cat <<__EOT

CONGRATULATIONS! Your LibertyBSD $MODE has been successfully completed!
To boot the new system, enter 'reboot' at the command prompt.
__EOT
	[[ $MODE == install ]] && cat <<__EOT
When you login to your new system the first time, please read your mail
using the 'mail' command.
__EOT

	md_congrats
	$AUTO && >/ai.done
}

do_install(){
	# Ask for/set the system hostname and add the hostname specific siteXX set.
	ask_until "System hostname? (short form, e.g. 'foo')" "$(hostname -s)"
	[[ ${resp%%.*} != $(hostname -s) ]] && hostname "$resp"
	THESETS="$THESETS site$VERSION-$(hostname -s).tgz"

	echo

	# Configure the network.
	donetconfig

	# If there's network connectivity, fetch list of mirror servers
	((NIFS)) && mirror_fetch

	echo

	while :; do
		askpassword "Password for root account?"
		_rootpass="$_password"
		[[ -n "$_password" ]] && break
		echo "The root password must be set."
	done

	# Ask for the root user public ssh key during autoinstall.
	rootkey=
	if $AUTO; then
		ask "Public ssh key for root account?" none
		[[ $resp != none ]] && rootkey=$resp
	fi

	# Ask user about daemon startup on boot, X Window usage and console setup.
	questions

	# Gather information for setting up the initial user account.
	user_setup
	ask_root_sshd

	# Set TZ variable based on zonefile and user selection.
	set_timezone /var/tzlist

	echo

	# Get information about ROOTDISK, etc.
	get_rootinfo

	DISKS_DONE=
	FSENT=

	# Remove traces of previous install attempt.
	rm -f /tmp/fstab*

	# Configure the disk(s).
	while :; do
		# Always do ROOTDISK first, and repeat until it is configured.
		if ! isin "$ROOTDISK" $DISKS_DONE; then
			resp=$ROOTDISK
			rm -f /tmp/fstab
		else
			# Force the user to think and type in a disk name by
			# making 'done' the default choice.
			ask_which "disk" "do you wish to initialize" \
				'$(get_dkdevs_uninitialized)' done
			[[ $resp == done ]] && break
		fi
		_disk=$resp
		configure_disk $_disk || continue
		DISKS_DONE=$(addel $_disk $DISKS_DONE)
	done

	# Write fstab entries to fstab in mount point alphabetic order
	# to enforce a rational mount order.
	for _mp in $(bsort $FSENT); do
		_pp=${_mp##*!}
		_mp=${_mp%!*}
		echo -n "$_pp $_mp ffs rw"

		# Only '/' is neither nodev nor nosuid. i.e. it can obviously
		# *always* contain devices or setuid programs.
		[[ $_mp == / ]] && { echo " 1 1"; continue; }

		# Every other mounted filesystem is nodev. If the user chooses
		# to mount /dev as a separate filesystem, then on the user's
		# head be it.
		echo -n ",nodev"

		# The only directories that the install puts suid binaries into
		# (as of 3.2) are:
		#
		# /sbin
		# /usr/bin
		# /usr/sbin
		# /usr/libexec
		# /usr/libexec/auth
		# /usr/X11R6/bin
		#
		# and ports and users can do who knows what to /usr/local and
		# sub directories thereof.
		#
		# So try to ensure that only filesystems that are mounted at
		# or above these directories can contain suid programs. In the
		# case of /usr/libexec, give blanket permission for
		# subdirectories.
		case $_mp in
		/sbin|/usr)			;;
		/usr/bin|/usr/sbin)		;;
		/usr/libexec|/usr/libexec/*)	;;
		/usr/local|/usr/local/*)	;;
		/usr/X11R6|/usr/X11R6/bin)	;;
		*)	echo -n ",nosuid"	;;
		esac
		echo " 1 2"
	done >>/tmp/fstab

	# Create a skeletal /etc/fstab which is usable for the installation process.
	munge_fstab

	# Use async options for faster mounts of the filesystems.
	mount_fs "-o async"

	# Feed the random pool some entropy before we read from it.
	feed_random

	# Ask the user for locations, and install whatever sets the user selected.
	install_sets

	# Set 'wxallowed' mount option for the filesystem /usr/local resides on.
	_mp=$(df /mnt/usr/local | sed '$!d')
	_mp=${_mp##*/mnt}
	sed -i "s#\(${_mp:-/} ffs rw\)#\1,wxallowed#" /tmp/fstab

	# If we did not succeed at setting TZ yet, we try again
	# using the timezone names extracted from the base set.
	if [[ -z $TZ ]]; then
		(cd /mnt/usr/share/zoneinfo
			ls -1dF $(tar cvf /dev/null [A-Za-y]*) >/mnt/tmp/tzlist )
		echo
		set_timezone /mnt/tmp/tzlist
		rm -f /mnt/tmp/tzlist
	fi

	# If we got a timestamp from the cgi server, and that time diffs by more
	# than 120 seconds, ask if the user wants to adjust the time.
	if _time=$(http_time) && _now=$(date +%s) &&
		(( _now - _time > 120 || _time - _now > 120 )); then
		_tz=/mnt/usr/share/zoneinfo/$TZ
		if ask_yn "Time appears wrong.  Set to '$(TZ=$_tz date -r "$(http_time)")'?" yes; then
			# We do not need to specify TZ below since both date
			# invocations use the same one.
			date $(date -r "$(http_time)" "+%Y%m%d%H%M.%S") >/dev/null
			# N.B. This will screw up SECONDS.
		fi
	fi

	# If we managed to talk to the cgi server before, tell it what
	# location we used... so it can perform magic next time.
	if [[ -s $HTTP_LIST ]]; then
		_i=${INSTALL_URL:+install=$INSTALL_URL&}
		_i=$_i${TZ:+TZ=$TZ&}
		_i=$_i${INSTALL_METHOD:+method=$INSTALL_METHOD}
		_i=${_i%&}
		[[ -n $_i ]] && ftp -Vao - \
			"http://129.128.5.191/cgi-bin/ftpinstall.cgi?$_i" >/dev/null 2>&1 &
	fi

	# Ensure an enabled console has the correct speed in /etc/ttys.
	sed "/^console.*on.*secure.*$/s/std\.[0-9]*/std.$(stty speed </dev/console)/" \
		/mnt/etc/ttys >/tmp/ttys
	mv /tmp/ttys /mnt/etc/ttys

	echo -n "Saving configuration files..."

	# Save any leases obtained during install.
	(cd /var/db; for _f in dhclient.leases.*; do
		[[ -f $_f ]] && mv $_f /mnt/var/db/.
	done)

	# Move configuration files from /tmp to /mnt/etc.
	hostname >/tmp/myname

	# Append entries to installed hosts file, changing '1.2.3.4 hostname'
	# to '1.2.3.4 hostname.$FQDN hostname'. Leave untouched lines containing
	# domain information or aliases. These are lines the user added/changed
	# manually.

	# Add common entries.
	echo "127.0.0.1\tlocalhost" >/mnt/etc/hosts
	echo "::1\t\tlocalhost" >>/mnt/etc/hosts

	# Note we may have no hosts file if no interfaces were configured.
	if [[ -f /tmp/hosts ]]; then
		_dn=$(get_fqdn)
		while read _addr _hn _aliases; do
			if [[ -n $_aliases || $_hn != ${_hn%%.*} || -z $_dn ]]; then
				echo "$_addr\t$_hn $_aliases"
			else
				echo "$_addr\t$_hn.$_dn $_hn"
			fi
		done </tmp/hosts >>/mnt/etc/hosts
		rm /tmp/hosts
	fi

	# Append dhclient.conf to installed dhclient.conf.
	_f=dhclient.conf
	[[ -f /tmp/$_f ]] && { cat /tmp/$_f >>/mnt/etc/$_f; rm /tmp/$_f; }

	# Possible files to copy from /tmp: fstab hostname.* kbdtype mygate
	#     myname ttys boot.conf resolv.conf sysctl.conf resolv.conf.tail
	# Save only non-empty (-s) regular (-f) files.
	(cd /tmp; for _f in fstab hostname* kbdtype my* ttys *.conf *.tail; do
		[[ -f $_f && -s $_f ]] && mv $_f /mnt/etc/.
	done)

	echo "done."

	# Apply configuration settings based on information from questions().
	apply

	# Create user account based on information from user_setup().
	if [[ -n $user ]]; then
		_encr=$(encr_pwd "$userpass")
		_home=/home/$user
		uline="${user}:${_encr}:1000:1000:staff:0:0:${username}:$_home:/bin/ksh"
		echo "$uline" >>/mnt/etc/master.passwd
		echo "${user}:*:1000:" >>/mnt/etc/group
		echo ${user} >/mnt/root/.forward

		_home=/mnt$_home
		mkdir -p $_home
		(cd /mnt/etc/skel; cp -pR . $_home)
		(umask 077 && sed "s,^To: root\$,To: ${username} <${user}>," \
			/mnt/var/mail/root >/mnt/var/mail/$user )
		chown -R 1000:1000 $_home /mnt/var/mail/$user
		sed -i -e "s@^wheel:.:0:root\$@wheel:\*:0:root,${user}@" \
			/mnt/etc/group 2>/dev/null

		# During autoinstall, add public ssh key to authorized_keys.
		[[ -n "$userkey" ]] &&
			print -r -- "$userkey" >>$_home/.ssh/authorized_keys
	fi

	# Store root password and rebuild password database.
	if [[ -n "$_rootpass" ]]; then
		_encr=$(encr_pwd "$_rootpass")
		sed -i -e "s@^root::@root:${_encr}:@" /mnt/etc/master.passwd 2>/dev/null
	fi
	pwd_mkdb -p -d /mnt/etc /etc/master.passwd

	# During autoinstall, add root user's public ssh key to authorized_keys.
	[[ -n "$rootkey" ]] && (
		umask 077
		mkdir /mnt/root/.ssh
		print -r -- "$rootkey" >>/mnt/root/.ssh/authorized_keys
	)

	# Perform final steps common to both an install and an upgrade.
	finish_up
}

do_upgrade(){
	# Have the user confirm that $ROOTDEV is the root filesystem.
	get_rootinfo
	while :; do
		ask "Root filesystem?" $ROOTDEV
		resp=${resp##*/}
		[[ -b /dev/$resp ]] && break
		echo "$resp is not a block device."
	done
	ROOTDEV=$resp

	echo -n "Checking root filesystem (fsck -fp /dev/$ROOTDEV)..."
	fsck -fp /dev/$ROOTDEV >/dev/null 2>&1 || { echo "FAILED."; exit; }
	echo "OK."

	echo -n "Mounting root filesystem (mount -o ro /dev/$ROOTDEV /mnt)..."
	mount -o ro /dev/$ROOTDEV /mnt || { echo "FAILED."; exit; }
	echo "OK."

	# The fstab, hosts and myname files are required.
	for _f in /mnt/etc/{fstab,hosts,myname}; do
		[[ -f $_f ]] || { echo "No $_f!"; exit; }
		cp $_f /tmp/${_f##*/}
	done
	hostname $(stripcom /tmp/myname)
	THESETS="$THESETS site$VERSION-$(hostname -s).tgz"

	# Configure the network.
	enable_network

	# If there's network connectivity, fetch the list of mirror servers
	((NIFS)) && mirror_fetch

	# Create a skeletal /etc/fstab which is usable for the upgrade process.
	munge_fstab

	# fsck -p non-root filesystems in /etc/fstab.
	check_fs

	# Mount filesystems in /etc/fstab.
	umount /mnt || { echo "Can't umount $ROOTDEV!"; exit; }
	mount_fs

	# Feed the random pool some entropy before we read from it.
	feed_random

	# Ask the user for locations, and install whatever sets the user selected.
	install_sets

	# Perform final steps common to both an install and an upgrade.
	finish_up
}

# ------------------------------------------------------------------------------
#
# Initial actions common to both installs and upgrades.
#
# Some may require machine dependent routines, which may
# call functions defined above, so it's safest to put this
# code here rather than at the top of the file.
#
# ------------------------------------------------------------------------------

# Parse parameters.
AUTO=false
MODE=
PROGNAME=${0##*/}
RESPFILE=
while getopts "af:m:" opt; do
	case $opt in
	a)	AUTO=true;;
	f)	RESPFILE=$OPTARG;;
	m)	MODE=$OPTARG;;
	*)	usage;;
	esac
done
shift $((OPTIND-1))
(($# == 0)) || usage

if [[ -z $MODE ]]; then
	case $PROGNAME in
	autoinstall)		AUTO=true;;
	install|upgrade)	MODE=$PROGNAME;;
	*)			exit 1;;
	esac
fi

# Include machine-dependent functions and definitions.
#
# The following functions must be provided:
#	md_congrats()		  - display friendly message
#	md_installboot()	  - install boot-blocks on disk
#	md_prep_disklabel()	  - put an OpenBSD disklabel on the disk
#	md_consoleinfo()	  - set CDEV, CTTY, CSPEED, CPROM
#
# The following variables can be provided if required:
#	MDSETS	    - list of files to add to THESETS
#	MDTERM      - 'vt220' assumed if not provided
#	MDDKDEVS    - '/^[sw]d[0-9][0-9]* /s/ .*//p' assumed if not provided
#	MDCDDEVS    - '/^cd[0-9][0-9]* /s/ .*//p'    assumed if not provided
#	MDMTDEVS    - '/^[cms]t[0-9][0-9]* /s/ .*//p'
#	MDXAPERTURE - set machdep.allowaperture=value in sysctl.conf
#	NCPU	    - the number of cpus for mp capable arches
. install.md

# Make sure lock is initially released.
rm -df /tmp/lock

# The dmesg listener will check for the existence of this file and sends a
# signal to the child process if the dmesg output differs from the contents
# of that file.
rm -f /tmp/update

if ! $AUTO; then
	# Start listener process looking for dmesg changes.
	(
		while :; do
			lock
			if [[ -e /tmp/update && "$(dmesg)" != "$(</tmp/update)" ]]; then
				dmesg >/tmp/update
				kill -TERM 2>/dev/null $$ || exit 1
			fi
			unlock
			sleep .5
		done
	) |&
	CPPID=$!

	# Kill the child on exit.
	retrap
fi

AI_MODE=
AI_SERVER=

ROOTDISK=
ROOTDEV=
PACKAGE_PATH=

SETDIR="$VNAME/$ARCH"
CGI_INFO=/tmp/cgiinfo
CGI_TZ=
CGI_TIME=
CGI_METHOD=
INSTALL_URL=
INSTALL_METHOD=
HTTP_DIR=
HTTP_SEC=/tmp/httpsec
HTTP_LIST=/tmp/httplist
WLANLIST=/tmp/wlanlist

# Do not limit ourselves during installs or upgrades.
for _opt in d f l m n p s; do
	ulimit -$_opt unlimited
done

# Extract and save one boot's worth of dmesg.
dmesg | sed -n '/^OpenBSD /h;/^OpenBSD /!H;${g;p;}' >/var/run/dmesg.boot

# Are we in a real release, or a snapshot?  If this is a snapshot
# install media, default us to a snapshot directory.
HTTP_SETDIR=$SETDIR
set -- $(scan_dmesg "/^OpenBSD $VNAME\([^ ]*\).*$/s//\1/p")
[[ $1 == -!(stable) ]] && HTTP_SETDIR=snapshots/$ARCH

# Scan /var/run/dmesg.boot for interesting devices.
MTDEVS=$(scan_dmesg "${MDMTDEVS:-/^[cms]t[0-9][0-9]* /s/ .*//p}")
NIFS=0
DISPLAY=$(scan_dmesg '/^wsdisplay[0-9]* /s/ .*//p')

CONSOLE=$(scan_dmesg '/^\([^ ]*\).*: console$/s//\1/p')
CONSOLE=${CONSOLE% }
[[ -n $CONSOLE ]] && CSPEED=$(stty speed </dev/console)

# Look for the serial device matching the console. If we are not installing
# from a serial console, just find the first serial device that could be used
# as a console. If a suitable device is found, set CDEV, CTTY, CSPEED, CPROM.
md_consoleinfo

# Selected sets will be installed in the order they are listed in $THESETS.
# Ensure that siteXX.tgz is the *last* set listed so its contents overwrite
# the contents of the other sets, not the other way around.
SETS=$(echo {base,comp,man,game,xbase,xshare,xfont,xserv}$VERSION.tgz)
DEFAULTSETS="${DEFAULTSETS:-bsd bsd.rd} $SETS"
THESETS="bsd bsd.rd bsd.mp $MDSETS $SETS site$VERSION.tgz"
SANESETS="${SANESETS:-bsd} base${VERSION}.tgz"

# Prepare COLUMNS sanely.
COLUMNS=$(stty -a </dev/console | sed -n '/columns/{s/^.* \([0-9]*\) columns.*$/\1/;p;}')
((COLUMNS == 0)) && COLUMNS=80

# Decide upon an editor.
: ${EDITOR:=ed}
[[ -x /usr/bin/vi ]] && EDITOR=vi
export EDITOR COLUMNS

# umount all filesystems, just in case we are re-running install or upgrade.
cd /
umount -af 1>/dev/null 2>&1

# Make sure only successful dhcp requests retain their state.
for _if in $(get_ifs dhcp); do
	set -- $(v4_info $_if)
	[[ $1 == UP && -n $2 ]] && continue
	ifconfig $_if delete down -group dhcp 2>/dev/null
done

# Interactive or automatic installation?
if ! $AUTO; then
	cat <<__EOT
At any prompt except password prompts you can escape to a shell by
typing '!'. Default answers are shown in []'s and are selected by
pressing RETURN.  You can exit this program at any time by pressing
Control-C, but this can leave your system in an inconsistent state.

__EOT
elif [[ -z $RESPFILE ]]; then
	if ! get_responsefile; then
		echo "No response file found; non-interactive mode aborted."
		exit 1
	fi
	rm -f /ai.done
	echo "Performing non-interactive $AI_MODE..."
	/$AI_MODE -af /ai.$AI_MODE.conf 2>&1 </dev/null | sed 's/^.*
//;w/ai.log'
	if [[ -f /ai.done ]]; then
		# Email ai.log to root on next boot.
		prep_root_mail /ai.log "$(hostname) $AI_MODE log"
		exec reboot
	else
		echo "failed; check /ai.log"
		exit 1
	fi
else
	cp $RESPFILE /ai.conf || exit
fi

# Configure the terminal and keyboard.
set_term

# In case of restart, delete previously logged answers.
rm -f /tmp/$MODE.resp

case $MODE in
install)	do_install;;
upgrade)	do_upgrade;;
esac


code:
LibertyBSD ?.? (UNKNOWN)

Welcome to LibertyBSD: The unofficial, OpenBSD-based, completely
free operating system.

If you find any bugs or FSDG non-compliant data, please tell us
on our #libertybsd Freenode channel, our online fourm, or with
the sendbug(1) utility.

For information on Free Software, read the free-software(7) man page.

LibertyBSD is *not* affiliated with the OpenBSD project. All
trademarks are the property of their respective owners.

repo_name: pecg/LibertyBSD

path: files/motd

license: unknown


code:
From deraadt@do-not-reply.openbsd.org Sun May  1 07:59:59 MST 2016
Return-Path: root
Date: May  1 07:59:59 MST 2016
From: deraadt@do-not-reply.openbsd.org (Theo de Raadt)
To: root
Subject: Welcome to [LibertyBSD] 6.0!

This message attempts to describe the most basic initial questions that a
system administrator of a [LibertyBSD] box might have.  You are urged to save
this message for later reference.

For more information on how to set up your [LibertyBSD] system, refer to the
"afterboot" man page (i.e. after you exit the mail subsystem, type
"man afterboot").  If you are not familiar with how to read man pages, type
"man man" at a shell prompt and read the entire thing.  Pay specific
attention to the "man -k keyword" option, which will permit you to find the
man page you are looking for more easily; for instance, "man -k ethernet".
The GNU "info" subsystem is also installed with further documentation
resources: to read info pages type "info".  (The info subsystem behaves like
the popular emacs editor.)

Again, PLEASE READ THE MANUAL PAGES.  [OpenBSD] developers have spent countless
hours improving them so that they are clear and precise.

If you have installed the X11 file sets during the install process, you can
find further information regarding configuration in the file /usr/X11R6/README.

Several popular binary packages (pre-compiled applications) are available
for [the supported architectures.]

[...]

Significant efforts were made to centralize all system configuration in the
/etc directory.  You should be able to find each of the configuration files
you seek there, lightly documented.  In particular, much of the configuration
has been centralized in the file /etc/rc.conf.  You should not need to ever
edit the file /etc/rc.   The files /etc/rc.securelevel and /etc/rc.local exist
for this purpose; the first is run before the system has gone into secure
mode; the second is run afterwards (if in doubt, add your tools to rc.local).

[LibertyBSD] is free software.  You can do with it as you like, subject to very few
conditions (described at www.OpenBSD.org/policy.html).  But free software isn't
written without money.  Network links, hardware costs, release engineering and
testing work all take money and significant effort on the part of [those who
have made OpenBSD, and, by doing so, LibertyBSD] what it is. [...]
For information on how you can help, please see
www.OpenBSD.org/goals.html and visit www.OpenBSD.org/donations.html to see a
list of those who have donated money, equipment, or other resources to ensure
OpenBSD continues.

------------------

This message was written by Theo de Raadt, the founder and leader of OpenBSD.
In order to prevent his statement being misrepresented, sections modified by
LibertyBSD contributors are inside brackets.

From faceless@do-not-reply.libertybsd.net Sun May  1 07:59:59 MST 2016
Return-Path: root
Date: May  1 07:59:59 MST 2016
From: faceless@do-not-reply.libertybsd.net (...)
To: root
Subject: Some Additional Notes

LibertyBSD is an unofficial downstream of OpenBSD-- non-free firmware is
removed, and references to non-free software have been removed.
As such-- and I can't stress this enough-- don't go bothering OpenBSD
developers, users, or anyone that has to do with OpenBSD about issues or
bugs you encounter.
Please don't waste their time.
If you believe you have found a legitimate bug, make sure it can be
reproduced in OpenBSD before reporting it to the people I told you not to
bother.
If you run into any issues, give us a holler at the #libertybsd Freenode
channel or with the sendbug(1) utility. If it's a software freedom issue
(I.E. you've found something non-compliant with the FSDG in LibertyBSD),
it's probably best if you holler louder.

If you'd like more information on Free Software or what the Free Software
Distribution Guidelines (FSDG) entail, check the man pages free-software(7)
and fsdg(7).

If you'd like to support the brilliant hackers and contributors of OpenBSD
as well as the project's development, please donate to the project or
foundation. I urge you to donate as much as you would spend on a certain
popular proprietary operating system.
http://www.openbsd.org/donations.html
http://www.openbsdfoundation.org/donations.html


code:
#!/bin/ksh

#########################
# Name: libdeblob.sh
# Main: jadedctrl
# Lisc: ISC
# Desc: Functions to be
#       used for deblobbing
#       and rebranding OBSD
#       sources for the LBSD
#       project.
#########################

# Turns a file and it's path into a friendly filename
# Usage: filetize $filepath
filetize() {
	echo $1 | sed 's|/|\^|g'
}

# Vice-versa, clearly.
# Usage: defiletize $filetizedpath
unfiletize() {
	echo $1 | sed 's|\^|/|g'
}

# Prints $1 number of spaces.
# Usage: space $number
space() {
	i=0
	while [ $i != $1 ]
	do
		printf " "
		i=$((i+1))
	done
}

# Replace a string in a file
# Usage: rep $replacee $replacer $file
rep() {
	if [ -e "$PATCH_DIR/$(filetize "$3")" ]
	then
		sed 's^'"$1"'^'"$2"'^g' $PATCH_DIR/$(filetize "$3") > $PATCH_DIR/$(filetize "$3").tmp
		mv $PATCH_DIR/$(filetize "$3").tmp $PATCH_DIR/$(filetize "$3")
		diff ${SRC_DIR}/$3 $PATCH_DIR/$(filetize "$3") > $PATCH_DIR/$(filetize "$3").patch
	else
		sed 's^'"$1"'^'"$2"'^g' ${SRC_DIR}/${3} > $PATCH_DIR/$(filetize "$3")
		diff ${SRC_DIR}/$3 $PATCH_DIR/$(filetize "$3") > $PATCH_DIR/$(filetize "$3").patch
	fi

}

# Delete a string in a file
# Usage: strdel $string $file
strdel() {
	rep "$1" "" $2
}

# Inserts a new line after another
# Usage: lineadd $string $newline $file
lineadd() {
	if [ -e "$PATCH_DIR/$(filetize "$3")" ]
	then
		sed 's^'"$1"'^'"$1"'\n'"$2"'^' $PATCH_DIR/$(filetize "$3") > $PATCH_DIR/$(filetize "$3").tmp
		mv $PATCH_DIR/$(filetize "$3").tmp $PATCH_DIR/$(filetize "$3")
		diff ${SRC_DIR}/$3 $PATCH_DIR/$(filetize "$3") > $PATCH_DIR/$(filetize "$3").patch
	else
		sed 's^'"$1"'^'"$1"'\n'"$2"'^' ${SRC_DIR}/${3} > $PATCH_DIR/$(filetize "$3")
		diff ${SRC_DIR}/$3 $PATCH_DIR/$(filetize "$3") > $PATCH_DIR/$(filetize "$3").patch
	fi
}

# Removes a line.
# Usage linedel $string $file
linedel() {
	if [ -e "$PATCH_DIR/$(filetize "$2")" ]
	then
		grep -v "$1" $PATCH_DIR/$(filetize "$2") > $PATCH_DIR/$(filetize "$2").tmp
		mv $PATCH_DIR/$(filetize "$2").tmp $PATCH_DIR/$(filetize "$2")
		diff ${SRC_DIR}/$2 $PATCH_DIR/$(filetize "$2") > $PATCH_DIR/$(filetize "$2").patch
	else
		echo otherwise
		grep -v "$1" "${SRC_DIR}/$2" > $PATCH_DIR/$(filetize "$2")
		diff ${SRC_DIR}/$2 $PATCH_DIR/$(filetize "$2") > $PATCH_DIR/$(filetize "$2").patch
		echo otherhell
	fi
}

# "Copies" a file
# Usage: filedel $file $dest
filecp() {
	cp $1 $PATCH_DIR/ADD_$(filetize "$2")
}

# "Deletes" a file
# Usage: filedel $file
filedel() {
	echo $PATCH_DIR $1
	touch $PATCH_DIR/RM_$(filetize $1)
}

# Applies patches.
apply() {
	for file in $PATCH_DIR/*
	do
		realname=$(echo $file | sed 's^.*/^^' | sed 's/ADD_//' | sed 's/RM_//')
		realname="$(unfiletize "$realname")"

		if echo "$file" | grep "RM_" > /dev/null
		then
			realname=$(echo "$realname" | sed 's/RM_//')
			echo "Deleting $realname in three seconds..."
			echo "3"; sleep 1
			echo "2"; sleep 1
			echo "1"; sleep 1
			if rm -rf ${SRC_DIR}/$realname
			then
				echo "$realname deleted" >> apply.log
				echo "$realname deleted"
			else
				echo "!!! $realname NOT deleted" >> apply.log
				echo "!!! $realname NOT deleted"
			fi
		elif echo "$file" | grep "ADD_" > /dev/null
		then
			realname=$(echo "$realname" | sed 's/ADD_//')
			echo "Copying $file to $realname..."
			if cp $file ${SRC_DIR}/$realname
			then
				echo "$realname copied from $file" >> apply.log
			else
				echo "!!! $realname NOT copied from $file" >> apply.log
			fi
		elif echo "$file" | grep ".patch$" > /dev/null
		then
			if patch "${SRC_DIR}/$(echo $realname | sed 's/\.patch//')" < $file
			then
				echo "${SRC_DIR}/$(echo $realname | sed 's/\.patch//') patched from $file" >> apply.log
			else
				echo "!!! ${SRC_DIR}/$(echo $realname | sed 's/\.patch//') NOT patched from $file" >> apply.log
			fi
		fi
	done
}

self_destruct_sequence() {
	echo "$1 will be deleted in five seconds."
	echo "CTRL-C now to avoid this fate!"
	echo "5"; sleep 1
	echo "4"; sleep 1
	echo "3"; sleep 1
	echo "2"; sleep 1
	echo "1"; sleep 1
	printf "0\nDestruction!"
	rm -rf "$1"
}


code:
#!/bin/sh

#########################
# Name: man_deblob.sh
# Main: jadedctrl
# Lisc: ISC
# Desc: Delobbing OBSD man
#       pages for use in
#       LBSD.
#########################

# Usage: man_deblob.sh $SRC_DIR

. ./libdeblob.sh

PATCH_DIR=/tmp/man_deblob

if [ -e $PATCH_DIR ]
then
        self_destruct_sequence $PATCH_DIR
	mkdir $PATCH_DIR
else
        mkdir $PATCH_DIR
fi

if test -z $1
then
        SRC_DIR=/usr/src
else
        SRC_DIR=$1
fi

# man4
fw_list="acx adw adv athn bnx bwi drm fxp inteldrm ips ipw iwi iwm iwn kue malo myx neo otus pgt ral"
fw_list="$fw_list radeondrm rsu rtwn rum siop tht thtc ti uath udl ulpt upgt urtwn uvideo wpi yds zyd"
for man_blob in $fw_list
do
        strdel " ${man_blob}.4" share/man/man4/Makefile
        strdel "\^${man_blob}.4" share/man/man4/Makefile
        linedel "${man_blob}.4" distrib/sets/lists/man/mi
done
linedel "MLINKS+=adv.4 adw.4" share/man/man4/Makefile
linedel "MLINKS+=drm.4 inteldrm.4 drm.4 radeondrm.4" share/man/man4/Makefile
linedel "MLINKS+=tht.4 thtc.4" share/man/man4/Makefile


code:
#!/bin/sh

#########################
# Name: man_rebrand.sh
# Main: jadedctrl
# Lisc: ISC
# Desc: Rebranding OBSD man
#       pages for use in
#       LBSD.
#########################

# Usage: man_rebrand.sh $SRC_DIR
# Often: man_rebrand.sh /usr/src/share/man

. ./libdeblob.sh

PATCH_DIR=/tmp/man_deblob

if [ -e $PATCH_DIR ]
then
        self_destruct_sequence $PATCH_DIR
	mkdir $PATCH_DIR
else
        mkdir $PATCH_DIR
fi

if test -z $1
then
        SRC_DIR=/usr/src/share/man
else
        SRC_DIR=$1
fi

# man1
rep "administrators with" "administrators with LibertyBSD" share/man/man1/help.1
linedel "^Other$" "man1/help.1"
linedel "references include the FAQ" share/man/man1/help.1
linedel "https://openbsd.org/faq"
linedel "which is mostly intended for administrators" share/man/man1/help.1
linedel "a working knowledge of" share/man/man1/help.1
linedel "\.Ux \." "man1/help.1"
linedel "There are also mailing lists in place" share/man/man1/help.1
rep "\.Ox ," "LibertyBSD," share/man/man1/help.1
rep "available;" "available; LibertyBSD" share/man/man1/help.1
linedel "\.Ox" share/man/man1/help.1

rep "\.Ox" "LibertyBSD" share/man/man1/gcc-local.1
rep "\.Ox" "LibertyBSD" share/man/man1/clang-local.1

# man3
rep "\.Ox" "LibertyBSD" share/man/man3/makedev.3
rep "\.Ox" "LibertyBSD" share/man/man3/sysexits.3

# man4


code:
#!/bin/ksh

#########################
# Name: src_deblob.sh
# Main: jadedctrl
# Lisc: ISC
# Desc: Delobbing OBSD base
#       sources for use in
#       LBSD.
#########################

# Usage: src_deblob.sh

. ./libdeblob.sh

PATCH_DIR=/tmp/src_deblob/

if [ -e $PATCH_DIR ]
then
        self_destruct_sequence $PATCH_DIR
else
        mkdir $PATCH_DIR
fi

if test -z $1
then
        SRC_DIR=/usr/src/
	mkdir $SRC_DIR
else
        SRC_DIR=$1
fi

echo $SRC_DIR

arch_list="amd64 i386"

for arch in $arch_list # not all archs have ramdisk_cd... fix!
do
	linedel "\${DESTDIR}/etc/firmware/kue.*" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/bnx-b06" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/bnx-b09"  distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/bnx-rv2p" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/bnx-xi-rv2p" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/bnx-xi90-rv2p" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/ral-rt2561" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/ral-rt2561s" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/ral-rt2661" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/ral-rt2860" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/ral-rt2573" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/ral-rt2870" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/ral-rt3071" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/rum-rt2573" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/run-rt2870" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/run-rt3071" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/tigon1" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/tigon2" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/zd1211" distrib/${arch}/ramdisk_cd/list.local
	linedel "\${DESTDIR}/etc/firmware/zd1211b" distrib/${arch}/ramdisk_cd/list.local
done

linedel "pkg_add pkg_sign" usr.sbin/pkg_add/Makefile
rep "pkg_add fw_update" "pkg_add pkg_sign" usr.sbin/pkg_add/Makefile
strdel "fw_update.1" usr.sbin/pkg_add/Makefile
strdel "fw_update" usr.sbin/pkg_add/Makefile
linedel "FwUpdate.pm" usr.sbin/pkg_add/Makefile

# Remove fw man pages and fw_update from base set, etc.
for arch in $arch_list
do
	linedel "./usr/libdata/perl5/OpenBSD/FwUpdate.pm" "distrib/sets/lists/base/md.${arch}"
	linedel "./usr/sbin/fw_update" "distrib/sets/lists/base/md.${arch}"

	linedel "./usr/share/man/man1/fw_update.1" "distrib/sets/lists/base/md.${arch}"
	linedel "./etc/firmware/4c9904" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/3c990-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-at76c503-i3863-ext" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-at77c503-i3863-int" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-at76c503-rfmd-acc-ext" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-at76c503-rfmd-acc-int" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-at76c505-rfmd-ext" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-at76c505-rfmd-int" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-intersil-ext" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-intersil-int" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-rfmd-ext" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-rfmd-int" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-rfmd2958-ext" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-rfmd2958-int" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-rfmd2958smc-ext" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/atu-rfmd2958smc-int" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/bnx-b06" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/bnx-b09" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/bnx-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/bnx-rv2p" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/bnx-xi-rv2p" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/bnx-xi90-rv2p" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/cs4280" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/cs4280-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/fxp-d101a" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/fxp-d101b0" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/fxp-d101ma" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/fxp-d101s" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/fxp-d102" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/fxp-d102c" "distrib/sets/lists/base/md.${arch}"
	linedel "./etc/firmware/fxp-d102e" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/fxp-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/kue" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/kue-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/myx-eth_z8e" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/myx-ethp_z8e" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/myx-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/ral-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/ral-rt2561" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/ral-rt2561s" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/ral-rt2661" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/ral-rt2860" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/rum-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/rum-rt2573" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/run-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/run-rt2870" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/run-rt3071" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/symbol-eprim" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/symbol-esec" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/symbol-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/tht" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/tht-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/tigon-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/tigon1" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/tigon2" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/tusb3410" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/tusb3410-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/udl_huffman" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/yds" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/yds-license" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/zd1211" "distrib/sets/lists/base/md.${arch}"
        linedel "./etc/firmware/zd1211-license" "distrib/sets/lists/base/md.${arch}"
	linedel "./etc/firmware/zd1211b" "distrib/sets/lists/base/md.${arch}"

done

linedel "./usr/libdata/perl5/OpenBSD/FwUpdate.pm" distrib/sets/lists/base/mi
linedel "./usr/sbin/fw_update" distrib/sets/lists/base/mi

# Add Free Software-related man pages
filecp files/fsdg.7 share/man/man7/fsdg.7
filecp files/free-software.7 share/man/man7/free-software.7
rep "environ.7 glob.7 hier.7 hostname.7 intro.7 kgdb.7 " "environ.7 free-software.7 fsdg.7 glob.7 hier.7 " share/man/man7/Makefile
rep "library-specs.7 mailaddr.7" "hostname.7 intro.7 kgdb.7 library-specs.7 mailaddr.7" share/man/man7/Makefile
lineadd "./usr/share/man/man7/free-software.7" "./usr/share/man/man7/packages.7" distrib/sets/lists/man/mi
lineadd "./usr/share/man/man7/fsdg.7" "./usr/share/man/man7/packages.7" distrib/sets/lists/man/mi
linedel "./usr/share/man/man1/fw_update.1" distrib/sets/lists/man/mi

apply