1# $MirOS: src/distrib/common/install.sub,v 1.70 2014/03/30 11:03:32 tg Exp $
2# $OpenBSD: install.sub,v 1.388 2005/07/02 00:55:48 uwe Exp $
3# $NetBSD: install.sub,v 1.5.2.8 1996/09/02 23:25:02 pk Exp $
4#
5# Copyright (c) 2003, 2004, 2005, 2008, 2009, 2010, 2011, 2013, 2014
6#	Thorsten “mirabilos” Glaser <tg@mirbsd.org>
7# Copyright (c) 1997-2005 Todd Miller, Theo de Raadt, Ken Westerback
8# All rights reserved.
9#
10# Redistribution and use in source and binary forms, with or without
11# modification, are permitted provided that the following conditions
12# are met:
13# 1. Redistributions of source code must retain the above copyright
14#    notice, this list of conditions and the following disclaimer.
15# 2. Redistributions in binary form must reproduce the above copyright
16#    notice, this list of conditions and the following disclaimer in the
17#    documentation and/or other materials provided with the distribution.
18#
19# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29#
30# Copyright (c) 1996 The NetBSD Foundation, Inc.
31# All rights reserved.
32#
33# This code is derived from software contributed to The NetBSD Foundation
34# by Jason R. Thorpe.
35#
36# Redistribution and use in source and binary forms, with or without
37# modification, are permitted provided that the following conditions
38# are met:
39# 1. Redistributions of source code must retain the above copyright
40#    notice, this list of conditions and the following disclaimer.
41# 2. Redistributions in binary form must reproduce the above copyright
42#    notice, this list of conditions and the following disclaimer in the
43#    documentation and/or other materials provided with the distribution.
44# 3. All advertising materials mentioning features or use of this software
45#    must display the following acknowledgement:
46#        This product includes software developed by the NetBSD
47#        Foundation, Inc. and its contributors.
48# 4. Neither the name of The NetBSD Foundation nor the names of its
49#    contributors may be used to endorse or promote products derived
50#    from this software without specific prior written permission.
51#
52# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
53# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
54# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
55# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
56# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
59# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
60# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
61# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
62# POSSIBILITY OF SUCH DAMAGE.
63#
64
65# MirBSD install/upgrade script common subroutines and initialization code
66
67# Include machine-dependent functions and definitions.
68#
69# The following functions must be provided:
70#	md_congrats()		  - display friendly message
71#	md_prep_disklabel()	  - put an OpenBSD disklabel on the disk
72#
73# The following variables can be provided if required:
74#	MDSETS	    - list of files to add to THESETS
75#	MDFSTYPE    - nothing assumed if not provided
76#	MDFSOPTS    - nothing assumed if not provided
77#	MDDKDEVS    - '/^r*a*[swi]d[0-9][0-9]* /s/ .*//p' assumed if not provided
78#	MDCDDEVS    - '/^cd[0-9][0-9]* /s/ .*//p'    assumed if not provided
79#	MDMTDEVS    - '/^[cmsw]t[0-9][0-9]* /s/ .*//p'
80#	MDXAPERTURE - set machdep.allowaperture=value in sysctl.conf
81. /etc/functions
82. install.md
83
84if _vbox_check; then
85	echo Sorry, WirrtualBox is not supported.
86	echo To continue on your own risk: ':>/allow-vbox'
87	echo But remember that vbox is buggy and often broken!
88	test -e /allow-vbox || exit 1
89fi
90
91set_term() {
92	typeset TERMS=vt100,vt220,wsvtg,wsvt25,dumb
93	echo "Possible types: $TERMS"
94	ask "Terminal type?" ${TERM:-vt220}
95	TERM=$resp
96	export TERM
97
98	[[ -x /sbin/kbd ]] || return
99	while :; do
100		ask "kbd(8) mapping? ('?' for list)" "none"
101		[[ $resp = none ]] && return
102		[[ $resp = @(\?|+([0-9])) && -z ${layouts[0]} ]] && \
103		    set -A layouts -- $(kbd -lq | \
104		    egrep '^..\.?(nodead|dvorak)?$' | sort)
105		[[ $resp = +([0-9]) ]] && resp=${layouts[$resp]}
106		[[ $resp = [a-z]* ]] && if kbd $resp; then
107			echo $resp >/tmp/kbdtype
108			return
109		fi
110		[[ $resp = \? ]] || continue
111		print Available keyboard mappings:
112		typeset -iR3 i=0
113		while (( i < ${#layouts[*]} )); do
114			print "$i) ${layouts[i]}"
115			let i++
116		done | rs
117	done
118}
119
120welcome() {
121	typeset _q
122
123	cat <<__EOT
124
125Welcome to the ${OBSD} $MODE program (old, rewrite in progress).
126
127This programme will help you $MODE MirOS. At any prompt except password
128prompts you can run a shell command by typing '!foo', or escape to a shell
129by typing '!'. Default answers are shown in []s and are selected by just
130RETURN, but sometimes there is no default. At any time you can exit this
131programme by pressing Control-C and then RETURN, but quitting during an
132$MODE can leave your system in an inconsistent state.
133
134__EOT
135
136	# Configure the terminal and keyboard.
137	set_term
138
139	cat <<__EOT
140
141IS YOUR DATA BACKED UP? As with anything that modifies disk contents, this
142program can cause SIGNIFICANT data loss.
143
144__EOT
145
146	case $MODE in
147	upgrade)
148		cat <<__EOT
149NOTE: before your system has been upgraded, you must manually merge any changes
150to files in the 'etc' and 'xetc' sets into the files already on your system.
151
152__EOT
153		_q="Proceed with upgrade?"
154		;;
155
156	install)
157		cat <<__EOT
158It is often helpful to have the installation notes handy. For complex disk
159configurations, relevant disk hardware manuals and a calculator are useful.
160
161__EOT
162
163		if [ -f /etc/fstab ]; then
164			cat <<__EOT
165You seem to be trying to restart an interrupted installation! You can skip
166the disk preparation steps and continue, or you can reboot and start over.
167
168__EOT
169			_q="Skip disk initialization?"
170		else
171			_q="Proceed with install?"
172		fi
173		;;
174	esac
175
176	ask_yn "$_q"
177	if [[ $resp = n ]]; then
178		cat <<__EOT
179
180Enter 'halt -p' or 'reboot' at the prompt to gracefully exit MirBSD.
181You can then power cycle the machine and boot BSD or your other OSes.
182__EOT
183		exit
184	fi
185
186	echo "Cool! Let's get to it."
187}
188
189scan_dmesg() {
190	bsort $(sed -ne "$1" /var/run/dmesg.boot)
191}
192
193# Get the first (lowest unit #) serial device if any, if MDSERIAL is set.
194# NOTE: Only single digit serial devices (<dev>0 -> <dev>9) are looked for.
195get_serialdev() {
196	typeset _d _bd
197
198	[[ -n $MDSERIAL ]] || exit
199	set -- $MDSERIAL
200	_d=$1
201	_bd=$2
202	set -- $(scan_dmesg "/^${_d}\([0-9]\) .*/s//\1/p")
203	[[ -z $1 ]] || echo "$_bd$1"
204}
205
206get_drive() {
207	ask_which "$1" "contains the $MODE media" "$2"
208	[[ $resp = done ]] && return 1
209	makedev $resp || return 1
210	return 0
211}
212
213get_partition() {
214	typeset _drive=$1 _fstypes=$2 _part _fst
215
216	# Create file /tmp/parts.$_drive where each line is of the
217	# form "<partition letter> <fs type>".
218	disklabel $_drive 2>/dev/null		\
219		| grep '^  [a-p]: '		\
220		| egrep -v "swap|unused"	\
221		| sed -e 's/^  \(.\):  *[^ ]*  *[^ ]*  *\([^ ]*\) .*/\1 \2/' \
222		>/tmp/parts.$_drive
223
224	disklabel $_drive 2>/dev/null | grep '^  .:'
225
226	ask_which "$_drive partition" "has the $MODE sets" \
227		 "$(sed -e 's/^\(.\).*/\1/' /tmp/parts.$_drive)"
228	[[ $resp = done ]] && return 1
229
230	_part=$resp
231	_fst=$(sed -ne "/^$_part /s///p" /tmp/parts.$_drive)
232
233	ask_which "filesystem type" "should be used to mount $_drive$_part" "$_fst $_fstypes ffs"
234	case $resp in
235	done)	return 1 ;;
236	$_fst)	resp="$_part" ;;
237	*)	resp="$_part $resp" ;;
238	esac
239
240	return 0
241}
242
243# Ask for a password, saving the input in $resp.
244#    Display $1 as the prompt.
245#    *Don't* allow the '!' options that ask does.
246#    *Don't* echo input.
247askpass() {
248	set -o noglob
249	stty -echo
250	read resp?"$1 "
251	stty echo
252	set +o noglob
253	echo
254}
255
256# Ask for user input.
257#
258#    $1    = the question to ask the user
259#    $2    = the default answer
260#
261# Save the user input (or the default) in $resp.
262#
263# Allow the user to escape to shells ('!') or execute commands
264# ('!foo') before entering the input.
265ask() {
266	typeset _question=$1 _default=$2
267
268	set -o noglob
269	while :; do
270		echo -n "$_question "
271		[[ -z $_default ]] || echo -n "[$_default] "
272		read resp
273		case $resp in
274		!)	echo "Type 'exit' to return to install."
275			sh
276			;;
277		!*)	eval ${resp#?}
278			;;
279		*)	: ${resp:=$_default}
280			break
281			;;
282		esac
283	done
284	set +o noglob
285}
286
287# Ask for user input until a non-empty reply is entered.
288#
289#    $1    = the question to ask the user
290#    $2    = the default answer
291#
292# Save the user input (or the default) in $resp.
293ask_until() {
294	resp=
295	while [[ -z $resp ]]; do
296		ask "$1" "$2"
297	done
298}
299
300# Ask the user for a y or n, and insist on 'y', 'yes', 'n' or 'no'.
301#
302#    $1    = the question to ask the user
303#    $2    = the default answer (assumed to be 'n' if empty).
304#
305# Return 'y' or 'n' in $resp.
306ask_yn() {
307	typeset _q=$1 _a=${2:-no} _resp
308	typeset -l _resp
309
310	while :; do
311		ask "$_q" "$_a"
312		_resp=$resp
313		case $_resp in
314		y|yes)	resp=y; return ;;
315		n|no)	resp=n; return ;;
316		esac
317	done
318 }
319
320# Ask for the user to select one value from a list, or 'done'.
321#
322# $1 = name of the list items (disk, cd, etc.)
323# $2 = question to ask
324# $3 = list of valid choices
325# $4 = default choice, if it is not specified use the first item in $3
326# $5 = error message if no items in $3, defaults to 'No $1s found.'
327#
328# At exit $resp holds selected item, or 'done'
329ask_which() {
330	typeset _name=$1 _query=$2 _list=$3 _def=$4 _err=$5
331
332	set -- $_list
333	if (( $# < 1 )); then
334		echo "${_err:=No ${_name}s found}."
335		resp=done
336		return
337	fi
338	: ${_def:=$1}
339
340	# Eliminate extraneous (especially trailing) whitespace in _list.
341	_list="$*"
342
343	while :; do
344		# Put both lines in ask prompt, rather than use a
345		# separate 'echo' to ensure the entire question is
346		# re-ask'ed after a '!' or '!foo' shell escape.
347		ask "Available ${_name}s are: $_list.\nWhich one $_query? (or 'done')" "$_def"
348
349		# Quote $resp to prevent user from confusing isin() by
350		# entering something like 'a a'.
351		isin "$resp" $_list done && break
352		echo "'$resp' is not a valid choice."
353	done
354}
355
356# test the first argument against the remaining ones, return success on a match
357isin() {
358	typeset	_a=$1 _b
359
360	shift
361	for _b; do
362		[[ $_a = $_b ]] && return 0
363	done
364	return 1
365}
366
367# add first argument to list formed by the remaining arguments
368# adds to the tail if the element does not already exist
369addel() {
370	typeset	_a=$1
371
372	shift
373
374	echo -n "$*"
375	isin "$_a" $* || echo -n " $_a"
376}
377
378# remove all occurrences of first argument from list formed by
379# the remaining arguments
380rmel() {
381	typeset	_a=$1 _b
382
383	shift
384	for _b; do
385		[[ $_a != $_b ]] && echo -n "$_b "
386	done
387}
388
389bsort() {
390	typeset _l _a=$1 _b
391
392	(( $# > 0 )) || return
393
394	shift
395	for _b; do
396		if [[ $_a != $_b ]]; then
397			if [[ $_a >$_b ]]; then
398				_l="$_a $_l"; _a=$_b
399			else
400				_l="$_b $_l"
401			fi
402		fi
403	done
404
405	# Output the smallest value found.
406	echo -n "$_a "
407
408	# Sort remaining values.
409	bsort $_l
410}
411
412# Add interesting/useful comments from mnt/etc/$1 to /tmp/$1.
413#
414# $1 == file in /tmp and /mnt/etc directories
415save_comments() {
416	typeset _file=$1
417
418	if [[ -f /mnt/etc/$_file ]]; then
419		grep "^#" /mnt/etc/$_file >/tmp/$_file.new
420		[[ -f /tmp/$_file ]] && cat /tmp/$_file >>/tmp/$_file.new
421		mv /tmp/$_file.new /tmp/$_file
422	fi
423}
424
425# Offer to edit a file in /tmp and execute ${EDITOR} to do so if the user
426# accepts the offer.
427#
428# $1 == file in /tmp to edit
429edit_tmp_file() {
430	typeset _file=$1
431
432	ask_yn "Edit $_file with $EDITOR?"
433	[[ $resp = y ]] && $EDITOR /tmp/$_file
434}
435
436# Offer to shell out for manual network configuration, and do so if
437# the user accepts the offer.
438manual_net_cfg() {
439	typeset _x
440
441	ask_yn "Do you want to do any manual network configuration?"
442
443	[[ $resp = y ]] && { echo "Type 'exit' to return to $MODE."; sh; }
444
445	# the network is now up…
446	pf=https
447	ftp -h 2>&1 | fgrep https >/dev/null 2>&1 || pf=http
448	_getrnd net $pf
449	_ntp
450}
451
452# log in via ftp to host $1 as user $2 with password $3
453# and return a list of all files in the directory $4 on stdout
454ftp_list_files() {
455	ftp ${_ftp_active} -V -n "$1" <<__EOT
456user "$2" "$3"
457cd "$4"
458ls
459quit
460__EOT
461}
462
463# Create a device.
464#
465# $1 = name of the device to create.
466makedev() {
467	typeset _dev=$1
468
469	if [[ ! -r /dev/MAKEDEV ]]; then
470		echo "MAKEDEV not found. Can't create device nodes."
471		return 1
472	fi
473
474	cd /dev; mksh MAKEDEV $_dev || return 1 ; cd - >/dev/null
475}
476
477# Create an entry in the hosts file. If an entry with the
478# same symbolic name and address family already exists, delete it.
479# $1 - IP address (v6 if it contains ':', else v4)
480# $2 - symbolic name
481addhostent() {
482	typeset _addr=$1 _name=$2 _leader
483
484	if [[ $_addr = *:* ]]; then
485		_leader='/^[0-9a-fA-F]*:'
486	else
487		_leader='/^[0-9]*\.'
488	fi
489	sed "${_leader}.*[	 ]$_name\$/d" /tmp/hosts >/tmp/hosts.new
490	echo "$_addr $_name" >>/tmp/hosts.new
491	mv /tmp/hosts.new /tmp/hosts
492}
493
494# Show list of available sets and let the user select which sets to install.
495#
496# $1 = available sets
497# $2 = already selected sets
498#
499# Set $resp to list of selected sets.
500select_sets() {
501	typeset _avail=$1 _selected=$2 _next _f _action
502
503	cat <<__EOT
504
505Select sets by entering a set name, a file name pattern or 'all'. De-select
506sets by prepending a '-' to the set name, file name pattern or 'all'. Selected
507sets are labelled '[x]'.
508__EOT
509	while :; do
510		_action=
511		_next=
512		echo
513		for _f in $_avail; do
514			if isin $_f $_selected; then
515				echo "	[X] $_f"
516			else
517				echo "	[ ] $_f"
518				: ${_next:=$_f}
519			fi
520		done
521		: ${_next:=done}
522
523		ask "Set name? (or 'done')" "$_next"
524		case $resp in
525		done)	break ;;
526		-*)	_action=rmel ;;
527		esac
528
529		: ${_action:=addel}
530		resp=${resp#+|-}
531
532		case $resp in
533		"")	continue ;;
534		all)	resp=* ;;
535		esac
536
537		# Use @($resp) rather than just $resp to protect
538		# against silly user input that might cause syntax
539		# errors.
540		for _f in $_avail; do
541			eval "case $_f in
542			@($resp)) _selected=\`$_action $_f \$_selected\` ;;
543			esac"
544		done
545	done
546
547	resp=$_selected
548}
549
550configure_ifs() {
551	typeset _IFDEVS=$(ifconfig -l) _ifs _name _media _hn
552
553	while :; do
554		ask_which "interface" "do you wish to initialise" "$_IFDEVS" \
555			"" "No more interfaces to initialise"
556		[[ $resp = done ]] && break
557
558		_ifs=$resp
559		_hn=/tmp/hostname.$_ifs
560
561		# Get symbolic name - will be used in DHCP requests.
562		ask "Symbolic (host) name for $_ifs?" "$(hostname -s)"
563		_name=$resp
564
565		# Get and apply media options.
566		_media=$(ifconfig -m $_ifs | grep "media ")
567		if [[ -n $_media ]]; then
568			cat <<__EOT
569The media options for $_ifs are currently
570$(ifconfig -m $_ifs | sed -n '/supported/D;/media:/p')
571__EOT
572			ask_yn "Do you want to change the media options?"
573			case $resp in
574			y)	cat <<__EOT
575Supported media options for $_ifs are:
576$_media
577__EOT
578				ask "Media options for $_ifs?"
579				_media=$resp
580				ifconfig $_ifs $_media || return 1
581				;;
582			n)	_media=
583				;;
584			esac
585		fi
586
587		rm -f $_hn
588		v4_config "$_ifs" "$_media" "$_name" "$_hn"
589		v6_config "$_ifs" "$_media" "$_name" "$_hn"
590
591		[[ -f $_hn ]] && _IFDEVS=$(rmel "$_ifs" $_IFDEVS)
592	done
593}
594
595# Output '<UP | DOWN> [<addr> <netmask> <rest of inet line>]'.
596#
597# $1 == interface
598v4_info() {
599	ifconfig $1 inet | sed -n '
600		1s/.*<UP,.*/UP/p
601		1s/.*<.*/DOWN/p
602		/inet/s/netmask//
603		/inet/s///p'
604}
605
606# Obtain and output the inet6 information related to the given
607# interface. Should output '<UP/DOWN> <addr> <prefixlen> <rest of inet line> '.
608#
609# $1 == interface
610v6_info() {
611	ifconfig $1 inet6 | sed -n '
612		1s/.*<UP,.*/UP/p
613		1s/.*<.*/DOWN/p
614		/scopeid/d
615		/inet6/s///p'
616}
617
618# Construct etc/dhclient.conf and issue DHCP request. Return FALSE if
619# no IP address assigned to $1.
620#
621# $1 == interface
622# $2 == hostname (optional).
623dhcp_request() {
624	typeset _ifs=$1 _hn=$2
625
626	echo "lookup file bind" >/etc/resolv.conf.tail
627
628	if [[ -n $_hn ]]; then
629		_hn="send host-name \"$_hn\";"
630		echo "Issuing hostname-associated DHCP request for $_ifs."
631	else
632		echo "Issuing free-roaming DHCP request for $_ifs."
633	fi
634
635	cat >/etc/dhclient.conf <<__EOT
636initial-interval 1;
637$_hn
638request subnet-mask, broadcast-address, routers, domain-name,
639	domain-name-servers, host-name;
640__EOT
641
642	dhclient $_ifs
643
644	set -- $(v4_info $_ifs)
645
646	if [[ $1 = UP && -n $2 ]]; then
647		# Move configuration files to where they will be copied to the
648		# installed system. Overwrites configuration information from
649		# last successful dhcp attempt.
650		mv /etc/dhclient.conf /tmp/dhclient.conf
651		mv /etc/resolv.conf.tail /tmp/resolv.conf.tail
652		return 0
653	fi
654
655	ifconfig $_ifs delete down
656	rm /etc/dhclient.conf /etc/resolv.conf.tail
657	return 1
658}
659
660v4_config() {
661	typeset _ifs=$1 _media=$2 _name=$3 _hn=$4 _prompt
662
663	set -- $(v4_info $_ifs)
664	if [[ -n $2 ]]; then
665		ifconfig $_ifs inet $2 delete
666		[[ $2 != "0.0.0.0" ]] && { _addr=$2; _mask=$3; }
667	fi
668
669	[[ -x /sbin/dhclient ]] && _prompt=" or 'dhcp'"
670	_prompt="IPv4 address for $_ifs? (or 'none'$_prompt)"
671
672	ask_until "$_prompt" "$_addr"
673	case $resp in
674	none)	;;
675	dhcp)	if [[ ! -x /sbin/dhclient ]]; then
676			echo "DHCP not possible - no /sbin/dhclient."
677		elif dhcp_request $_ifs "$_name" || dhcp_request $_ifs ; then
678			addhostent "127.0.0.1" "$_name"
679			echo "dhcp NONE NONE NONE $_media" >>$_hn
680			dhcp_requested=", 'dhcp'"
681		fi
682		;;
683	*)	_addr=$resp
684		ask_until "Netmask?" "${_mask:=255.255.255.0}"
685		if ifconfig $_ifs inet $_addr netmask $resp up ; then
686			addhostent "$_addr" "$_name"
687			echo "inet $_addr $resp NONE $_media" >$_hn
688		fi
689		;;
690	esac
691}
692
693v6_config() {
694	typeset _ifs=$1 _media=$2 _name=$3 _hn=$4 _addr _prefixlen _prompt _eui
695
696	ifconfig lo0 inet6 >/dev/null 2>&1 || return
697
698	set -- $(v6_info $_ifs)
699	[[ -n $2 ]] && { _addr=$2; _prefixlen=$3; }
700
701	[[ -x /sbin/rtsol ]] && _prompt="or 'rtsol' "
702	echo To append EUI64 automatically, let the address end with two colons.
703	_prompt="IPv6 address for $_ifs? (${_prompt}or 'none')"
704	ask_until "$_prompt" "${_addr:-none}"
705
706	case $resp in
707	none)	return
708		;;
709	rtsol)	[[ ! -x /sbin/rtsol ]] && { echo "No /sbin/rtsol." ; return ; }
710		sysctl -w net.inet6.ip6.accept_rtadv=1
711		ifconfig $_ifs up
712		rtsol $_ifs
713		addhostent "::1" "$_name"
714		echo "up\nrtsol $media" >>$_hn
715		return
716		;;
717	*::)	_eui=eui64
718		;;
719	esac
720
721	_addr=$resp
722	ask_until "IPv6 prefix length for $_ifs?" "${_prefixlen:=64}"
723	ifconfig $_ifs inet6 $_addr prefixlen $resp $_eui up || return
724	echo inet6 $_addr $resp $_eui $media >>$_hn
725	if [[ -n $_eui ]]; then
726		set -- $(v6_info $_ifs)
727		[[ -n $2 ]] && _addr=$2
728	fi
729	addhostent "$_addr" "$_name"
730
731	v6_defroute $_ifs
732	[[ $resp = none ]] && return
733	route -n add -inet6 default "$resp" || return
734	echo "route add -inet6 default $resp" >>$_hn
735}
736
737v4_defroute() {
738	typeset _dr _fls _prompt=" or 'none'"
739
740	_prompt="Default IPv4 route? (IPv4 address$dhcp_requested$_prompt)"
741
742	_dr=$(route -n show -inet | sed -ne '/^default */{s///; s/ .*//; p;}')
743	[[ -f /tmp/dhclient.conf ]] && _dr=dhcp
744
745	while :; do
746		ask_until "$_prompt" "$_dr"
747		[[ $resp = @(none|dhcp) ]] && break
748		route -n delete -inet default >/dev/null 2>&1
749		route -n add -inet default "$resp" && {
750			set -A _fls -- /tmp/hostname.*
751			(( ${#_fls[*]} == 1 )) || _fls=/tmp/hostname.local
752			echo "route add -inet default $resp" >>$_fls
753			break
754		}
755		# Put the old default route back. The new one did not work.
756		route -n add -inet default $_dr >/dev/null 2>&1
757	done
758}
759
760v6_defroute() {
761	typeset _if=$1 _routers _oifs
762
763	if [[ -n $(route -n show -inet6 | sed -ne '/^default */{s///; s/ .*//; p;}') ]]; then
764		resp=none
765		return
766	fi
767
768	if [[ -x /sbin/ping6 ]]; then
769		_routers=$(ping6 -n -c 2 ff02::2%$_if 2>&1 | sed -n \
770			-e '/bytes from/{s/^.*from //;s/,.*$//;p;}')
771	fi
772
773	_oifs=$IFS
774	IFS=
775	PS3="IPv6 default router? (list #, IPv6 address or 'none'): "
776	select i in $_routers; do
777		case $i in
778		"")	resp=$REPLY
779			[[ -n $resp ]] && break
780			;;
781		*)	resp=$i
782			break
783			;;
784		esac
785	done
786	IFS=$_oifs
787}
788
789# Much of this is gratuitously stolen from /etc/netstart.
790enable_network() {
791	typeset _netfile
792
793	# Copy any required or optional files found
794	for _netfile in hosts dhclient.conf resolv.conf resolv.conf.tail protocols services; do
795		if [ -f /mnt/etc/${_netfile} ]; then
796			cp /mnt/etc/${_netfile} /etc/${_netfile}
797		fi
798	done
799
800	# Set the address for the loopback interface. Bringing the
801	# interface up, automatically invokes the IPv6 address ::1.
802	ifconfig lo0 inet 127.0.0.1
803
804	# configure all of the non-loopback interfaces which we know about.
805	# refer to hostname.if(5)
806	for hn in /mnt/etc/hostname.*; do
807		# Strip off /mnt/etc/hostname. prefix
808		if=${hn#/mnt/etc/hostname.}
809
810		# Check for ifconfig'able interface.
811		ifconfig $if >/dev/null 2>&1 || continue
812
813		# Now parse the hostname.* file
814		while :; do
815			if [ "$cmd2" ]; then
816				# we are carrying over from the
817				# 'read dt dtaddr' last time
818				set -A i -- $cmd2
819				af=${i[0]}
820				name=${i[1]}
821				mask=${i[2]}
822				bcaddr=${i[3]}
823				ext1=${i[4]}
824				unset i[0] i[1] i[2] i[3] i[4]
825				ext2="${i[*]}"
826				cmd2=
827			else
828				# read the next line or exit the while loop
829				read af name mask bcaddr ext1 ext2 || break
830			fi
831			# $af can be "dhcp", "up", "rtsol", an address family, commands, or
832			# a comment.
833			case $af in
834			"route"|"!route")
835				routep="-n $name"
836				[ x"$name" = x"+n" ] && routep=
837				cmd="/sbin/route ${routep} ${mask} ${bcaddr} ${ext1} ${ext2}"
838				;;
839			"#"*|"!"*|"bridge"|"")
840				# skip comments, user commands, bridges,
841				# and empty lines
842				continue
843				;;
844			"dhcp")	[ "$name" = "NONE" ] && name=
845				[ "$mask" = "NONE" ] && mask=
846				[ "$bcaddr" = "NONE" ] && bcaddr=
847				ifconfig $if $name $mask $bcaddr $ext1 $ext2 down
848				cmd="dhclient $if"
849				;;
850			"rtsol")
851				ifconfig $if $name $mask $bcaddr $ext1 $ext2 up
852				rtsif="$rtsif $if"
853				cmd=
854				;;
855			"up")
856				# The only one of these guaranteed to be set is $if
857				# the remaining ones exist so that media controls work
858				cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 up"
859				;;
860			*)	read dt dtaddr
861				if [ "$name" = "alias" ]; then
862					# perform a 'shift' of sorts
863					alias=$name
864					name=$mask
865					mask=$bcaddr
866					bcaddr=$ext1
867					ext1=$ext2
868					ext2=
869				else
870					alias=
871				fi
872				cmd="ifconfig $if $af $alias $name "
873				case $dt in
874				dest)	cmd="$cmd $dtaddr"
875					;;
876				[a-z!]*)
877					cmd2="$dt $dtaddr"
878					;;
879				esac
880				if [ ! -n "$name" ]; then
881					echo "/mnt/etc/hostname.$if: invalid network configuration file"
882					return
883				fi
884				case $af in
885				inet)	[ "$mask" ] && cmd="$cmd netmask $mask"
886					if [ "$bcaddr" -a "$bcaddr" != "NONE" ]; then
887						cmd="$cmd broadcast $bcaddr"
888					fi
889					[ "$alias" ] && rtcmd="; route -qn add -host $name 127.0.0.1"
890					;;
891				inet6)
892					[ "$mask" ] && cmd="$cmd prefixlen $mask"
893					cmd="$cmd $bcaddr"
894					;;
895				*)	cmd="$cmd $mask $bcaddr"
896				esac
897				cmd="$cmd $ext1 $ext2$rtcmd" rtcmd=
898				;;
899			esac
900			eval "$cmd"
901		done </mnt/etc/hostname.$if
902	done
903
904	# Use loopback, not the wire.
905	route -qn add -host $(hostname) 127.0.0.1 >/dev/null
906	route -qn add -net 127 127.0.0.1 -reject >/dev/null
907
908	# Grab default route, if existent
909	if [ -s /mnt/etc/hostname.local ]; then
910		cmd="$(grep ^route /mnt/etc/hostname.local | grep default)"
911		[[ -n $cmd ]] && eval "$cmd"
912	fi
913
914	# Display results...
915	echo "Network interface configuration:"
916	ifconfig -am
917
918	# enable the resolver if resolv.conf is available
919	route -n show
920	if [ -f /etc/resolv.conf ]; then
921		echo "\nResolver enabled."
922	else
923		echo "\nResolver not enabled."
924	fi
925}
926
927# Install a user-selected subset of the files in $2 from the source
928# named in $1. Display an error message for failed installs so the
929# user will know to try again.
930install_files() {
931	typeset _src=$1 _files=$2 _f _sets _get_sets
932
933	# Initialize _sets to the list of sets found in _src, and initialize
934	# _get_sets to the intersection of _sets and DEFAULTSETS.
935	#
936	# Sets will be installed in the order given in THESETS to ensure proper
937	# installation. So, to minimize user confusion display the sets in the
938	# order in which they will be installed.
939	for _f in $THESETS; do
940		isin $_f $_files || continue;
941		_sets=$(addel $_f $_sets)
942		isin $_f $DEFAULTSETS && _get_sets=$(addel $_f $_get_sets)
943	done
944
945	if [[ -z $_sets ]]; then
946		# Show $_src, but delete any ftp password.
947		cat <<__EOT
948No $OBSD sets were found at
949
950	$(echo $_src | sed -e 's/\(^ftp:\/\/[^/]*\)\(:[^/]*\)\(@.*\)/\1\3/')
951
952Set names are: $THESETS
953__EOT
954		return
955	fi
956
957	select_sets "$_sets" "$_get_sets"
958
959	[[ -n $resp ]] || return
960	_get_sets=$resp
961
962	ask_yn "Ready to $MODE sets?" yes
963	[[ $resp = n ]] && return
964
965	for _f in $THESETS; do
966		isin $_f $_get_sets || continue
967		echo "Getting $_f ..."
968		case $_f in
969		*.ngz)	ftp $_ftp_active -o - -V -m "$_src/$_f" | \
970			    gzip -dcf | tar -M lncp -xphf - -C /mnt
971			;;
972		*)	ftp $_ftp_active -o "/mnt/$_f" -V -m "$_src/$_f"
973			;;
974		esac
975		if [ $? -ne 0 ]; then
976			echo "'$_f' did not install correctly."
977		else
978			DEFAULTSETS=$(rmel $_f $DEFAULTSETS)
979		fi
980	done
981}
982
983# Encode $1 as specified for usercodes and passwords in RFC 1738
984# section 3.1 and section 5.
985#
986# Escape everything between 0x20 and 0x7e to avoid both illegal url
987# characters and characters causing problems during script processing.
988#
989# *NOTE*
990#	1) quotes around $1 are required to preserve trailing or
991#	   embeddded blanks in usercodes and passwords.
992#	2) substitute '%' FIRST so it doesn't eliminate '%' chars we insert.
993encode_for_url() {
994	print -nr -- "$1" | sed -e '
995s.%.%25.g
996s.;.%3B.g
997s./.%2F.g
998s.?.%3F.g
999s.:.%3A.g
1000s.@.%40.g
1001s.&.%26.g
1002s.=.%3D.g
1003s.+.%2B.g
1004s.\$.%24.g
1005s.,.%2C.g
1006s.	.%09.g
1007s. .%20.g
1008s.<.%3C.g
1009s.>.%3E.g
1010s.#.%23.g
1011s.".%22.g
1012s.{.%7B.g
1013s.}.%7D.g
1014s.|.%7C.g
1015s.\\.%5C.g
1016s.\^.%5E.g
1017s.\[.%5B.g
1018s.].%5D.g
1019s.`.%60.g
1020s.'\''.%27.g
1021s/!/%21/g
1022s/(/%28/g
1023s/)/%29/g
1024s/\*/%2a/g
1025s/-/%2d/g
1026s/\./%2e/g
1027s/_/%5f/g
1028s/~/%7e/g
1029'
1030}
1031
1032# Check for the presence of an error message in the output of the ftp commands
1033# used to get the list of files in a directory.
1034#
1035# $1 = error message to look for
1036# $2 = ftp command output
1037ftp_error() {
1038	if [[ -n $(echo "$2" | grep "$1") ]]; then
1039		echo $1
1040		return 0
1041	fi
1042	return 1
1043}
1044
1045# Get several parameters from the user, and xfer
1046# files from the server.
1047# $1 = url type (ftp, http or https)
1048# Note:	_ftp_server_ip, _ftp_server_dir, _ftp_server_login,
1049#	and _ftp_active must be global.
1050install_url() {
1051	typeset _url_type=$1 _file_list _url_base _oifs _prompt _passwd
1052
1053	ask "HTTP/FTP proxy URL? (e.g. 'http://proxy:8080', or 'none')" \
1054	    "${ftp_proxy:-none}"
1055	unset ftp_proxy http_proxy
1056	[[ $resp = none ]] || export ftp_proxy=$resp http_proxy=$resp
1057
1058	rm -f $SERVERLIST
1059#	ask_yn "Display the list of known $_url_type servers?" "${_get_server_list:-yes}"
1060#	_get_server_list=$resp
1061	_get_server_list=n
1062#	if [[ $_get_server_list = y ]]; then
1063#		# ftp.openbsd.org == 129.128.5.191 and will remain at
1064#		# that address for the forseeable future.
1065#		echo -n "Getting the list from 129.128.5.191 (ftp.openbsd.org)..."
1066#		ftp $_ftp_active -V -a -o - \
1067#			ftp://129.128.5.191/$FTPDIR/ftplist 2>/tmp/ftplisterr \
1068#			| sed -ne "/^${_url_type}:\/\//s///p" >$SERVERLIST
1069#		if [[ -s $SERVERLIST ]]; then
1070#			echo "done."
1071#			_prompt="Server? (IP address, hostname, list#, 'done' or '?')"
1072#			sed = $SERVERLIST | sed 'N;s/\n/	/' | less -XE
1073#		else
1074#			echo "FAILED."
1075#			cat /tmp/ftplisterr
1076#		fi
1077#	fi
1078
1079	# Get server IP address or hostname
1080	: ${_prompt:="Server? (IP address, hostname or 'done')"}
1081	eval ': ${_'${_url_type}'_server_ip:=www.mirbsd.org}'
1082	while :; do
1083		eval resp=\$_${_url_type}_server_ip
1084		ask_until "$_prompt" "$resp"
1085		case $resp in
1086		done)	return ;;
1087		"?")	[[ -s $SERVERLIST ]] || continue
1088			sed = $SERVERLIST | sed 'N;s/\n/	/' | less -XE
1089			;;
1090		+([0-9]))
1091			# A numeric hostname is ignored. A number is only used
1092			# as a line number in $SERVERLIST.
1093			[[ -s $SERVERLIST ]] || continue
1094			set -- $(sed -ne "${resp}p" $SERVERLIST)
1095			if (( $# < 1 )); then
1096				echo "There is no line $resp."
1097				continue
1098			fi
1099			echo "Using	$*"
1100			eval _${_url_type}_server_ip=${1%%/*}
1101			eval _${_url_type}_server_dir=${1#*/}/$SETDIR
1102			# Repeat loop to get user to confirm server address.
1103			;;
1104		*)	eval _${_url_type}_server_ip=$resp
1105			break
1106			;;
1107		esac
1108	done
1109
1110	# Some older servers lie about their support for passive mode ftp, so
1111	# ask the user if it worth trying passive mode to the chosen server.
1112	# Irrelevant if using a proxy.
1113	if [[ $_url_type = ftp && -z $ftp_proxy ]]; then
1114		case $_ftp_active in
1115		-A)	resp=no ;;
1116		*)	resp=yes ;;
1117		esac
1118
1119		unset _ftp_active
1120		ask_yn "Does the server support passive mode ftp?" $resp
1121		[[ $resp = n ]] && _ftp_active=-A
1122	fi
1123
1124	# Get server directory
1125	eval resp=\$_${_url_type}_server_dir
1126	ask_until "Server directory?" "${resp:-$FTPDIR$SETDIR}"
1127	eval _${_url_type}_server_dir=$resp
1128
1129	if [[ $_url_type = ftp ]]; then
1130		# Get login name, setting IFS to nothing so trailing or
1131		# embedded blanks are preserved!
1132		_oifs=$IFS
1133		IFS=
1134		ask_until "Login?" "${_ftp_server_login:=anonymous}"
1135		_ftp_server_login=$resp
1136
1137		# Get password unless anonymous
1138		_passwd=root@$(hostname)
1139		if [[ $_ftp_server_login != anonymous ]]; then
1140			resp=
1141			while [[ -z $resp ]]; do
1142				askpass "Password? (will not echo)"
1143			done
1144			_passwd=$resp
1145		fi
1146		IFS=$_oifs
1147	fi
1148
1149	# Build up the base url since it is so nasty...
1150	_url_base=$_url_type://
1151	if [[ $_url_type = ftp && $_ftp_server_login != anonymous ]]; then
1152		_url_base=$_url_base$(encode_for_url "$_ftp_server_login"):$(encode_for_url "$_passwd")@
1153	fi
1154	eval _url_base=$_url_base\$_${_url_type}_server_ip/\$_${_url_type}_server_dir
1155
1156	# XXX Workaround for problems ftp'ing out from a v6 only host.
1157	ifconfig lo0 127.0.0.1
1158
1159	# Get list of files from the server.
1160	if [[ $_url_type = ftp && -z $ftp_proxy ]]; then
1161		_file_list=$(ftp_list_files "$_ftp_server_ip" "$_ftp_server_login" "$_passwd" "$_ftp_server_dir")
1162		ftp_error "Login failed." "$_file_list" && return
1163		ftp_error "No such file or directory." "$_file_list" && return
1164	else
1165		# Assumes index file is "index.txt" for http (or proxy)
1166		# We can't use index.html since the format is server-dependent
1167		_file_list=$(ftp -o - -V "$_url_base/index.txt" | sed 's/
1168//')
1169	fi
1170
1171	install_files "$_url_base" "$_file_list"
1172}
1173
1174install_mounted_fs() {
1175	typeset _dir
1176
1177	while :; do
1178		ask_until "Pathname to the sets? (or 'done')" "$SETDIR"
1179		[[ $resp = done ]] && return
1180		# Accept a valid /mnt2 or /mnt relative path.
1181		[[ -d /mnt2/$resp ]] && { _dir=/mnt2/$resp ; break ; }
1182		[[ -d /mnt/$resp ]] && { _dir=/mnt/$resp ; break ; }
1183		# Accept a valid absolute path.
1184		[[ -d /$resp ]] && { _dir=/$resp ; break ; }
1185		echo "The directory '$resp' does not exist."
1186	done
1187
1188	install_files "file://$_dir" "$(ls -l $_dir)"
1189}
1190
1191install_cdrom() {
1192	typeset _drive _part=c _fstype _err=0
1193
1194	get_drive "CD-ROM" "$CDDEVS" || return
1195	_drive=$resp
1196
1197	set -- $(disklabel $_drive 2>&1 | grep '^  c: ') || _err=1
1198	case $_err:$4 in
1199	1*|0:ISO9660)
1200		_fstype=cd9660 ;;
1201	0:UDF)	_fstype=udf ;;
1202	*)	get_partition $_drive "cd9660" || return
1203		set -- $resp
1204		_part=$1
1205		[[ -n $2 ]] && _fstype=$2
1206		;;
1207	esac
1208
1209	mount -t $_fstype -o ro /dev/$_drive$_part /mnt2 || return
1210	install_mounted_fs
1211}
1212
1213install_disk() {
1214	typeset _drive _dev _fstype _fsopts
1215
1216	ask_yn "Is the disk partition already mounted?"
1217	if [[ $resp = n ]]; then
1218		get_drive "disk" "$DKDEVS" || return
1219		_drive=$resp
1220
1221		get_partition $_drive "$MDFSTYPE" || return
1222		set -- $resp
1223		_dev=/dev/$_drive$1
1224		[[ -n $2 ]] && _fstype="-t $2"
1225		[[ $_fstype = $MDFSTYPE ]] && _fsopts=$MDFSOPTS
1226
1227		if [[ -z $(mount | grep "^$_dev") ]]; then
1228			mount $_fstype -o ro,$_fsopts $_dev /mnt2 || return
1229		fi
1230	fi
1231	install_mounted_fs
1232}
1233
1234install_nfs() {
1235	typeset _tcp
1236
1237	# Get the IP address of the server.
1238	ask_until "Server IP address or hostname?" "$NFS_ADDR"
1239	NFS_ADDR=$resp
1240
1241	# Get the server path to mount.
1242	ask_until "Filesystem on server to mount?" "$NFS_PATH"
1243	NFS_PATH=$resp
1244
1245	# Determine use of TCP
1246	ask_yn "Use TCP transport? (requires TCP-capable NFS server)" yes
1247	[[ $resp = y ]] && _tcp=-T
1248
1249	# Mount the server
1250	mount_nfs $_tcp -o ro $NFS_ADDR:$NFS_PATH /mnt2 || return
1251
1252	install_mounted_fs
1253}
1254
1255install_tape() {
1256	typeset _z _bs
1257
1258	# Get the name of the tape device.
1259	get_drive "tape drive" "$MTDEVS" || return
1260	export TAPE=/dev/nr$resp
1261	if [[ ! -c $TAPE ]]; then
1262		echo "$TAPE is not a character special file."
1263		return
1264	fi
1265
1266	# Rewind the tape device.
1267	echo -n "Rewinding $TAPE (mt rewind)..."
1268	mt rewind || return
1269	echo "done."
1270
1271	# Extract the desired files.
1272	while :; do
1273		ask_until "Skip how many files? (or 'done')" 0
1274		[[ $resp = done ]] && return
1275		[[ $resp = +([0-9]) ]] || continue
1276		(( resp < 0 )) && continue
1277
1278		if (( resp > 0 )); then
1279			echo -n "Skipping $resp file(s)..."
1280			mt fsf $resp || return
1281			echo "done."
1282		elif [[ -n $_bs ]]; then
1283			# Dance to start of next file.
1284			mt bsf ; mt fsf
1285		fi
1286
1287		unset _z
1288		ask_yn "Is the file gzipped?" yes
1289		[[ $resp = y ]] && _z=z
1290
1291		# Get the blocksize to use. If the file isn't gzipped then
1292		# default to the 20 x 512 = 10,240 byte tar default.
1293		[[ $_z = z ]] || _bs=10240
1294		ask_until "Blocksize for this file?" "${_bs:-8k}"
1295		[[ $resp = done ]] && return
1296		_bs=$resp
1297
1298		dd if=$TAPE bs=$_bs | tar -M lncp -${_z}xvphf - -C /mnt || \
1299		    return
1300	done
1301}
1302
1303set_timezone() {
1304	typeset _zoneroot=/mnt/usr/share/zoneinfo/ _zonepath
1305
1306	# If the timezone directory structure is not
1307	# available, return immediately.
1308
1309	[[ ! -d $_zoneroot ]] && return
1310
1311	if [[ -L /mnt/etc/localtime ]]; then
1312		TZ=$(ls -l /mnt/etc/localtime 2>/dev/null)
1313		TZ=${TZ#*${_zoneroot#/mnt}}
1314	fi
1315
1316	: ${TZ:=GMT}
1317
1318	while :; do
1319		_zonepath=$_zoneroot
1320
1321		ask "What timezone are you in? ('?' for list)" "$TZ"
1322
1323		if [[ $resp = ? ]]; then
1324			ls -F ${_zonepath}
1325			continue;
1326		fi
1327
1328		_zonepath=${_zonepath}${resp}
1329
1330		while [[ -d $_zonepath ]]; do
1331			ask "What sub-timezone of '${_zonepath#$_zoneroot}' are you in? ('?' for list)"
1332			case $resp in
1333			"")	;;
1334			?)	ls -F $_zonepath ;;
1335			*)	_zonepath=$_zonepath/$resp ;;
1336			esac
1337		done
1338
1339		if [[ -f $_zonepath ]]; then
1340			TZ=${_zonepath#$_zoneroot}
1341			echo -n "Setting local timezone to '$TZ'..."
1342			ln -sf /usr/share/zoneinfo/$TZ /mnt/etc/localtime
1343			echo "done."
1344			return
1345		fi
1346
1347		echo -n "'${_zonepath#$_zoneroot}'"
1348		echo " is not a valid timezone on this system."
1349	done
1350}
1351
1352# Check with the user that missing required sets were deliberately skipped.
1353sane_install() {
1354	typeset _s _m
1355
1356	for _s in $SANESETS; do
1357		isin $_s $DEFAULTSETS || continue
1358		ask_yn "'$_s' was not installed.\nAre you *SURE* your $MODE is complete without '$_s'?"
1359		[[ $resp = n ]] && _m="$_m $_s"
1360	done
1361
1362	[[ -n $_m ]] && return 1
1363	return 0
1364}
1365
1366# Ask the user for locations of sets, and then install whatever sets the
1367# user selects from that location. Repeat as many times as the user
1368# needs to get all desired sets.
1369install_sets() {
1370	typeset _d=disk _locs="disk ftp http shttp"
1371
1372	[[ -n $CDDEVS ]] && { _locs="cd $_locs" ; _d=cd ; }
1373	[[ -x /sbin/mount_nfs ]] && _locs="$_locs nfs"
1374	[[ -n $MTDEVS && -x /bin/mt ]] && _locs="$_locs tape"
1375
1376	echo "\nLet's $MODE the sets!"
1377	while :; do
1378		umount -f /mnt2 >/dev/null 2>&1
1379		[[ -z $DEFAULTSETS ]] && _d=done
1380
1381		ask "Location of sets? ($_locs or 'done')" "$_d"
1382		case $resp in
1383		done)	sane_install && return ;;
1384		c*|C*)	isin "cd" $_locs && install_cdrom ;;
1385		d*|D*)	install_disk ;;
1386		f*|F*)	isin "ftp" $_locs && install_url ftp ;;
1387		h*|H*)	isin "http" $_locs && install_url http ;;
1388		n*|N*)	isin "nfs" $_locs && install_nfs ;;
1389		s*|S*)	isin "http" $_locs && install_url https ;;
1390		t*|T*)	isin "tape" $_locs && install_tape ;;
1391		esac
1392	done
1393}
1394
1395# Create a skeletal but useful /etc/fstab from /tmp/fstab by stripping all
1396# comment lines and dropping all filesystems which
1397#
1398#       1) can't be mounted (no mount_* command is found),
1399#	2) have 'xx' in the option field (usually /altroot),
1400#	3) have 'noauto' in the option field,
1401#	4) are nfs (since name resolution may not be present),
1402#	5) are mfs (breaks install usually).
1403#
1404# In addition,
1405#
1406#	2) mount non-ffs filesystems read only,
1407#	3) prepend '/mnt' to all mount points,
1408#	4) delete any trailing '/' from the mount point (e.g. root),
1409#	5) leave out fs_freq and fs_passno fields.
1410#
1411# If no /etc/fstab is created, do not proceed with install/upgrade.
1412munge_fstab() {
1413	typeset _dev _mp _fstype _opt _rest
1414
1415	while read _dev _mp _fstype _opt _rest; do
1416		# Drop irrelevant lines and filesystems.
1417		[[ $_dev = \#* || \
1418		    $_fstype = nfs || \
1419		    $_fstype = mfs || \
1420		    ! -f /sbin/mount_$_fstype || \
1421		    $_opt = *noauto* || \
1422		    $_opt = *xx* ]] && continue
1423
1424		# Mount non-ffs filesystems read only.
1425		[[ $_fstype = ffs ]] || _opt=$(echo $_opt | sed -e 's/rw/ro/')
1426
1427		# Write fs entry in fstab.
1428		# 1) prepend '/mnt' to the mount point.
1429		# 2) remove a trailing '/' from the mount point (e.g. root).
1430		# 3) leave out fs_freq and fs_passno fields (i.e. $_rest).
1431		echo $_dev /mnt${_mp%/} $_fstype $_opt
1432
1433	done </tmp/fstab >/etc/fstab
1434
1435	# If no /etc/fstab was created, we have nowhere to $MODE to.
1436	if [ ! -s /etc/fstab ]; then
1437		echo "Unable to create valid /etc/fstab."
1438		exit
1439	fi
1440}
1441
1442# Must mount filesystems manually, one at a time, so we can make
1443# sure the mount points exist.
1444mount_fs() {
1445	typeset _async=$1 _dev _mp _fstype _opt _rest
1446
1447	while read _dev _mp _fstype _opt _rest; do
1448		# If not the root filesystem, make sure the mount
1449		# point is present.
1450		[ "$_mp" = "/mnt" ] || mkdir -p $_mp
1451
1452		# Mount the filesystem. If the mount fails, exit.
1453		mount -v -t $_fstype $_async -o $_opt $_dev $_mp && continue
1454		# If it failed, try without async (important for raid) first
1455		mount -v -t $_fstype         -o $_opt $_dev $_mp && continue
1456		# In addition to the error message displayed by mount ...
1457		cat <<__EOT
1458
1459FATAL ERROR:	Cannot mount filesystems. Double-check your configuration
1460		and restart the $MODE.
1461
1462__EOT
1463		exit
1464	done </etc/fstab
1465	[[ -s /mnt/var/db/host.random ]] && \
1466	    cat /mnt/var/db/host.random >/dev/arandom
1467}
1468
1469# Preen all filesystems in /etc/fstab that have a /sbin/fsck_XXX,
1470# showing individual results, but skipping $ROOTDEV. This was already
1471# fsck'ed successfully.
1472#
1473# Exit if any fsck's fail (but do them all before exiting!).
1474check_fs() {
1475	typeset _dev _mp _fstype _rest _fail
1476
1477	echo "Checking non-root filesystems..."
1478
1479	while read _dev _mp _fstype _rest; do
1480		[ "$_dev" != /dev/"$ROOTDEV" ] || continue
1481		[ -f "/sbin/fsck_$_fstype" ] || continue
1482		# Make sure device exists before fsck'ing it.
1483		_rest=${_dev#/dev/}
1484		makedev ${_rest%[a-p]} || continue
1485		echo -n "fsck -p ${_dev}..."
1486		if ! fsck -fp ${_dev} >/dev/null 2>&1; then
1487			echo "FAILED. You must fsck $_dev manually."
1488			_fail=y
1489		else
1490			echo "OK."
1491		fi
1492	done </etc/fstab
1493
1494	echo "...done."
1495
1496	[ "$_fail" ] && exit
1497}
1498
1499# Extract fully qualified domain name from current hostname. If none is
1500# currently set, use 'my.domain'.
1501get_fqdn() {
1502	typeset _dn
1503
1504	_dn=$(hostname)
1505	_dn=${_dn#$(hostname -s)}
1506	_dn=${_dn#.}
1507
1508	echo "${_dn:=my.domain}"
1509}
1510
1511donetconfig() {
1512	typeset _dn _ns _n
1513
1514	configure_ifs
1515
1516	# As dhclient will populate /etc/resolv.conf, a symbolic link to
1517	# /tmp/resolv.conf.shadow, mv any such file to /tmp/resolv.conf
1518	# so it will eventually be copied to /mnt/etc/resolv.conf and will
1519	# not in the meantime remove the user's ability to choose to use it
1520	# or not, during the rest of the install.
1521	if [ -f /tmp/resolv.conf.shadow ]; then
1522		mv /tmp/resolv.conf.shadow /tmp/resolv.conf
1523		# Get nameserver address(es). Store as a blank separated list.
1524		for _n in $(grep '^nameserver ' /tmp/resolv.conf); do
1525			[[ $_n = nameserver ]] || _ns="$_ns$_n "
1526		done
1527		# Zap trailing space in _ns.
1528		set -- $_ns
1529		_ns=$*
1530		# Get default fully qualified domain name from *first* domain
1531		# given on *last* search or domain statement.
1532		_dn=$(sed -n \
1533			-e '/^domain[[:space:]][[:space:]]*/{s///;s/\([^[:space:]]*\).*$/\1/;h;}' \
1534			-e '/^search[[:space:]][[:space:]]*/{s///;s/\([^[:space:]]*\).*$/\1/;h;}' \
1535			-e '${g;p;}' /tmp/resolv.conf)
1536	fi
1537
1538	# Get/Confirm an IPv4 default route if an IPv4 address was configured.
1539	[[ -n $(ifconfig -a | sed -ne '/[ 	]inet .* broadcast /p') ]] && v4_defroute
1540
1541	# Get & apply fully qualified domain name to hostname.
1542	ask "DNS domain name? (e.g. 'bar.com')" "${_dn:=$(get_fqdn)}"
1543	hostname "$(hostname -s).$resp"
1544
1545	# Use a potential IPv4 default route as default nameserver
1546	[[ -n $_ns ]] || _ns=$(route -n show -inet | \
1547	    sed -ne '/^default */{s///; s/ .*//; p;}')
1548
1549	# Get/Confirm nameservers, and construct appropriate resolv.conf.
1550	ask "DNS nameserver? (IP address or 'none')" "${_ns:=none}"
1551	if [[ $resp != none ]]; then
1552		echo '# $MirSecuCron$' >/tmp/resolv.conf
1553		echo "lookup file bind" >>/tmp/resolv.conf
1554		for _ns in $resp; do
1555			echo "nameserver $_ns" >>/tmp/resolv.conf
1556		done
1557		ask_yn "Use the nameserver now?" yes
1558		[[ $resp = y ]] && cp /tmp/resolv.conf /tmp/resolv.conf.shadow
1559	fi
1560
1561	edit_tmp_file hosts
1562	manual_net_cfg
1563}
1564
1565questions() {
1566	[[ -e /mnt/etc/rc.conf.local ]] && mv -f /mnt/etc/rc.conf.local \
1567	    /mnt/etc/rc.conf.local~
1568
1569	ask_yn "Start sshd(8) by default?" yes
1570	if [[ $resp = n ]]; then
1571		echo "sshd_flags=NO		# disabled during install" \
1572		    >>/mnt/etc/rc.conf.local~
1573	fi
1574
1575	ask_yn "Start ntpd(8) by default?" yes
1576	if [[ $resp = y ]]; then
1577		echo "ntpd_flags=		# enabled during install" \
1578		    >>/mnt/etc/rc.conf.local~
1579		echo "rdate_flags='-nv ntp.mirbsd.org'" >>/mnt/etc/rc.conf.local~
1580	fi
1581
1582	if [[ -e /mnt/etc/rc.conf.local~ ]]; then
1583		(echo '# $MirSecuCron$'; echo; cat /mnt/etc/rc.conf.local~) \
1584		    >/mnt/etc/rc.conf.local
1585		rm -f /mnt/etc/rc.conf.local~
1586	fi
1587
1588	if [[ -n $MDXAPERTURE ]]; then
1589		echo 'This setting affects the machdep.allowaperture sysctl.'
1590		echo 'If you respond negatively, you must enable it later in'
1591		echo '/etc/sysctl.conf in order to be able to run XFree86(R).'
1592		if [[ -e /mnt/usr/X11R6/bin/X ]]; then
1593			resp=yes
1594		else
1595			resp=no
1596		fi
1597		ask_yn "Do you expect to run the X Window System?" $resp
1598		if [[ $resp = y ]]; then
1599			sed -e "/^#\(machdep\.allowaperture=${MDXAPERTURE}\)/s//\1	/" \
1600			    /mnt/etc/sysctl.conf >/tmp/sysctl.conf
1601			cp /tmp/sysctl.conf /mnt/etc/sysctl.conf
1602		fi
1603	fi
1604
1605	echo 'The size for the RSA host key can now be selected here. Larger'
1606	echo 'key sizes usually mean more security, but always imply much'
1607	echo 'longer key exchange times (e.g. mail delivery, ssh login), so'
1608	echo 'they are not recommended for old boxen (say, a SPARCstation)'
1609	echo 'or if you have to communicate with them very often; choose a'
1610	echo 'lower size (e.g. 2048) than the default of 4096 then.'
1611	ask_which "size" "should the RSA host key have" \
1612	    "2048 3072 4096 6144 8192" 4096
1613	[[ $resp = done ]] || print "/4096/s//$resp/\nwq" | ed -s /mnt/etc/rc
1614
1615	[[ -z $SERIALDEV ]] && return
1616	ask_yn "Change the default console to $SERIALDEV?"
1617	[[ $resp = n ]] && return
1618	ask_which "speed" "should $SERIALDEV use" "9600 19200 38400 57600 115200"
1619	[[ $resp = done ]] && return
1620	echo '# $MirSecuCron$' >/mnt/boot.cfg
1621	echo "set tty $SERIALDEV\nstty $SERIALDEV $resp" >>/mnt/boot.cfg
1622}
1623
1624finish_up() {
1625	typeset _dev _mp _fstype _rest
1626
1627	# Mount all known swap partitions.  This gives systems with little
1628	# memory a better chance at running 'MAKEDEV all'.
1629	if [[ -x /mnt/sbin/swapctl ]]; then
1630		/mnt/sbin/swapctl -a /dev/$SWAPDEV >/dev/null 2>&1
1631		# Can't do chmod && swapctl -A because devices are not yet
1632		# created on install'ed systems. On upgrade'ed system there
1633		# is a small chance the device does not exist on the ramdisk
1634		# and will thus not get mounted.
1635		while read _dev _mp _fstype _rest; do
1636			[[ $_fstype = swap ]] && \
1637			    /mnt/sbin/swapctl -a $_dev >/dev/null 2>&1
1638		done </mnt/etc/fstab
1639	fi
1640
1641	[[ ! -s /mnt/etc/ttys && -s /mnt/etc/ttys.dist ]] && \
1642	    cp /mnt/etc/ttys.dist /mnt/etc/ttys
1643
1644	if grep -qs '^rtsol' /mnt/etc/hostname.*; then
1645		sed -e "/^#\(net\.inet6\.ip6\.accept_rtadv\)/s//\1/" \
1646		    /mnt/etc/sysctl.conf >/tmp/sysctl.conf
1647		cp /tmp/sysctl.conf /mnt/etc/sysctl.conf
1648	fi
1649
1650	echo -n "Making all device nodes..."
1651	cd /mnt/dev
1652	mksh MAKEDEV all
1653	# Make sure any devices we found during probe are created in the
1654	# installed system.
1655	for _dev in $DKDEVS $CDDEVS $MTDEVS; do
1656		mksh MAKEDEV $_dev
1657	done
1658	echo "done."
1659	cd /
1660
1661	if [[ -e $ROOTDISK ]]; then
1662		_dev=$ROOTDISK
1663	else
1664		_dev=/dev/r${ROOTDISK}c
1665	fi
1666	# use extracted mdec if it exists (may be newer)
1667	if [ -e /mnt/usr/mdec/boot ]; then
1668		_prefix=/mnt/usr/mdec
1669	elif [ -e /usr/mdec/boot ]; then
1670		_prefix=/usr/mdec
1671	else
1672		_prefix=
1673	fi
1674	if [[ ! -e $_dev ]]; then
1675		print Cannot install bootblocks to "'$ROOTDISK'".
1676		print You must run installboot manually.
1677	elif [[ -z $_prefix ]]; then
1678		print No boot block prototypes found.
1679		print You must run installboot manually.
1680	else
1681		print Installing boot block...
1682		cat ${_prefix}/boot >/mnt/boot
1683		chmod 0 /mnt/boot
1684		sync; sync; sync
1685		${_prefix}/installboot -v /mnt/boot ${_prefix}/bootxx $_dev
1686		print done.
1687	fi
1688
1689	[ -x /mnt/$MODE.fixes ] && /mnt/usr/sbin/chroot /mnt /$MODE.fixes
1690	[ -x /mnt/$MODE.site ] && /mnt/usr/sbin/chroot /mnt /$MODE.site
1691
1692	# Pat on the back.
1693	cat <<__EOT
1694
1695CONGRATULATIONS! Your MirBSD $MODE has been successfully completed!
1696To boot the new system, enter halt at the command prompt. Once the
1697system has halted, reset the machine and boot from the disk.
1698
1699Hello there! We from the MirOS project would like to hear from you!
1700If you installed or updated your existing MirOS installation, which
1701architecture, maybe your country, a dmesg and a few words about how
1702you like MirOS. If you don't mind being counted to help us estimate
1703our userbase, mail to <miros-dev@mirbsd.de> - thanks in advance!
1704__EOT
1705
1706	md_congrats
1707}
1708
1709# #######################################################################
1710#
1711# Initial actions common to both installs and upgrades.
1712#
1713# Some may require machine dependent routines, which may
1714# call functions defined above, so it's safest to put this
1715# code here rather than at the top of the file.
1716#
1717# #######################################################################
1718
1719ulimit -c 0
1720
1721ROOTDISK=
1722ROOTDEV=
1723
1724VERSION=10
1725
1726# FTPDIR: prefix for SETDIR for ftp/http/https installs
1727FTPDIR="MirOS/"
1728#SETDIR="v${VERSION}/$ARCH"
1729#OBSD="MirOS BSD #$VERSION/$ARCH"
1730SETDIR="current/$ARCH"
1731OBSD="MirOS BSD #$VERSION-current/$ARCH"
1732SERVERLIST=/tmp/serverlist
1733
1734# Do not limit ourselves during installs or upgrades.
1735for _opt in d f l m n p s; do
1736	ulimit -$_opt unlimited
1737done
1738
1739# Scan /var/run/dmesg.boot for interesting devices.
1740DKDEVS=$(scan_dmesg "${MDDKDEVS:-/^r*a*[swi]d[0-9][0-9]* /s/ .*//p}")
1741CDDEVS=$(scan_dmesg "${MDCDDEVS:-/^cd[0-9][0-9]* /s/ .*//p}")
1742MTDEVS=$(scan_dmesg "${MDMTDEVS:-/^[cmsw]t[0-9][0-9]* /s/ .*//p}")
1743SERIALDEV=$(get_serialdev)
1744
1745# Selected sets will be installed in the order they are listed in $THESETS.
1746# Ensure that siteXX.ngz is the *last* set listed so its contents overwrite
1747# the contents of the other sets, not the other way around. Similarly fixes
1748# must be second-to-last.
1749THESETS="bsd bsd.rd $MDSETS"
1750DEFAULTSETS="bsd"
1751for _set in base etc gnu dev ada xbase xetc xfont xserv unfree pkgutl ports \
1752    source xfree pkgsrc psbsk fixes site; do
1753	[[ $MODE = upgrade && $_set = @(?(x)etc|ports|source|xfree|pkgsrc|psbsk) ]] && continue
1754	THESETS="$THESETS ${_set}${VERSION}.ngz"
1755	isin $_set base etc gnu dev fixes && \
1756	    DEFAULTSETS="$DEFAULTSETS ${_set}${VERSION}.ngz"
1757done
1758# Since etc${VERSION}.ngz is not in DEFAULTSETS for upgrades, it can always be
1759# in SANESETS.
1760SANESETS="bsd base${VERSION}.ngz etc${VERSION}.ngz"
1761
1762# decide upon an editor
1763: ${EDITOR:=ed}
1764[[ -x /usr/bin/vi ]] && EDITOR=vi
1765export EDITOR
1766
1767# umount all filesystems, just in case we are re-running install or upgrade.
1768[[ -f /etc/fstab ]] && umount -avt nomfs 1>/dev/null 2>&1
1769umount -v /mnt 1>/dev/null 2>&1
1770
1771# Introduce ourselves.
1772welcome
1773
1774# Get ROOTDISK, ROOTDEV and SWAPDEV.
1775if [[ $MODE = install && ! -f /etc/fstab ]]; then
1776	cat <<__EOT
1777
1778You will now initialise the disk(s) that MirBSD will use. To enable all
1779available security features you should configure the disk(s) to allow the
1780creation of separate filesystems for /, /tmp, /var, /usr, and /home.
1781
1782__EOT
1783fi
1784
1785set -- $DKDEVS
1786(( $# > 1 )) && _defdsk=done
1787
1788ask_which "disk" "is the root disk" "$DKDEVS" "$_defdsk"
1789[[ $resp = done ]] && exit
1790makedev $resp || exit
1791
1792ROOTDISK=$resp
1793ROOTDEV=${ROOTDISK}a
1794SWAPDEV=${ROOTDISK}b
1795