xref: /openbsd/distrib/miniroot/install.sub (revision 3d8817e4)
1#	$OpenBSD: install.sub,v 1.638 2011/04/18 16:52:10 thib Exp $
2#	$NetBSD: install.sub,v 1.5.2.8 1996/09/02 23:25:02 pk Exp $
3#
4# Copyright (c) 1997-2009 Todd Miller, Theo de Raadt, Ken Westerback
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# Copyright (c) 1996 The NetBSD Foundation, Inc.
28# All rights reserved.
29#
30# This code is derived from software contributed to The NetBSD Foundation
31# by Jason R. Thorpe.
32#
33# Redistribution and use in source and binary forms, with or without
34# modification, are permitted provided that the following conditions
35# are met:
36# 1. Redistributions of source code must retain the above copyright
37#    notice, this list of conditions and the following disclaimer.
38# 2. Redistributions in binary form must reproduce the above copyright
39#    notice, this list of conditions and the following disclaimer in the
40#    documentation and/or other materials provided with the distribution.
41#
42# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
43# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
44# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
45# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
46# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
47# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
48# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
49# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
50# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
52# POSSIBILITY OF SUCH DAMAGE.
53#
54
55# OpenBSD install/upgrade script common subroutines and initialization code
56
57# Include machine-dependent functions and definitions.
58#
59# The following functions must be provided:
60#	md_congrats()		  - display friendly message
61#	md_installboot()	  - install boot-blocks on disk
62#	md_prep_disklabel()	  - put an OpenBSD disklabel on the disk
63#	md_consoleinfo()	  - set CDEV, CTTY, CSPEED, CPROM
64#
65# The following variables can be provided if required:
66#	MDSETS	    - list of files to add to THESETS
67#	MDTERM      - 'vt220' assumed if not provided
68#	MDDKDEVS    - '/^[sw]d[0-9][0-9]* /s/ .*//p' assumed if not provided
69#	MDCDDEVS    - '/^cd[0-9][0-9]* /s/ .*//p'    assumed if not provided
70#	MDMTDEVS    - '/^[cms]t[0-9][0-9]* /s/ .*//p'
71#	MDXAPERTURE - set machdep.allowaperture=value in sysctl.conf
72. install.md
73
74set_term() {
75	local _layouts
76
77	export TERM=${TERM:-${MDTERM:-vt220}}
78	if [[ -n $CONSOLE ]]; then
79		ask "Terminal type?" $TERM
80		export TERM=$resp
81	else
82		[[ -x /sbin/kbd ]] || return
83		_layouts=$(bsort $(kbd -l | egrep -v "^(user|tables|encoding)"))
84		while :; do
85			ask "Choose your keyboard layout ('?' or 'L' for list)" "default"
86			case $resp in
87			"?"|L|l) echo "Available layouts: $_layouts" ;;
88			default) return ;;
89			*)	kbd $resp && { echo $resp >/tmp/kbdtype ; return ; } ;;
90			esac
91		done
92	fi
93}
94
95# Echo the file $1 to standard output, skipping any lines that begin with a
96# '#'. i.e. strip comment lines from the file.
97stripcom () {
98	local _l
99
100	[[ -f $1 ]] || return
101
102	while read _l; do
103		[[ -n ${_l%%#*} ]] && echo $_l
104	done <$1
105}
106
107# Prints the supplied parameters properly escaped for future sh/ksh parsing.
108# Quotes are added if needed, so you should not do that yourself.
109quote() (
110	# Since this is a subshell we won't pollute the calling namespace
111	for a; do
112		alias Q=$a; a=$(alias Q); print -rn -- " ${a#Q=}"
113	done | sed '1s/ //'
114	echo
115)
116
117scan_dmesg() {
118	bsort $(sed -ne "$1" /var/run/dmesg.boot)
119}
120
121scan_disknames() {
122	local _n _oifs=$IFS
123	IFS=","
124	bsort $(for _n in $(sysctl -n hw.disknames); do echo "${_n%%:*} "; done | sed -ne "$1")
125	IFS=$_oifs
126}
127
128get_dkdevs () {
129	echo $(scan_disknames "${MDDKDEVS:-/^[sw]d[0-9][0-9]* /s/ .*//p}")
130}
131
132get_cddevs () {
133	echo $(scan_disknames "${MDCDDEVS:-/^cd[0-9][0-9]* /s/ .*//p}")
134}
135
136get_ifdevs() {
137	ifconfig "$@" 2>&- \
138		| egrep -v '^[[:space:]]|(bridge|enc|gif|gre|lo|pflog|pfsync|ppp|sl|tun)[[:digit:]]+:' \
139		| sed -ne 's/^\(.*\):.*/\1/p'
140}
141
142get_drive() {
143	ask_which "$1" "contains the $MODE media" "$2" "$3"
144	[[ $resp == done ]] && return 1
145	makedev $resp || return 1
146	return 0
147}
148
149mount_mnt2() {
150	local _dev=$1 _opts _file=/tmp/parts.$1 _parts
151
152	disklabel $_dev 2>/dev/null | grep '^  [a-p]: '	\
153		| egrep -v "swap|unused" >$_file
154
155	_parts=$(sed -e 's/^  \(.\): .*/\1/' $_file)
156	set -- $_parts
157	[[ $# == 0 ]] && { echo "No filesystems found on $_dev." ; return 1 ; }
158
159	if isin "c" $_parts; then
160		# Don't ask questions if 'c' contains a filesystem.
161		resp=c
162	elif [[ $# == 1 ]]; then
163		# Don't ask questions if there's only one choice.
164		resp=$1
165	else
166		# Display partitions with filesystems and ask which to use.
167		cat /tmp/parts.$_dev
168		ask_which "$_dev partition" "has the $MODE sets" \
169			'$(disklabel '$_dev' 2>/dev/null | grep "^  [a-p]: " |
170			egrep -v "swap|unused" |
171			sed '\''s/^  \(.\): .*/\1/'\'')'
172		[[ $resp == done ]] && return 1
173	fi
174
175	# Always mount msdos partitions with -s to get lower case names.
176	grep -q "^  $resp: .*MSDOS" $_file && _opts="-s"
177	mount -o ro,$_opts /dev/$_dev$resp /mnt2
178}
179
180# Ask for a password, saving the input in $resp.
181#    Display $1 as the prompt.
182#    *Don't* allow the '!' options that ask does.
183#    *Don't* echo input.
184#    *Don't* interpret "\" as escape character.
185askpass() {
186	set -o noglob
187	stty -echo
188	read -r resp?"$1 "
189	stty echo
190	set +o noglob
191	echo
192}
193
194# Make sure lock is initially released
195rm -df /tmp/lock
196
197# Acquire lock
198lock() {
199	while ! mkdir /tmp/lock 2>&- && sleep .1; do done
200}
201
202# Release lock
203unlock() {
204	rm -d /tmp/lock 2>&-
205}
206
207# Add trap to kill the listener process
208retrap() {
209	trap '>&- && kill -KILL $cppid 2>&-; echo; stty echo; exit 0' \
210		INT EXIT TERM
211}
212
213# The dmesg listener will check for the existance of this file and send a
214# signal to the child process if the dmesg output differs from the contents
215# of that file
216rm -f /tmp/update
217
218# Start listener process looking for dmesg changes
219(
220	while :; do
221		lock
222		if test -e /tmp/update && [[ "`dmesg`" != "`cat /tmp/update`" ]]; then
223			dmesg >/tmp/update
224			kill -TERM 2>&- $$ || exit 1
225		fi
226		unlock
227		sleep .5
228	done
229) |&
230cppid=$!
231
232# Kill the child on exit
233retrap
234
235# Issue a read into the global variable $resp. If the dmesg output is
236# changed while inside this function, the current read will be aborted
237# and the function will return a non-zero value. Normally, the caller
238# will then reprint any prompt and call the function again.
239_ask() {
240	local _int _redo=0 _pid
241
242	trap "_int=1" INT
243	trap "_redo=1" TERM
244	lock; dmesg >/tmp/update; unlock
245	read resp
246	lock; rm /tmp/update; unlock
247	if (( _redo )); then
248		stty raw
249		stty -raw
250	else
251		case $resp in
252		!)	echo "Type 'exit' to return to install."
253			sh
254			_redo=1
255			;;
256		!*)	eval "${resp#?}"
257			_redo=1
258			;;
259		esac
260	fi
261	retrap
262	(( _int )) && kill -INT $$
263	return $_redo
264}
265
266# Ask for user input.
267#
268#    $1    = the question to ask the user
269#    $2    = the default answer
270#
271# Save the user input (or the default) in $resp.
272#
273# Allow the user to escape to shells ('!') or execute commands
274# ('!foo') before entering the input.
275ask() {
276	local _question=$1 _default=$2
277
278	while :; do
279		echo -n "$_question "
280		[[ -z $_default ]] || echo -n "[$_default] "
281		_ask && : ${resp:=$_default} && break
282	done
283}
284
285# Ask for a password twice, saving the input in $_password
286askpassword() {
287	local _oifs=$IFS
288	IFS=
289	while :; do
290		askpass "Password for $1 account? (will not echo)"
291		_password=$resp
292
293		askpass "Password for $1 account? (again)"
294		# N.B.: Need quotes around $resp and $_password to preserve leading
295		#       or trailing spaces.
296		[[ "$resp" == "$_password" ]] && break
297
298		echo "Passwords do not match, try again."
299	done
300	IFS=$_oifs
301}
302
303user_setup() {
304	local _q="Setup a user? (enter a lower-case loginname, or 'no')"
305
306	while :; do
307		ask "$_q" no
308		case $resp in
309		n|no)	return ;;
310		y|yes)	_q="No really, what is the lower-case loginname, or 'no'?"
311			continue ;;
312		root|daemon|operator|bin|smmsp|popa3d) ;;
313		sshd|uucp|www|named|proxy|nobody|ftp) ;;
314		[a-z]*([a-z0-9_]))
315			(( ${#resp} <= 31 )) && break ;;
316		esac
317		echo "$resp is not a useable loginname."
318	done
319	user=$resp
320	while :; do
321		ask "Full user name for $user?" $user
322		case $resp in
323		*[:\&,]*)
324			echo "':', '&' or ',' are not allowed." ;;
325		*)
326			(( ${#resp} <= 100 )) && break
327			echo "Too long." ;;
328		esac
329	done
330	username=$resp
331
332	askpassword $user
333	userpass=$_password
334
335	if [[ $sshd == y ]]; then
336		ask_yn "Since you set up a user, disable sshd(8) logins to root?" yes
337		sshd_disableroot=$resp
338	fi
339
340}
341
342# Ask for user input until a non-empty reply is entered.
343#
344#    $1    = the question to ask the user
345#    $2    = the default answer
346#
347# Save the user input (or the default) in $resp.
348ask_until() {
349	resp=
350	while [[ -z $resp ]] ; do
351		ask "$1" "$2"
352	done
353}
354
355# Ask the user for a y or n, and insist on 'y', 'yes', 'n' or 'no'.
356#
357#    $1    = the question to ask the user
358#    $2    = the default answer (assumed to be 'n' if empty).
359#
360# Return 'y' or 'n' in $resp.
361ask_yn() {
362	local _q=$1 _a=${2:-no} _resp
363	typeset -l _resp
364
365	while :; do
366		ask "$_q" "$_a"
367		_resp=$resp
368		case $_resp in
369		y|yes)	resp=y ; return ;;
370		n|no)	resp=n ; return ;;
371		esac
372	done
373}
374
375# Ask for the user to select one value from a list, or 'done'.
376#
377# $1 = name of the list items (disk, cd, etc.)
378# $2 = question to ask
379# $3 = list of valid choices
380# $4 = default choice, if it is not specified use the first item in $3
381#
382# N.B.! $3 and $4 will be "expanded" using eval, so be sure to escape them
383#       if they contain spooky stuff
384#
385# At exit $resp holds selected item, or 'done'
386ask_which() {
387	local _name=$1 _query=$2 _list=$3 _def=$4 _dynlist _dyndef
388
389	while :; do
390		# Put both lines in ask prompt, rather than use a
391		# separate 'echo' to ensure the entire question is
392		# re-ask'ed after a '!' or '!foo' shell escape.
393		eval "_dynlist=\"$_list\""
394		eval "_dyndef=\"$_def\""
395
396		# Clean away whitespace and determine the default
397		set -o noglob
398		set -- $_dyndef; _dyndef="$1"
399		set -- $_dynlist; _dynlist="$*"
400		set +o noglob
401		(( $# < 1 )) && resp=done && return
402
403		: ${_dyndef:=$1}
404		echo "Available ${_name}s are: $_dynlist."
405		echo -n "Which one $_query? (or 'done') "
406		[[ -n $_dyndef ]] && echo -n "[$_dyndef] "
407		_ask || continue
408		[[ -z $resp ]] && resp="$_dyndef"
409
410		# Quote $resp to prevent user from confusing isin() by
411		# entering something like 'a a'.
412		isin "$resp" $_dynlist done && break
413		echo "'$resp' is not a valid choice."
414	done
415}
416
417# test the first argument against the remaining ones, return success on a match
418isin() {
419	local	_a=$1 _b
420
421	shift
422	for _b; do
423		[[ $_a == $_b ]] && return 0
424	done
425	return 1
426}
427
428# add first argument to list formed by the remaining arguments
429# adds to the tail if the element does not already exist
430addel() {
431	local	_a=$1
432
433	shift
434
435	echo -n "$*"
436	isin "$_a" $* || echo -n " $_a"
437}
438
439# remove all occurrences of first argument from list formed by
440# the remaining arguments
441rmel() {
442	local	_a=$1 _b
443
444	shift
445	for _b; do
446		[[ $_a != $_b ]] && echo -n "$_b "
447	done
448}
449
450bsort() {
451	local _l _a=$1 _b
452
453	[[ $# -gt 0 ]] || return
454
455	shift
456	for _b; do
457		if [[ $_a != $_b ]] ; then
458			if [[ $_a > $_b ]] ; then
459				_l="$_a $_l"; _a=$_b
460			else
461				_l="$_b $_l"
462			fi
463		fi
464	done
465
466	# Output the smallest value found.
467	echo -n "$_a "
468
469	# Sort remaining values.
470	bsort $_l
471}
472
473# show a list (passed via ordered arguments) in column output using ls
474showcols() {
475	local _l _cdir=/tmp/cdir
476	set -A _clist
477	mkdir -p $_cdir
478	rm -rf -- $_cdir/*
479	while read _l; do
480		[ "$_l" ] || continue
481		mkdir -p /tmp/cdir/"$_l"
482		_clist[${#_clist[*]}]="$_l"
483	done
484	(cd $_cdir; ls -Cdf "${_clist[@]}")
485	rm -rf -- $_cdir
486}
487
488# Offer to shell out for manual network configuration, and do so if
489# the user accepts the offer.
490manual_net_cfg() {
491	ask_yn "Do you want to do any manual network configuration?"
492
493	[[ $resp == y ]] && { echo "Type 'exit' to return to $MODE." ; sh ; }
494}
495
496# Create a device.
497#
498# $1 = name of the device to create.
499makedev() {
500	local _dev=$1
501
502	if [[ ! -r /dev/MAKEDEV ]] ; then
503		echo "MAKEDEV not found. Can't create device nodes."
504		return 1
505	fi
506
507	(cd /dev && sh MAKEDEV "$_dev") >/dev/null 2>&1
508}
509
510# Create an entry in the hosts file. If an entry with the
511# same symbolic name and address family already exists, delete it.
512# $1 - IP address (v6 if it contains ':', else v4)
513# $2 - symbolic name
514addhostent() {
515	local _addr=$1 _name=$2 _delim="."
516
517	[[ -z $_addr || -z $_name ]] && return
518
519	[[ $_addr == *:* ]] && _delim=":"
520
521	sed "/^[0-9a-fA-F]*[$_delim].*[ 	]$_name\$/d" /tmp/hosts \
522		>/tmp/hosts.new 2>/dev/null
523	mv /tmp/hosts.new /tmp/hosts
524
525	echo "$_addr $_name" >>/tmp/hosts
526}
527
528# Show list of available sets and let the user select which sets to install.
529#
530# $1 = available sets
531# $2 = already selected sets
532#
533# Set $resp to list of selected sets.
534select_sets() {
535	local _avail=$1 _selected=$2 _f _action _col=$COLUMNS
536	# account for 4 spaces added to the sets list
537	let COLUMNS=_col-8
538
539	cat <<__EOT
540
541Select sets by entering a set name, a file name pattern or 'all'. De-select
542sets by prepending a '-' to the set name, file name pattern or 'all'. Selected
543sets are labelled '[X]'.
544__EOT
545	while :; do
546		for _f in $_avail; do
547			isin $_f $_selected && echo "[X] $_f" || echo "[ ] $_f"
548		done | showcols | sed 's/^/    /'
549		ask "Set name(s)? (or 'abort' or 'done')" done
550
551		set -o noglob
552		for resp in $resp; do
553			case $resp in
554			abort)	_selected=; break 2 ;;
555			done)	break 2 ;;
556			-*)	_action=rmel ;;
557			*)	_action=addel ;;
558			esac
559			resp=${resp#[+-]}
560			[[ $resp = all ]] && resp=*
561
562			for _f in $_avail; do
563				[[ $_f = $resp ]] && _selected=$($_action $_f $_selected)
564			done
565		done
566	done
567
568	set +o noglob
569	COLUMNS=$_col
570
571	resp=$_selected
572}
573
574configure_ifs() {
575	local _first _ifdevs _ifs _name _hn _vl=0 _vd _vi _p _tags
576
577	# In case of restart, discover last vlan configured.
578	while :; do
579		_vd=$(ifconfig vlan$_vl 2>&1)
580		[[ $_vd == @(*no such interface*) ]] && break
581		[[ $_vd == @(vlan$_vl: flags=0<>*) ]] && break
582		: $(( _vl++ ))
583	done
584	_vd=
585
586	while :; do
587		# Create new vlan if possible.
588		ifconfig vlan$_vl create >/dev/null 2>&1
589		ask_which "network interface" "do you wish to configure" \
590			'$(get_ifdevs)' \
591			${_p:-'$( (ifconfig netboot 2>/dev/null | sed -n '\''1s/:.*//p'\''; get_ifdevs) | sed q )'}
592		[[ $resp == done ]] && break
593
594		_ifs=$resp
595		_hn=/tmp/hostname.$_ifs
596		rm -f $_hn
597
598		# If the offered vlan is chosen, ask the relevant
599		# questions and bring it up
600		if [[ $_ifs == vlan[0-9]* ]]; then
601			# Get existing tag for this vlan.
602			_vi=$(ifconfig $_ifs 2>/dev/null | \
603				sed -n 's/vlan: \([0-9]*\).*/\1/p')
604			# Get list of all in-use tags.
605			_tags=$(ifconfig vlan 2>/dev/null | \
606				sed -n 's/vlan: \([0-9]*\).*/\1/p')
607			# Current tag is a valid tag for this vlan.
608			[[ -n $_tags ]] && _tags=$(rmel "$_vi" $_tags)
609			if [[ -z $_vi ]]; then
610				_vi=0
611				while (( (_vi += 1) < 4096 )); do
612					! isin "$_vi" $_tags && break
613				done
614			fi
615			_ifdevs=$(get_ifdevs)
616			set -- $_ifdevs
617			while [[ $1 == vlan[0-9]* ]]; do
618				shift
619			done
620			ask "Which interface:tag should $_ifs be on?" "${_vd:=$1}:$_vi"
621			_vd=${resp%%:*}
622			_vi=${resp##*:}
623
624			# Validate that $_vd is a real interface
625			if ! (isin "$_vd" $_ifdevs && [[ $_vd != vlan[0-9]* ]]); then
626				echo "Invalid interface choice '$_vd'"
627				_vd=
628				continue
629			fi
630
631			# Validate range of $_vi as 1-4095, and $_vi not in use.
632			if (( _vi < 1 || _vi > 4095 )) || isin "$_vi" $_tags; then
633				echo "Invalid or in-use vlan tag '$_vi'"
634				continue
635			fi
636
637			# hostname.$_vd must say something, anything, to
638			# make sure it is up.
639			grep -qs "^up" /tmp/hostname.$_vd || \
640				echo "up" >>/tmp/hostname.$_vd
641			ifconfig $_vd up
642
643			# Make sure a hostname.$_ifs is created with this info.
644			ifconfig $_ifs destroy >/dev/null 2>&1
645			ifconfig $_ifs vlan $_vi vlandev $_vd
646			echo "vlan $_vi vlandev $_vd" >>$_hn
647			# Create a new vlan if we just configured the highest.
648			[[ ${_ifs##vlan} == $_vl ]] && (( _vl += 1 ))
649		fi
650
651		# Test if it is an 802.11 interface
652		ifconfig $_ifs 2>&- | grep -q "^[[:space:]]*ieee80211:" &&
653			ieee80211_config $_ifs $_hn
654
655		# First interface configured will use the hostname without
656		# asking the user.
657		resp=$(hostname -s)
658		[[ -n $_first && $_first != $_ifs ]] && \
659			ask "Symbolic (host) name for $_ifs?" $resp
660		_name=$resp
661
662		v4_config $_ifs $_name $_hn
663		v6_config $_ifs $_name $_hn
664
665		if [[ -f $_hn ]]; then
666			chmod 640 $_hn
667			(( nifs += 1 ))
668			: ${_first:=$_ifs}
669			_p=done
670		fi
671	done
672}
673
674# Output '<UP | DOWN> [<addr> <netmask> <rest of inet line>]'.
675#
676# $1 == interface
677v4_info() {
678	ifconfig $1 inet | sed -n '
679		1s/.*<UP,.*/UP/p
680		1s/.*<.*/DOWN/p
681		/inet/s/netmask//
682		/inet/s///p'
683}
684
685# Obtain and output the inet6 information related to the given
686# interface. Should output '<UP/DOWN> <addr> <prefixlen> <rest of inet line> '.
687#
688# $1 == interface
689v6_info() {
690	ifconfig $1 inet6 | sed -n '
691		1s/.*<UP,.*/UP/p
692		1s/.*<.*/DOWN/p
693		/scopeid/d
694		/inet6/s///p'
695}
696
697# Construct etc/dhclient.conf and issue DHCP request. Return FALSE if
698# no IP address assigned to $1.
699#
700# $1 == interface
701# $2 == hostname (optional).
702dhcp_request() {
703	local _ifs=$1 _hn=$2
704
705	echo "lookup file bind" >/etc/resolv.conf.tail
706
707	if [[ -n $_hn ]]; then
708		_hn="send host-name \"$_hn\";"
709		echo "Issuing hostname-associated DHCP request for $_ifs."
710	else
711		echo "Issuing free-roaming DHCP request for $_ifs."
712	fi
713
714	cat >/etc/dhclient.conf <<__EOT
715initial-interval 1;
716$_hn
717request subnet-mask, broadcast-address, routers, domain-name,
718	domain-name-servers, host-name;
719__EOT
720
721	ifconfig $_ifs group dhcp >/dev/null 2>&1
722	dhclient $_ifs
723
724	set -- $(v4_info $_ifs)
725
726	if [[ $1 == UP && -n $2 ]]; then
727		# Move configuration files to where they will be copied to the
728		# installed system. Overwrites configuration information from
729		# last successful dhcp attempt.
730		mv /etc/dhclient.conf /tmp/dhclient.conf
731		mv /etc/resolv.conf.tail /tmp/resolv.conf.tail
732		return 0
733	fi
734
735	ifconfig $_ifs delete down -group dhcp 2>&-
736	rm /etc/dhclient.conf /etc/resolv.conf.tail
737	return 1
738}
739
740# Convert a hex value to dotted decimal format
741hextodec() {
742	local _d _b
743
744	for _b in $(echo ${1#0x} | sed 's/\(..\)/\1 /g'); do
745		_d=$_d.$((0x$_b))
746	done
747	echo ${_d#.}
748}
749
750# Perform an 802.11 interface network scan.
751# The result is cached in $WLANLIST
752# $1 == interface
753ieee80211_scan() {
754	# N.B. Skipping quoted nwid's for now
755	[[ -f $WLANLIST ]] ||
756		ifconfig $1 scan |
757		sed -n 's/^		nwid \([^"]\)/\1/p' > $WLANLIST
758	cat $WLANLIST
759}
760
761# Configures an 802.11 interface
762#
763# $1 == interface
764# $2 == hostname
765ieee80211_config() {
766	local _ifs=$1 _hn=$2 _prompt _nwid _haswpa=0 _err
767
768	# Reset 802.11 settings and determine wpa capability
769	ifconfig $_ifs -nwid -nwkey
770	ifconfig $_ifs -wpa 2>&- && _haswpa=1
771
772	# Empty scan cache
773	rm -f $WLANLIST
774
775	while [[ -z $_nwid ]]; do
776		ask_until "Access point? (ESSID, 'any', list# or '?')" "any"
777		case "$resp" in
778		+([0-9]))
779			_nwid=$(ieee80211_scan $_ifs | sed -n "${resp}s/ .*//p")
780			[[ -z $_nwid ]] && echo "There is no line $resp."
781			;;
782		\?)	ieee80211_scan $_ifs |
783				sed -n 's/^\([^ ]*\) chan .* bssid \([^ ]*\) .*$/	\1 (\2)/p' |
784				less -XEN
785			;;
786		*)	_nwid=$resp ;;
787		esac
788	done
789
790	# 'any' implies that only open access points are considered
791	if [[ $_nwid != any ]]; then
792		ifconfig $_ifs nwid "$_nwid"
793		quote nwid "$_nwid" >>$_hn
794
795		_prompt="Security protocol? (O)pen, (W)EP"
796		[[ $_haswpa == 1 ]] && _prompt="$_prompt, WPA-(P)SK"
797		while :; do
798			ask_until "$_prompt" "O"
799			case "$_haswpa-$resp" in
800			?-[Oo])	break
801				;;
802			?-[Ww])	ask_until "WEP key? (will echo)"
803				# Make sure ifconfig accepts the key
804				if _err=$(ifconfig $_ifs nwkey "$resp" 2>&1) &&
805					[[ -z $_err ]]; then
806					quote nwkey "$resp" >>$_hn
807					break
808				fi
809				echo "$_err"
810				;;
811			1-[Pp])	ask_until "WPA passphrase? (will echo)"
812				# Make sure ifconfig accepts the key
813				if ifconfig $_ifs wpakey "$resp"; then
814					quote wpakey "$resp" >>$_hn
815					break
816				fi
817				;;
818			*)	echo "'$resp' is not a valid choice."
819				;;
820			esac
821		done
822	fi
823}
824
825v4_config() {
826	local _ifs=$1 _name=$2 _hn=$3 _prompt _addr _mask
827
828	if ifconfig $_ifs | grep 'groups:.* dhcp' >/dev/null 2>&1; then
829		_addr=dhcp
830	else
831		set -- $(v4_info $_ifs)
832		if [[ -n $2 ]]; then
833			_addr=$2; _mask=$(hextodec $3)
834			ifconfig $_ifs inet $_addr delete
835		fi
836	fi
837
838	if [[ -x /sbin/dhclient ]]; then
839		_prompt="or 'dhcp' "
840		# Don't make 'dhcp' the default if dhcp was already used.
841		ifconfig dhcp >/dev/null 2>&1 || _addr=dhcp
842	fi
843	_prompt="IPv4 address for $_ifs? (${_prompt}or 'none')"
844
845	ask_until "$_prompt" "$_addr"
846	case $resp in
847	none)	;;
848	dhcp)	if [[ ! -x /sbin/dhclient ]]; then
849			echo "DHCP not possible - no /sbin/dhclient."
850		elif dhcp_request $_ifs "$_name" || dhcp_request $_ifs ; then
851			# Add hosts entry. Overwrites previous entry if any.
852			set -- $(v4_info $_ifs)
853			addhostent "$2" "$_name"
854			echo "dhcp" >>$_hn
855			# Create a new bpf in case we start another dhclient
856			makedev bpf$(ls /dev | grep -c "^bpf[0-9]")
857		fi
858		;;
859	*)	_addr=$resp
860		ask_until "Netmask?" "${_mask:=255.255.255.0}"
861		ifconfig $_ifs -group dhcp >/dev/null 2>&1
862		if ifconfig $_ifs inet $_addr netmask $resp up ; then
863			addhostent "$_addr" "$_name"
864			echo "inet $_addr $resp" >>$_hn
865		fi
866		;;
867	esac
868}
869
870v6_config() {
871	local _ifs=$1 _name=$2 _hn=$3 _addr _prefixlen _prompt
872
873	ifconfig lo0 inet6 >/dev/null 2>&1 || return
874
875	set -- $(v6_info $_ifs)
876	[[ -n $2 ]] && { _addr=$2; _prefixlen=$3; }
877
878	[[ -x /sbin/rtsol ]] && _prompt="or 'rtsol' "
879	_prompt="IPv6 address for $_ifs? (${_prompt}or 'none')"
880	ask_until "$_prompt" "${_addr:-none}"
881
882	case $resp in
883	none)	return
884		;;
885	rtsol)	[[ ! -x /sbin/rtsol ]] && { echo "No /sbin/rtsol." ; return ; }
886		ifconfig $_ifs up
887		if rtsol -F $_ifs; then
888			set -- $(v6_info $_ifs)
889			addhostent "$2" "$_name"
890			echo "up\nrtsol" >>$_hn
891		fi
892		return
893		;;
894	esac
895
896	_addr=$resp
897	ask_until "IPv6 prefix length for $_ifs?" "${_prefixlen:=64}"
898	ifconfig $_ifs inet6 $_addr prefixlen $resp up || return
899	echo "inet6 $_addr $resp" >>$_hn
900	addhostent "$_addr" "$_name"
901
902	v6_defroute $_ifs
903	[[ $resp == none ]] && return
904	route -n add -inet6 -host default "$resp" || return
905	echo "$resp" >>/tmp/mygate
906}
907
908v4_defroute() {
909	local _dr _prompt=" or 'none'"
910
911	# Get/Confirm an IPv4 default route if an IPv4 address was configured.
912	[[ -n $(ifconfig | sed -ne '/[ 	]inet .* broadcast /p') ]] || return
913
914	# If only one interface, and it is running dhclient, ask nothing
915	[[ -f /tmp/dhclient.conf && $nifs == 1 ]] && return
916
917	[[ -x /sbin/dhclient ]] && _prompt=", 'dhcp'$_prompt"
918	_prompt="Default IPv4 route? (IPv4 address$_prompt)"
919
920	_dr=$(route -n show -inet | sed -ne '/^default */{s///; s/ .*//; p;}')
921	[[ -f /tmp/dhclient.conf ]] && _dr=dhcp
922
923	while :; do
924		ask_until "$_prompt" "$_dr"
925		[[ $resp == @(none|dhcp) ]] && break
926		route delete -inet default >/dev/null 2>&1
927		route -n add -inet -host default "$resp" && { echo "$resp" >/tmp/mygate ; break ; }
928		# Put the old default route back. The new one did not work.
929		route -n add -inet -host default $_dr >/dev/null 2>&1
930	done
931}
932
933v6_defroute() {
934	local _if=$1 _routers _oifs
935
936	if [[ -z $(route -n show -inet6 | sed -ne '/^default */{s///; s/ .*//; p;}') ]]; then
937		resp=none
938		return
939	fi
940
941	if [[ -x /sbin/ping6 ]]; then
942		_routers=$(ping6 -n -c 2 ff02::2%$_if 2>&1 | sed -n \
943			-e '/bytes from/{s/^.*from //;s/,.*$//;p;}')
944	fi
945
946	_oifs=$IFS
947	IFS=
948	PS3="IPv6 default router? (list #, IPv6 address or 'none'): "
949	select i in $_routers; do
950		case $i in
951		"")	resp=$REPLY
952			[[ -n $resp ]] && break
953			;;
954		*)	resp=$i
955			break
956			;;
957		esac
958	done
959	IFS=$_oifs
960}
961
962# Much of this is gratuitously stolen from /etc/netstart.
963enable_network() {
964	local _f _gw
965
966	# Copy any network configuration files. N.B.: hosts already copied.
967	for _f in dhclient.conf resolv.conf resolv.conf.tail; do
968		if [ -f /mnt/etc/$_f ]; then
969			cp /mnt/etc/$_f /etc/$_f
970		fi
971	done
972
973	# Set the address for the loopback interface. Bringing the
974	# interface up, automatically invokes the IPv6 address ::1.
975	ifconfig lo0 inet 127.0.0.1/8
976
977	# configure all of the non-loopback interfaces which we know about.
978	# refer to hostname.if(5)
979	for hn in /mnt/etc/hostname.*; do
980		(( nifs += 1 ))
981
982		# Strip off /mnt/etc/hostname. prefix
983		if=${hn#/mnt/etc/hostname.}
984
985		# Check for ifconfig'able interface.
986		(ifconfig $if||ifconfig $if create)> /dev/null 2>&1 || continue
987
988		# Now parse the hostname.* file
989		while :; do
990			if [ "$cmd2" ]; then
991				# we are carrying over from the 'read dt dtaddr' last time
992				set -- $cmd2
993				af=$1 name=$2 mask=$3 bcaddr=$4 ext1=$5 cmd2=
994				# make sure and get any remaining args in ext2, like the read below
995				i=1; while [ i -lt 6 -a -n "$1" ]; do shift; let i=i+1; done
996				ext2="$@"
997			else
998				# read the next line or exit the while loop
999				read af name mask bcaddr ext1 ext2 || break
1000			fi
1001			# $af can be "dhcp", "up", "rtsol", an address family, commands, or
1002			# a comment.
1003			case $af in
1004			"#"*|"!"*|"bridge"|"")
1005				# skip comments, user commands, bridges,
1006				# and empty lines
1007				continue
1008				;;
1009			"dhcp")	[ "$name" = "NONE" ] && name=
1010				[ "$mask" = "NONE" ] && mask=
1011				[ "$bcaddr" = "NONE" ] && bcaddr=
1012				dhcpif="$dhcpif $if"
1013				cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 down"
1014				cmd="$cmd; dhclient $if"
1015				# Create a new bpf in case we start another dhclient
1016				makedev bpf$(ls /dev | grep -c "^bpf[0-9]")
1017				;;
1018			"rtsol")
1019				rtsolif="$rtsolif $if"
1020				cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 up"
1021				;;
1022			"up")
1023				# The only one of these guaranteed to be set is $if
1024				# the remaining ones exist so that media controls work
1025				cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 up"
1026				;;
1027			*)	read dt dtaddr
1028				if [ "$name" = "alias" ]; then
1029					# perform a 'shift' of sorts
1030					alias=$name
1031					name=$mask
1032					mask=$bcaddr
1033					bcaddr=$ext1
1034					ext1=$ext2
1035					ext2=
1036				else
1037					alias=
1038				fi
1039				cmd="ifconfig $if $af $alias $name "
1040				case $dt in
1041				dest)	cmd="$cmd $dtaddr"
1042					;;
1043				[a-z!]*)
1044					cmd2="$dt $dtaddr"
1045					;;
1046				esac
1047				if [ -z "$name" ]; then
1048					echo "/mnt/etc/hostname.$if: invalid network configuration file"
1049					return
1050				fi
1051				case $af in
1052				inet)	[ "$mask" ] && cmd="$cmd netmask $mask"
1053					if [ "$bcaddr" -a "$bcaddr" != "NONE" ]; then
1054						cmd="$cmd broadcast $bcaddr"
1055					fi
1056					[ "$alias" ] && rtcmd="; route -qn add -host $name 127.0.0.1"
1057					;;
1058				inet6)
1059					[ "$mask" ] && cmd="$cmd prefixlen $mask"
1060					cmd="$cmd $bcaddr"
1061					;;
1062				*)	cmd="$cmd $mask $bcaddr"
1063					;;
1064				esac
1065				cmd="$cmd $ext1 $ext2$rtcmd" rtcmd=
1066				;;
1067			esac
1068			eval "$cmd"
1069		done </mnt/etc/hostname.$if
1070	done
1071
1072	[[ -n $rtsolif ]] && /mnt/sbin/rtsol -F $rtsolif
1073
1074	# /mnt/etc/mygate, if it exists, contains the address(es) of my
1075	# default gateway(s). Use for ipv4 if no interfaces configured via
1076	# dhcp. Use for ipv6 if no interfaces configured via rtsol.
1077	[[ -z $dhcpif ]] && stripcom /mnt/etc/mygate | while read _gw; do
1078		[[ $_gw == @(*:*) ]] && continue
1079		route -qn delete default >/dev/null 2>&1
1080		route -qn add -host default $_gw && break
1081	done
1082	[[ -z $rtsolif ]] && stripcom /mnt/etc/mygate | while read _gw; do
1083		[[ $_gw == !(*:*) ]] && continue
1084		route -qn delete -inet6 default >/dev/null 2>&1
1085		route -qn add -host -inet6 default $_gw && break
1086	done
1087
1088	route -qn add -net 127 127.0.0.1 -reject >/dev/null
1089}
1090
1091# Install a user-selected subset of the files in $2 from the source
1092# named in $1. Display an error message for failed installs so the
1093# user will know to try again.
1094install_files() {
1095	local _src=$1 _files=$2 _f _sets _get_sets _n _col=$COLUMNS
1096
1097	# Initialize _sets to the list of sets found in _src, and initialize
1098	# _get_sets to the intersection of _sets and DEFAULTSETS.
1099	#
1100	# Sets will be installed in the order given in THESETS to ensure proper
1101	# installation.  So, to minimize user confusion display the sets in the
1102	# order in which they will be installed.
1103	for _f in $THESETS; do
1104		isin $_f $_files || continue;
1105		_sets=$(addel $_f $_sets)
1106		if [[ -z $DISPLAY && ! -d /mnt/etc/X11 ]]; then
1107			# No displays and X isn't installed ==> skip X sets
1108			isin ${_f%${VERSION}.tgz} xbase xetc xshare xfont xserv && continue
1109		fi
1110		isin $_f $DEFAULTSETS "site$VERSION-$(hostname -s).tgz" && \
1111			_get_sets=$(addel $_f $_get_sets)
1112	done
1113
1114	if [[ -z $_sets ]]; then
1115		# Show $_src, but delete any ftp password.
1116		echo -n "Looked at "
1117		echo $_src | sed -e 's/\(^ftp:\/\/[^/]*\)\(:[^/]*\)\(@.*\)/\1\3/'
1118		echo "and found no $OBSD sets.  The set names looked for were:"
1119
1120		let COLUMNS=_col-8
1121		for _n in $THESETS; do echo $_n; done | showcols | sed 's/^/    /'
1122		COLUMNS=$_col
1123
1124		echo
1125		return
1126	fi
1127
1128	resp=y
1129	isin INSTALL.$ARCH $_files ||
1130		ask_yn "INSTALL.$ARCH not found. Use sets found here anyway?"
1131	[[ $resp = n ]] && return
1132
1133	select_sets "$_sets" "$_get_sets"
1134
1135	[[ -n $resp ]] || return
1136	_get_sets=$resp
1137
1138	[[ $resp = n ]] && return
1139
1140	shacmd="cat"
1141	[[ -x /bin/sha256 ]] && shacmd="sha256 /tmp/h"
1142
1143	for _f in $THESETS ; do
1144		isin $_f $_get_sets || continue
1145		echo -n "Getting $_f ..."
1146		rm -f /tmp/h
1147		case $_f in
1148		*.tgz)	ftp $FTPOPTS -o - -m "$_src/$_f" | \
1149				$shacmd | tar zxphf - -C /mnt
1150			;;
1151		*)	ftp $FTPOPTS -o - -m "$_src/$_f" | \
1152				$shacmd > "/mnt/$_f"
1153			;;
1154		esac
1155		if [ $? -ne 0 ]; then
1156			echo "'$_f' did not install correctly."
1157		elif [ -f /tmp/h -a -f "/var/hash/$_f" ]; then
1158			if [ "$(</tmp/h)" != "$(</var/hash/$_f)" ]; then
1159				echo "The SHA256 hash $(cat /tmp/h)"
1160				echo "for $_f did not match what this bsd.rd expected."
1161				# XXX should mark failure somehow
1162			fi
1163			DEFAULTSETS=$(rmel $_f $DEFAULTSETS)
1164			GOTSETS="$GOTSETS $_f"
1165		else
1166			DEFAULTSETS=$(rmel $_f $DEFAULTSETS)
1167			GOTSETS="$GOTSETS $_f"
1168		fi
1169	done
1170}
1171
1172# Encode $1 as specified for usercodes and passwords in RFC 1738
1173# section 3.1 and section 5.
1174#
1175# Escape everything between 0x20 and 0x7e to avoid both illegal url
1176# characters and characters causing problems during script processing.
1177#
1178# *NOTE*
1179#	1) quotes around $1 are required to preserve trailing or
1180#	   embedded blanks in usercodes and passwords.
1181#	2) substitute '%' FIRST so it doesn't eliminate '%' chars we insert.
1182encode_for_url() {
1183	echo "$1" | sed -e "
1184s/%/%25/g
1185s/ /%20/g
1186s/!/%21/g
1187s/\"/%22/g
1188s/#/%23/g
1189s/\\\$/%24/g
1190s/&/%26/g
1191s/'/%27/g
1192s/(/%28/g
1193s/)/%29/g
1194s/\*/%2a/g
1195s/+/%2b/g
1196s/,/%2c/g
1197s/-/%2d/g
1198s/\./%2e/g
1199s/\//%2f/g
1200s/:/%3a/g
1201s/;/%3b/g
1202s/</%3c/g
1203s/=/%3d/g
1204s/>/%3e/g
1205s/?/%3f/g
1206s/@/%40/g
1207s/\[/%5b/g
1208s/\\\\/%5c/g
1209s/]/%5d/g
1210s/\^/%5e/g
1211s/_/%5f/g
1212s/\`/%60/g
1213s/{/%7b/g
1214s/|/%7c/g
1215s/}/%7d/g
1216s/~/%7e/g
1217"
1218}
1219
1220# Check for the presence of an error message in the output of the ftp commands
1221# used to get the list of files in a directory.
1222#
1223# $1 = error message to look for
1224# $2 = ftp command output
1225ftp_error() {
1226	if [[ -n $(echo "$2" | grep "$1") ]]; then
1227		echo $1
1228		return 0
1229	fi
1230	return 1
1231}
1232
1233startftplist() {
1234	# If no networks are configured, we do not need the ftplist file
1235	(( nifs < 1 )) && return
1236
1237	# Make sure the ftp subshell gets its own process group
1238	set -m
1239	(
1240		# ftp.openbsd.org == 129.128.5.191 and will remain at
1241		# that address for the foreseeable future.
1242		ftp $FTPOPTS -a -o - "http://129.128.5.191/cgi-bin/ftplist.cgi?path=$FTPSETDIR" \
1243			2>/tmp/ftplisterr > $SERVERLISTALL
1244
1245		# Remember finish time for adjusting the received timestamp
1246		echo -n $SECONDS >$SERVERLISTSEC
1247	) & ftppid=$!
1248	set +m
1249
1250	# If the ftp process takes more than 12 seconds, kill it
1251	# XXX We are relying on the pid space not randomly biting us --
1252	# XXX ftp could terminate early, and the pid could be reused
1253	(sleep 12; kill -INT -$ftppid >/dev/null 2>&1) &
1254}
1255
1256# Wait for the ftp process to finish, or be killed after the timeout
1257# XXX contains a bit of debug code for now
1258waitftplist() {
1259	local _dot						# XXX
1260
1261	[[ -z $ftppid ]] && return
1262	while [[ -n $(jobs $ftppid 2>/dev/null) ]]; do
1263		echo -n .					# XXX
1264		_dot=.						# XXX
1265		sleep 0.2					# XXX
1266	done
1267	[[ -n $_dot ]] && echo					# XXX
1268}
1269
1270# If possible, print the timestamp received from the ftplist.cgi output,
1271# adjusted with the time elapsed since it was received
1272ftp_time() {
1273	local _ftplist_sec=$(cat $SERVERLISTSEC 2>&-)
1274	local _time=$(sed '/^TIME=\([0-9]*\)$/!d;s//\1/;q' $SERVERLISTALL 2>&-)
1275	[[ -n $_ftplist_sec && -n $_time ]] &&
1276		echo $((_time + SECONDS - _ftplist_sec))
1277}
1278
1279# Get several parameters from the user, and xfer
1280# files from the server.
1281# $1 = url type (ftp or http)
1282# Note:	_ftp_server_ip, _ftp_server_dir, _ftp_server_login,
1283#	and FTPOPTS must be global.
1284install_url() {
1285	local _url_type=$1 _file_list _url_base _oifs _prompt _passwd _mirror
1286	eval local _server_ip=\$_${_url_type}_server_ip \
1287		_server_dir=\$_${_url_type}_server_dir
1288
1289	waitftplist
1290	ask "HTTP/FTP proxy URL? (e.g. 'http://proxy:8080', or 'none')" \
1291		"${ftp_proxy:-none}"
1292	unset ftp_proxy http_proxy
1293	[[ $resp == none ]] || export ftp_proxy=$resp http_proxy=$resp
1294
1295	if [[ -s $SERVERLISTALL ]]; then
1296		_prompt="Server? (hostname, list#, 'done' or '?')"
1297		sed -n "s,^${_url_type}://"'\([[A-Za-z0-9\:_][]A-Za-z0-9:._-]*\),\1,p' \
1298			$SERVERLISTALL > $SERVERLIST
1299		set -- $(sed q $SERVERLIST)
1300		_server_ip=${1%%/*}
1301	else
1302		echo "(Was not able to get ftplist from ftp.openbsd.org, but that is OK)"
1303		_prompt="Server? (hostname or 'done')"
1304	fi
1305
1306	# Get server IP address or hostname
1307	while :; do
1308		ask_until "$_prompt" "$_server_ip"
1309		case $resp in
1310		done)	return ;;
1311		"?")	[[ -s $SERVERLIST ]] || continue
1312			less -XEN < $SERVERLIST
1313			;;
1314		+([0-9]))
1315			# A numeric hostname is ignored. A number is only used
1316			# as a line number in $SERVERLIST.
1317			[[ -s $SERVERLIST ]] || continue
1318			set -- $(sed -n "${resp}p" $SERVERLIST)
1319			[[ $# -lt 1 ]] && { echo "There is no line $resp." ; continue ; }
1320			_server_ip=${1%%/*}
1321			# Repeat loop to get user to confirm server address.
1322			;;
1323		+([A-Za-z0-9\:.\[\]_-]))
1324			_server_ip=$resp
1325			break
1326			;;
1327		*)	echo "'$resp' is not a valid hostname."
1328			;;
1329		esac
1330	done
1331	eval _${_url_type}_server_ip=$_server_ip
1332
1333	# Get directory info from *last* line starting with the server
1334	# name. This means the last install from a mirror will not keep
1335	# the specific directory info. But an install from a local
1336	# server *will* remember the specific directory info.
1337	set -- $(sed "/^$_server_ip/x;\$!d;x" $SERVERLIST 2>&-)
1338	resp=${1#*/}
1339	# If there is no directory specified, don't use the server name!
1340	[[ $resp == "$1" ]] && resp=
1341	if (( $# > 1 )); then
1342		# It's a mirror, since it has location info.
1343		resp=$resp/$FTPSETDIR
1344		_mirror=yes
1345	fi
1346
1347	ask_until "Server directory?" "${resp:-pub/OpenBSD/$FTPSETDIR}"
1348	_server_dir=$resp
1349	eval _${_url_type}_server_dir=$_server_dir
1350
1351	if [[ $_url_type == ftp ]]; then
1352		# Get login name, setting IFS to nothing so trailing or
1353		# embedded blanks are preserved!
1354		_oifs=$IFS
1355		IFS=
1356		ask_until "Login?" "${_ftp_server_login:=anonymous}"
1357		_ftp_server_login=$resp
1358
1359		# Get password unless login in 'anonymous' or 'ftp'
1360		if [[ $_ftp_server_login == @(anonymous|ftp) ]]; then
1361			_passwd=root@`hostname`
1362		else
1363			resp=
1364			while [[ -z $resp ]] ; do
1365				askpass "Password? (will not echo)"
1366			done
1367			_passwd=$resp
1368		fi
1369		IFS=$_oifs
1370	fi
1371
1372	# Build up the base url since it is so nasty...
1373	_url_base=$_url_type://
1374	if [[ $_url_type == ftp && $_ftp_server_login != anonymous ]]; then
1375		_url_base=$_url_base$(encode_for_url "$_ftp_server_login"):$(encode_for_url "$_passwd")@
1376	fi
1377	_url_base=$_url_base$_server_ip/$_server_dir
1378
1379	# XXX Workaround for problems ftp'ing out from a v6 only host.
1380	ifconfig lo0 127.0.0.1
1381
1382	# Get list of files from the server.
1383	if [[ $_url_type == ftp && -z $ftp_proxy ]] ; then
1384		_file_list=$(ftp $FTPOPTS "$_url_base/")
1385		ftp_error "Login failed." "$_file_list" && return
1386		ftp_error "No such file or directory." "$_file_list" && return
1387	else
1388		# Assumes index file is "index.txt" for http (or proxy)
1389		# We can't use index.html since the format is server-dependent
1390		_file_list=$(ftp $FTPOPTS -o - "$_url_base/index.txt" | \
1391			sed -e 's/^.* //' | sed -e 's/
1392//')
1393	fi
1394
1395	install_files "$_url_base" "$_file_list"
1396
1397	# Remember where we installed from
1398	installedfrom=$_url_type://$_server_ip/$_server_dir
1399
1400	# Bake a package path if we installed from a mirror
1401	if [[ -n $_mirror ]]; then
1402		package_path=$(print -r -- "$installedfrom" |
1403			sed -E "/\/(snapshots|[0-9]\.[0-9])\/($ARCH)\/*$/!d
1404				s!!/\1/packages/$(arch -s)/!;q")
1405	else
1406		package_path=
1407	fi
1408}
1409
1410install_mounted_fs() {
1411	local _dir
1412
1413	while :; do
1414		ask_until "Pathname to the sets? (or 'done')" "$SETDIR"
1415		[[ $resp == done ]] && return
1416		# Accept a valid /mnt2 or /mnt relative path.
1417		[[ -d /mnt2/$resp ]] && { _dir=/mnt2/$resp ; break ; }
1418		[[ -d /mnt/$resp ]] && { _dir=/mnt/$resp ; break ; }
1419		# Accept a valid absolute path.
1420		[[ -d /$resp ]] && { _dir=/$resp ; break ; }
1421		echo "The directory '$resp' does not exist."
1422	done
1423
1424	install_files "file://$_dir" "$(ls $_dir/)"
1425}
1426
1427install_cdrom() {
1428	get_drive "CD-ROM" '$(get_cddevs)' || return
1429	mount_mnt2 $resp || return
1430
1431	install_mounted_fs
1432}
1433
1434install_disk() {
1435	ask_yn "Is the disk partition already mounted?"
1436	if [[ $resp == n ]]; then
1437		get_drive "disk" '$(bsort $(get_dkdevs))' \
1438			'$(bsort $(rmel $ROOTDISK $(get_dkdevs)))' || return
1439		mount_mnt2 $resp || return
1440	fi
1441
1442	install_mounted_fs
1443}
1444
1445install_nfs() {
1446	local _tcp
1447
1448	# Get the IP address of the server.
1449	ask_until "Server IP address or hostname?" "$NFS_ADDR"
1450	NFS_ADDR=$resp
1451
1452	# Get the server path to mount.
1453	ask_until "Filesystem on server to mount?" "$NFS_PATH"
1454	NFS_PATH=$resp
1455
1456	# Determine use of TCP
1457	ask_yn "Use TCP transport? (requires TCP-capable NFS server)"
1458	[[ $resp == y ]] && _tcp=-T
1459
1460	# Mount the server
1461	mount_nfs $_tcp -o ro -R 5 $NFS_ADDR:$NFS_PATH /mnt2 || return
1462
1463	install_mounted_fs
1464}
1465
1466install_tape() {
1467	local _z _bs
1468
1469	# Get the name of the tape device.
1470	get_drive "tape drive" '$MTDEVS' || return
1471	export TAPE=/dev/nr$resp
1472	if [[ ! -c $TAPE ]]; then
1473		echo "$TAPE is not a character special file."
1474		return
1475	fi
1476
1477	# Rewind the tape device.
1478	echo -n "Rewinding $TAPE (mt rewind)..."
1479	mt rewind || return
1480	echo "done."
1481
1482	# Extract the desired files.
1483	while :; do
1484		ask_until "Skip how many files? (or 'done')" 0
1485		[[ $resp == done ]] && return
1486		[[ $resp == +([0-9]) ]] || continue
1487		(($resp < 0)) && continue
1488
1489		if (($resp > 0)); then
1490			echo -n "Skipping $resp file(s)..."
1491			mt fsf $resp || return
1492			echo "done."
1493		elif [[ -n $_bs ]]; then
1494			# Dance to start of next file.
1495			mt bsf ; mt fsf
1496		fi
1497
1498		unset _z
1499		ask_yn "Is the file gzipped?" yes
1500		[[ $resp == y ]] && _z=z
1501
1502		# Get the blocksize to use. If the file isn't gzipped then
1503		# default to the 20 x 512 = 10,240 byte tar default.
1504		[[ $_z == z ]] || _bs=10240
1505		ask_until "Blocksize for this file?" "${_bs:-8k}"
1506		[[ $resp == done ]] && return
1507		_bs=$resp
1508
1509		dd if=$TAPE bs=$_bs | tar ${_z}xvphf - -C /mnt || return
1510	done
1511}
1512
1513set_timezone() {
1514	local _zonefile=$1 _zonepath _zsed _tz _zoneroot=/usr/share/zoneinfo
1515
1516	# If the timezone file is not available,
1517	# return immediately.
1518
1519	[[ ! -f $_zonefile ]] && return
1520
1521	# If configured in a previous call, return immediately
1522	[[ -n $TZ ]] && return
1523
1524	if [[ -h /mnt/etc/localtime ]]; then
1525		TZ=$(ls -l /mnt/etc/localtime 2>/dev/null)
1526		TZ=${TZ#*${_zoneroot#/mnt}/}
1527	fi
1528
1529	waitftplist
1530	if [[ -s $SERVERLISTALL ]]; then
1531		_tz=$(sed -ne '/^TZ=/s/TZ=//p' <$SERVERLISTALL)
1532		[[ -n $_tz ]] && isin "$_tz" `cat $_zonefile` && TZ=$_tz
1533	fi
1534
1535	# If neither the base or SERVERLIST gave a hint, and this is the
1536	# early question, give up, and ask after the sets are installed
1537	[[ $_zonefile = /var/tzlist && -z $TZ ]] && return
1538
1539	while :; do
1540		ask "What timezone are you in? ('?' for list)" "$TZ"
1541		_zonepath=${resp%%*(/)}
1542		case $_zonepath in
1543		"")	continue ;;
1544		"?")	grep -v /. $_zonefile | showcols
1545			continue ;;
1546		esac
1547
1548		while isin $_zonepath/ $(cat $_zonefile); do
1549			ask "What sub-timezone of '$_zonepath' are you in? ('?' for list)"
1550			_zsed=$(echo $_zonepath/ | sed 's,/,\\/,g')
1551			resp=${resp%%*(/)}
1552			case $resp in
1553			"")	;;
1554			"?")	sed -n "/^$_zsed/{s/$_zsed//;/\/./!p;}" $_zonefile | showcols ;;
1555			*)	_zonepath=$_zonepath/$resp ;;
1556			esac
1557		done
1558
1559		if isin $_zonepath $(cat $_zonefile); then
1560			TZ=${_zonepath#$_zoneroot}
1561			return
1562		fi
1563
1564		echo -n "'${_zonepath}'"
1565		echo " is not a valid timezone on this system."
1566	done
1567}
1568
1569# Check that missing required sets were deliberately skipped.
1570sane_install() {
1571	local _q=$1 _s _m
1572
1573	for _s in $SANESETS; do
1574		isin $_s $DEFAULTSETS || continue
1575		# If sane_install has no argument, harass the user.
1576		resp=n
1577		[[ -z $_q ]] && ask_yn "Are you *SURE* your $MODE is complete without '$_s'?"
1578		[[ $resp == n ]] && _m="$_m $_s"
1579	done
1580
1581	[[ -n $_m ]] && return 1
1582	return 0
1583}
1584
1585# Ask the user for locations of sets, and then install whatever sets the
1586# user selects from that location. Repeat as many times as the user
1587# needs to get all desired sets.
1588install_sets() {
1589	local _d _locs="disk ftp http"
1590
1591	echo
1592
1593	[[ -s $SERVERLISTALL ]] && \
1594		_d=$(sed -ne '/^method=/s/method=//p' $SERVERLISTALL)
1595
1596	ifconfig netboot >/dev/null 2>&1 && : ${_d:=http}
1597	[[ -n $(get_cddevs) ]] && { _locs="cd $_locs" ; : ${_d:=cd} ; }
1598	[[ -x /sbin/mount_nfs ]] && _locs="$_locs nfs"
1599	[[ -n $MTDEVS && -x /bin/mt ]] && _locs="$_locs tape"
1600	: ${_d:=http}
1601
1602	if ! isin "$_d" $_locs; then
1603		for a in http ftp cd nfs tape disk; do
1604			isin $a $_locs && _d=$a && break
1605		done
1606	fi
1607
1608	echo "Let's $MODE the sets!"
1609	while :; do
1610		umount -f /mnt2 >/dev/null 2>&1
1611		[[ -n $method ]] && _d=$method
1612		sane_install quiet && _d=done
1613
1614		ask "Location of sets? ($_locs or 'done')" "$_d"
1615		case $resp in
1616		done)	sane_install && return ;;
1617		c*|C*)	isin cd $_locs && install_cdrom && method=cd ;;
1618		d*|D*)	install_disk && method=disk ;;
1619		f*|F*)	isin ftp $_locs && install_url ftp && method=ftp ;;
1620		h*|H*)	isin http $_locs && install_url http && method=http ;;
1621		n*|N*)	isin nfs $_locs && install_nfs && method=nfs ;;
1622		t*|T*)	isin tape $_locs && install_tape && method=tape ;;
1623		esac
1624	done
1625}
1626
1627# Create a skeletal but useful /etc/fstab from /tmp/fstab by stripping all
1628# comment lines and dropping all filesystems which
1629#
1630#	1) can't be mounted (no mount_* command is found),
1631#	2) have 'xx' in the option field (usually /altroot),
1632#	3) have 'noauto' in the option field,
1633#	4) are nfs (since name resolution may not be present),
1634#	5) are on a vnd device.
1635#
1636# In addition,
1637#
1638#	1) delete 'softdep' options (no soft updates in ramdisk kernels),
1639#	2) mount non-ffs filesystems read only,
1640#	3) prepend '/mnt' to all mount points,
1641#	4) delete any trailing '/' from the mount point (e.g. root),
1642#
1643# If no /etc/fstab is created, do not proceed with install/upgrade.
1644munge_fstab() {
1645	local _dev _mp _fstype _opt _rest
1646
1647	while read _dev _mp _fstype _opt _rest; do
1648		# Drop irrelevant lines and filesystems.
1649		[[ $_dev == @(/dev/vnd*|\#*) || \
1650			$_fstype == nfs || \
1651			! -f /sbin/mount_$_fstype || \
1652			$_opt == *noauto* || \
1653			$_opt == *xx* ]] && continue
1654
1655		# Remove any softdep options, as soft updates are not
1656		# available in the ramdisk kernels.
1657		_opt=$(echo $_opt | sed -e 's/softdep//')
1658
1659		# Change read-only ffs to read-write since we'll potentially
1660		# write to these filesystems.
1661		[[ $_fstype == ffs ]] && _opt=$(echo $_opt | sed -e 's/ro/rw/')
1662
1663		# Mount non-ffs filesystems read only.
1664		[[ $_fstype == ffs ]] || _opt=$(echo $_opt | sed -e 's/rw/ro/')
1665
1666		# Write fs entry in fstab.
1667		# 1) prepend '/mnt' to the mount point.
1668		# 2) remove a trailing '/' from the mount point (e.g. root).
1669		echo $_dev /mnt${_mp%/} $_fstype $_opt $_rest
1670
1671	done </tmp/fstab >/etc/fstab
1672
1673	# If no /etc/fstab was created, we have nowhere to $MODE to.
1674	if [ ! -s /etc/fstab ]; then
1675		echo "Unable to create valid /etc/fstab."
1676		exit
1677	fi
1678}
1679
1680# Must mount filesystems manually, one at a time, so we can make
1681# sure the mount points exist.
1682mount_fs() {
1683	local _async=$1 _dev _mp _fstype _opt _rest _msg _fail
1684
1685	while read _dev _mp _fstype _opt _rest; do
1686		# If not the root filesystem, make sure the mount
1687		# point is present.
1688		[ "$_mp" = "/mnt" ] || mkdir -p $_mp
1689
1690		# Mount the filesystem. Remember any failure.
1691		_msg=$(mount -v -t $_fstype $_async -o $_opt $_dev $_mp) ||
1692			_fail="$_fail\n$_mp ($_dev)"
1693		echo $_msg | sed -e 's/, ctime=[^,)]*//'
1694	done </etc/fstab
1695
1696	if [[ -n $_fail ]]; then
1697		# One or more mounts failed. Continue or abort?
1698		echo "\nWARNING! The following filesystems were not properly mounted:$_fail"
1699		ask_yn "Continue anyway?" no
1700		if [[ $resp == n ]]; then
1701			exit
1702		fi
1703	fi
1704}
1705
1706# Return the device name for the supplied device, which may be a disklabel UID.
1707getdevname() {
1708	local _dev=$1
1709	if [[ ${#_dev} == 18 && $_dev == +([0-9a-f]).[a-p] ||
1710		${#_dev} == 16 && $_dev == +([0-9a-f]) ]]; then
1711		# Lookup device name matching the disklabel UID
1712		sysctl -n hw.disknames |
1713			sed -nE "s/^(.*,)*(.*):${_dev%.?}.*/\\2/p"
1714	else
1715		_dev=${_dev#/dev/}
1716		print -r -- "${_dev%[a-p]}"
1717	fi
1718}
1719
1720# Preen all filesystems in /etc/fstab that have a /sbin/fsck_XXX and a
1721# fs_passno > 0, showing individual results, but skipping $ROOTDEV. This was
1722# already fsck'ed successfully.
1723#
1724# Exit if any fsck's fail (but do them all before exiting!).
1725check_fs() {
1726	local _dev _mp _fstype _rest _fail _f _passno
1727
1728	ask_yn "Force checking of clean non-root filesystems?"
1729	[[ $resp == y ]] && _f=f
1730
1731	while read _dev _mp _fstype _rest _rest _passno _rest; do
1732		[ "$_dev" != /dev/"$ROOTDEV" ] || continue
1733		[ -f "/sbin/fsck_$_fstype" ] || continue
1734		# Make sure device exists before fsck'ing it.
1735		makedev "$(getdevname "$_dev")" || continue
1736		[[ $_passno > 0 ]] || continue
1737		echo -n "fsck -${_f}p $_dev..."
1738		if ! fsck -${_f}p $_dev >/dev/null 2>&1; then
1739			echo "FAILED. You must fsck $_dev manually."
1740			_fail=y
1741		else
1742			echo "OK."
1743		fi
1744	done </etc/fstab
1745
1746	[ "$_fail" ] && exit
1747}
1748
1749# Extract fully qualified domain name from current hostname. If none is
1750# currently set, use 'my.domain'.
1751get_fqdn() {
1752	local _dn
1753
1754	_dn=$(hostname)
1755	_dn=${_dn#$(hostname -s)}
1756	_dn=${_dn#.}
1757
1758	echo "${_dn:=my.domain}"
1759}
1760
1761donetconfig() {
1762	local _dn _ns _n
1763
1764	configure_ifs
1765	v4_defroute
1766
1767	# As dhclient will populate /etc/resolv.conf, a symbolic link to
1768	# /tmp/resolv.conf.shadow, mv any such file to /tmp/resolv.conf
1769	# so it will eventually be copied to /mnt/etc/resolv.conf and will
1770	# not in the meantime remove the user's ability to choose to use it
1771	# or not, during the rest of the install.
1772	if [ -f /tmp/resolv.conf.shadow ]; then
1773		mv /tmp/resolv.conf.shadow /tmp/resolv.conf
1774		# Get nameserver address(es). Store as a blank separated list.
1775		for _n in $(grep '^nameserver ' /tmp/resolv.conf); do
1776			[[ $_n == nameserver ]] || _ns="$_ns$_n "
1777		done
1778		# Zap trailing space in _ns.
1779		set -- $_ns
1780		_ns=$*
1781		# Get default fully qualified domain name from *first* domain
1782		# given on *last* search or domain statement.
1783		_dn=$(sed -n \
1784			-e '/^domain[[:space:]][[:space:]]*/{s///;s/\([^[:space:]]*\).*$/\1/;h;}' \
1785			-e '/^search[[:space:]][[:space:]]*/{s///;s/\([^[:space:]]*\).*$/\1/;h;}' \
1786			-e '${g;p;}' /tmp/resolv.conf)
1787	fi
1788
1789	# Get & apply fully qualified domain name to hostname.
1790	resp="${_dn:=$(get_fqdn)}"
1791	if [[ ! -f /tmp/dhclient.conf || $nifs != 1 ]]; then
1792		ask "DNS domain name? (e.g. 'bar.com')" "$resp"
1793	else
1794		echo "Using DNS domainname $resp"
1795	fi
1796	hostname "$(hostname -s).$resp"
1797
1798	# If only one interface, and it is running dhclient, ask nothing
1799	# else Get/Confirm nameservers
1800	resp="${_ns:=none}"
1801	if [[ ! -f /tmp/dhclient.conf || $nifs != 1 || $resp == none ]]; then
1802		ask "DNS nameservers? (IP address list or 'none')" "$resp"
1803	else
1804		echo "Using DNS nameservers at $resp"
1805	fi
1806	# Construct appropriate resolv.conf.
1807	if [[ $resp != none ]]; then
1808		echo "lookup file bind" >/tmp/resolv.conf
1809		for _ns in $resp; do
1810			echo "nameserver $_ns" >>/tmp/resolv.conf
1811		done
1812		cp /tmp/resolv.conf /tmp/resolv.conf.shadow
1813	fi
1814
1815	manual_net_cfg
1816}
1817
1818populateusrlocal() {
1819	if [ -f /mnt/etc/mtree/BSD.local.dist ]; then
1820		/mnt/usr/sbin/chroot /mnt /usr/sbin/mtree -Uedqn -p /usr/local -f /etc/mtree/BSD.local.dist >/dev/null
1821	fi
1822}
1823
1824apply()
1825{
1826	if [[ $sshd == n ]]; then
1827		echo "sshd_flags=NO		# disabled during install" \
1828			>>/mnt/etc/rc.conf.local
1829	fi
1830	if [[ $sshd_disableroot == y ]]; then
1831		sed -e "/^#\(PermitRootLogin\) yes/s//\1 no/" \
1832			< /mnt/etc/ssh/sshd_config > /tmp/sshd_config
1833		cp /tmp/sshd_config /mnt/etc/ssh/sshd_config
1834	fi
1835	if [[ $ntpd == y ]]; then
1836		echo "ntpd_flags=		# enabled during install" \
1837			>>/mnt/etc/rc.conf.local
1838		if [[ $ntpd_server != default ]]; then
1839			# Comment out the default 'servers' line, and add a
1840			# 'servers' line with the first token in $resp as the
1841			# server.
1842			set -- $ntpd_server
1843			sed -e "s/^servers /#&/;/#server /a\\
1844servers $1
1845" /mnt/etc/ntpd.conf >/tmp/ntpd.conf
1846			cp /tmp/ntpd.conf /mnt/etc/ntpd.conf
1847		fi
1848	fi
1849
1850	if [[ $x11 == y ]]; then
1851		sed -e "/^#\(machdep\.allowaperture=${MDXAPERTURE}\)/s//\1	/" \
1852			/mnt/etc/sysctl.conf >/tmp/sysctl.conf
1853		cp /tmp/sysctl.conf /mnt/etc/sysctl.conf
1854	fi
1855
1856	if [[ $xdm == y && -x /mnt/usr/X11R6/bin/xdm ]]; then
1857		echo "xdm_flags=		# enabled during install" \
1858			>>/mnt/etc/rc.conf.local
1859	fi
1860
1861	if [[ $defcons == y ]]; then
1862		cp /mnt/etc/ttys /tmp/ttys
1863		sed	-e "/^$CTTY/s/std.9600/std.${CSPEED}/" \
1864			-e "/^$CTTY/s/unknown/vt220	/" \
1865			-e "/$CTTY/s/off.*/on secure/" /tmp/ttys >/mnt/etc/ttys
1866		[[ -n $CPROM ]] && \
1867			echo "stty $CPROM $CSPEED\nset tty $CPROM" >>/mnt/etc/boot.conf
1868	fi
1869
1870	ln -sf /usr/share/zoneinfo/$TZ /mnt/etc/localtime
1871}
1872
1873questions() {
1874	local _d _xdmask=y _def
1875
1876	ask_yn "Start sshd(8) by default?" yes
1877	sshd=$resp
1878
1879	ask_yn "Start ntpd(8) by default?"
1880	ntpd=$resp
1881	if [[ $resp == y ]]; then
1882		ask "NTP server? (hostname or 'default')" default
1883		ntpd_server=$resp
1884	fi
1885
1886	def=no
1887	[[ -n $DISPLAY ]] && def=yes
1888	if [[ -n $MDXAPERTURE ]]; then
1889		ask_yn "Do you expect to run the X Window System?" $def
1890		x11=$resp
1891		_xdmask=$resp	# if aperture was n, do not ask for xdm
1892	fi
1893
1894	if [[ -n $MDXDM && $_xdmask == y ]]; then
1895		ask_yn "Do you want the X Window System to be started by xdm(1)?"
1896		xdm=$resp
1897	fi
1898
1899	if [[ -n $CDEV ]]; then
1900		_d=${CPROM:-$CDEV}
1901		ask_yn "Change the default console to $_d?"
1902		defcons=$resp
1903		if [[ $resp == y ]]; then
1904			ask_which "speed" "should $_d use" \
1905				"9600 19200 38400 57600 115200" $CSPEED
1906			case $resp in
1907			done)	defcons=n ;;
1908			*)	CSPEED=$resp ;;
1909			esac
1910		fi
1911	fi
1912}
1913
1914finish_up() {
1915	local _dev _mp _fstype _rest
1916
1917	# Mount all known swap partitions.  This gives systems with little
1918	# memory a better chance at running 'MAKEDEV all'.
1919	if [[ -x /mnt/sbin/swapctl ]]; then
1920		/mnt/sbin/swapctl -a /dev/$SWAPDEV >/dev/null 2>&1
1921		# Can't do chmod && swapctl -A because devices are not yet
1922		# created on install'ed systems. On upgrade'ed system there
1923		# is a small chance the device does not exist on the ramdisk
1924		# and will thus not get mounted.
1925		while read _dev _mp _fstype _rest; do
1926			[[ $_fstype == swap ]] && \
1927				/mnt/sbin/swapctl -a $_dev >/dev/null 2>&1
1928		done </mnt/etc/fstab
1929	fi
1930
1931	if grep -qs '^rtsol' /mnt/etc/hostname.*; then
1932		sed -e "/^#\(net\.inet6\.ip6\.accept_rtadv\)/s//\1/" \
1933			/mnt/etc/sysctl.conf >/tmp/sysctl.conf
1934		cp /tmp/sysctl.conf /mnt/etc/sysctl.conf
1935	fi
1936
1937	# Create or update pkg.conf with the new package path, if any
1938	if [[ -n $package_path ]]; then
1939		grep -v '^[ 	]*installpath[ 	]*=' /mnt/etc/pkg.conf 2>&- > /tmp/pkgconf
1940		print -r -- "installpath = $package_path" >> /tmp/pkgconf
1941		cp /tmp/pkgconf /mnt/etc/pkg.conf
1942	fi
1943
1944	echo -n "Making all device nodes..."
1945	(cd /mnt/dev; sh MAKEDEV all
1946		# Make sure any devices we found during probe are created in the
1947		# installed system.
1948		for _dev in $(get_dkdevs) $(get_cddevs) $MTDEVS ; do
1949			sh MAKEDEV $_dev
1950		done
1951	)
1952	echo "done."
1953
1954	populateusrlocal
1955
1956	[ -x /mnt/$MODE.site ] && /mnt/usr/sbin/chroot /mnt /$MODE.site
1957
1958	md_installboot $ROOTDISK
1959
1960	# Pat on the back.
1961	cat <<__EOT
1962
1963CONGRATULATIONS! Your OpenBSD $MODE has been successfully completed!
1964To boot the new system, enter 'reboot' at the command prompt.
1965__EOT
1966	[[ $MODE == install ]] && cat <<__EOT
1967When you login to your new system the first time, please read your mail
1968using the 'mail' command.
1969__EOT
1970
1971	md_congrats
1972}
1973
1974# #######################################################################
1975#
1976# Initial actions common to both installs and upgrades.
1977#
1978# Some may require machine dependent routines, which may
1979# call functions defined above, so it's safest to put this
1980# code here rather than at the top of the file.
1981#
1982# #######################################################################
1983
1984ROOTDISK=
1985ROOTDEV=
1986package_path=
1987
1988SETDIR="$VNAME/$ARCH"
1989FTPDIR="pub/OpenBSD/$VNAME"
1990FTPOPTS="-V"
1991SERVERLISTALL=/tmp/serverlistall
1992SERVERLISTSEC=/tmp/serverlistsec
1993SERVERLIST=/tmp/serverlist
1994WLANLIST=/tmp/wlanlist
1995FSTABFLAG=-f
1996
1997# Do not limit ourselves during installs or upgrades.
1998for _opt in d f l m n p s; do
1999	ulimit -$_opt unlimited
2000done
2001
2002# Extract and save one boot's worth of dmesg
2003dmesg | sed -ne '/^OpenBSD /h;/^OpenBSD /!H;${g;p;}' >/var/run/dmesg.boot
2004
2005# Are we in a real release, or a snapshot?  If this is a snapshot
2006# install media, default us to a snapshot directory.
2007FTPSETDIR=$SETDIR
2008set -- $(scan_dmesg "/^OpenBSD $VNAME\([^ ]*\).*$/s//\1/p")
2009[[ $1 == -!(stable) ]] && FTPSETDIR=snapshots/$ARCH
2010
2011# Scan /var/run/dmesg.boot for interesting devices.
2012MTDEVS=$(scan_dmesg "${MDMTDEVS:-/^[cms]t[0-9][0-9]* /s/ .*//p}")
2013nifs=0
2014DISPLAY=$(scan_dmesg '/^wsdisplay[0-9]* /s/ .*//p')
2015
2016CONSOLE=$(scan_dmesg '/^\([^ ]*\).*: console$/s//\1/p')
2017CONSOLE=${CONSOLE% }
2018[[ -n $CONSOLE ]] && CSPEED=$(stty speed)
2019
2020# Look for the serial device matching the console. If we are not installing
2021# from a serial console, just find the first serial device that could be used
2022# as a console. If a suitable device is found, set CDEV, CTTY, CSPEED, CPROM.
2023md_consoleinfo
2024
2025# Selected sets will be installed in the order they are listed in $THESETS.
2026# Ensure that siteXX.tgz is the *last* set listed so its contents overwrite
2027# the contents of the other sets, not the other way around.
2028THESETS="bsd bsd.rd bsd.mp $MDSETS"
2029: ${DEFAULTSETS:="bsd bsd.rd"}
2030for _set in base etc comp man game xbase xetc xshare xfont xserv site ; do
2031	[[ $MODE == upgrade && ( $_set == etc || $_set == xetc ) ]] && continue
2032	THESETS="$THESETS ${_set}${VERSION}.tgz"
2033	isin $_set site && continue
2034	DEFAULTSETS="$DEFAULTSETS ${_set}${VERSION}.tgz"
2035done
2036# Since etc${VERSION}.tgz is not in DEFAULTSETS for upgrades, it can always be
2037# in SANESETS.
2038SANESETS="${SANESETS:-bsd} base${VERSION}.tgz etc${VERSION}.tgz"
2039
2040# prepare COLUMNS sanely
2041COLUMNS=$(stty -a | sed -n '/columns/{s/^.* \([0-9]*\) columns.*$/\1/;p;}')
2042[ COLUMNS -eq 0 ] && COLUMNS=80
2043
2044# decide upon an editor
2045: ${EDITOR:=ed}
2046[[ -x /usr/bin/vi ]] && EDITOR=vi
2047export EDITOR COLUMNS
2048
2049# umount all filesystems, just in case we are re-running install or upgrade.
2050cd /
2051umount -af 1>/dev/null 2>&1
2052
2053# make sure only successful dhcp requests retain their state
2054for _ifs in $(get_ifdevs dhcp); do
2055	set -- $(v4_info $_ifs)
2056	[[ $1 == UP && -n $2 ]] && continue
2057	ifconfig $_ifs delete down -group dhcp 2>&-
2058done
2059
2060cat <<__EOT
2061At any prompt except password prompts you can escape to a shell by
2062typing '!'. Default answers are shown in []'s and are selected by
2063pressing RETURN.  You can exit this program at any time by pressing
2064Control-C, but this can leave your system in an inconsistent state.
2065
2066__EOT
2067
2068# Configure the terminal and keyboard.
2069set_term
2070
2071if [[ $MODE == install ]]; then
2072	ask_until "System hostname? (short form, e.g. 'foo')" "$(hostname -s)"
2073	[[ ${resp%%.*} != $(hostname -s) ]] && hostname $resp
2074	THESETS="$THESETS site$VERSION-$(hostname -s).tgz"
2075
2076	echo
2077	donetconfig
2078
2079	(( nifs != 0 )) && startftplist
2080
2081	echo
2082	while :; do
2083		askpassword root
2084		_rootpass="$_password"
2085		[[ -n "$_password" ]] && break
2086		echo "The root password must be set."
2087	done
2088
2089	questions
2090	user_setup
2091
2092	set_timezone /var/tzlist
2093	echo
2094fi
2095
2096# Get ROOTDISK, ROOTDEV and SWAPDEV.
2097while :; do
2098	ask_which "disk" "is the root disk" '$(get_dkdevs | sed s,^$,none, )'
2099	[[ $resp == done ]] && exit
2100	[[ $resp != none ]] && break
2101done
2102makedev $resp || exit
2103
2104ROOTDISK=$resp
2105ROOTDEV=${ROOTDISK}a
2106SWAPDEV=${ROOTDISK}b
2107