xref: /netbsd-src/distrib/miniroot/install.sub (revision 8fe9dcb1b33745b2709d110f84cdc72f68b13b7c)
1#!/bin/sh
2#	$NetBSD: install.sub,v 1.66 2024/05/11 06:31:59 andvar Exp $
3#
4# Copyright (c) 1996 The NetBSD Foundation, Inc.
5# All rights reserved.
6#
7# This code is derived from software contributed to The NetBSD Foundation
8# by Jason R. Thorpe.
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29# POSSIBILITY OF SUCH DAMAGE.
30#
31
32#	NetBSD installation/upgrade script - common subroutines.
33
34ROOTDISK=""				# filled in below
35MACHINE=				# filled by distrib/miniroot/list
36export MACHINE
37VERSION=100				# updated by distrib/miniroot/list
38export VERSION
39RELEASE=10.0				# updated by distrib/miniroot/list
40export RELEASE
41
42ALLSETS="base comp etc games man misc modules rescue text"	# default install sets
43UPGRSETS="base comp games man misc modules rescue text"		# default upgrade sets
44THESETS=						# one of the above
45
46local_sets_dir=""			# Path searched for sets by install_sets
47					# on the local filesystems
48
49# decide upon an editor
50if [ -z "$EDITOR" ]; then
51	if [ -x /usr/bin/vi ]; then
52		EDITOR=vi
53	else
54		EDITOR=ed
55	fi
56fi
57
58getresp() {
59	read resp
60	if [ -z "$resp" ]; then
61		resp=$1
62	fi
63}
64
65isin() {
66# test the first argument against the remaining ones, return success on a match
67	_a=$1; shift
68	while [ $# != 0 ]; do
69		if [ "$_a" = "$1" ]; then return 0; fi
70		shift
71	done
72	return 1
73}
74
75rmel() {
76# remove first argument from list formed by the remaining arguments
77	local	_a
78
79	_a=$1; shift
80	while [ $# != 0 ]; do
81		if [ "$_a" != "$1" ]; then
82			echo "$1";
83		fi
84		shift
85	done
86}
87
88cutword () {
89# read a line of data, return Nth element.
90	local _a
91	local _n
92	local _oifs
93
94	# optional field separator
95	_oifs="$IFS"
96	case "$1" in
97		-t?*) IFS=${1#-t}; shift;;
98	esac
99
100	_n=$1
101	read _a; set -- $_a
102	IFS="$_oifs"
103	if [ "$1" = "" ]; then return; fi
104	eval echo \$$_n
105}
106
107cutlast () {
108# read a line of data, return last element. Equiv. of awk '{print $NF}'.
109	local _a
110	local _oifs
111
112	# optional field separator
113	_oifs="$IFS"
114	case "$1" in
115		-t?*) IFS=${1#-t}; shift;;
116	esac
117
118	read _a; set -- $_a
119	IFS="$_oifs"
120	if [ "$1" = "" ]; then return; fi
121	eval echo '"${'"$#"'}"'
122}
123
124firstchar () {
125# return first character of argument
126	local _a
127	_a=$1
128	while [ ${#_a} != 1 ]; do
129		_a=${_a%?}
130	done
131	echo $_a
132}
133
134basename () {
135	local _oifs
136	if [ "$1" = "" ]; then return; fi
137	_oifs="$IFS"
138	IFS="/"
139	set -- $1
140	IFS="$_oifs"
141	eval echo '"${'"$#"'}"'
142}
143
144dir_has_sets() {
145	# return true when the directory $1 contains a set for $2...$n
146	local _dir
147	local _file
148
149	_dir=$1; shift
150	for _file in $*
151	do
152		if [ -f $_dir/${_file}.tar.gz ]; then
153			return 0
154		fi
155		# Try for stupid msdos convention
156		if [ -f $_dir/${_file}.tgz ]; then
157			return 0
158		fi
159		# Try for uncompressed files
160		if [ -f $_dir/${_file}.tar ]; then
161			return 0
162		fi
163		# Try for split files
164		if [ -f $_dir/${_file}${VERSION}.aa ]; then
165			return 0
166		fi
167	done
168	return 1
169}
170
171twiddle() {
172# spin the propeller so we don't get bored
173	while : ; do
174		sleep 1; echo -n "/";
175		sleep 1; echo -n "-";
176		sleep 1; echo -n "\\";
177		sleep 1; echo -n "|";
178	done > /dev/tty & echo $!
179}
180
181get_localdir() {
182	# $1 is relative mountpoint
183	local _mp
184	local _dir
185
186	_mp=$1
187	_dir=
188	while : ; do
189	    if [ -n "$_mp" ]; then
190		cat << __get_localdir_1
191Note: your filesystems are mounted under the temporary mount point \"$_mp\".
192The pathname you are requested to enter below should NOT include the \"$_mp\"
193prefix.
194__get_localdir_1
195	    fi
196	    echo -n "Enter the pathname where the sets are stored [$_dir] "
197	    getresp "$_dir"
198	    _dir=$resp
199
200	    # Allow break-out with empty response
201	    if [ -z "$_dir" ]; then
202		echo -n "Are you sure you don't want to set the pathname? [n] "
203		getresp "n"
204		case "$resp" in
205			y*|Y*)
206				break
207				;;
208			*)
209				continue
210				;;
211		esac
212	    fi
213
214	    if dir_has_sets "$_mp/$_dir" $THESETS
215	    then
216		local_sets_dir="$_mp/$_dir"
217		break
218	    else
219		cat << __get_localdir_2
220The directory \"$_mp/$_dir\" does not exist, or does not hold any of the
221upgrade sets.
222__get_localdir_2
223		echo -n "Re-enter pathname? [y] "
224		getresp "y"
225		case "$resp" in
226			y*|Y*)
227				;;
228			*)
229				local_sets_dir=""
230				break
231				;;
232		esac
233	    fi
234	done
235}
236
237getrootdisk() {
238	cat << \__getrootdisk_1
239
240The installation program needs to know which disk to consider
241the root disk.  Note the unit number may be different than
242the unit number you used in the standalone installation
243program.
244
245Available disks are:
246
247__getrootdisk_1
248	_DKDEVS=$(md_get_diskdevs)
249	echo	"$_DKDEVS"
250	echo	""
251	echo -n	"Which disk is the root disk? "
252	getresp ""
253	if isin $resp $_DKDEVS ; then
254		ROOTDISK="$resp"
255	else
256		echo ""
257		echo "The disk $resp does not exist."
258		ROOTDISK=""
259	fi
260}
261
262labelmoredisks() {
263	cat << \__labelmoredisks_1
264
265You may label the following disks:
266
267__labelmoredisks_1
268	echo "$_DKDEVS"
269	echo	""
270	echo -n	"Label which disk? [done] "
271	getresp "done"
272	case "$resp" in
273		"done")
274			;;
275
276		*)
277			if isin $resp $_DKDEVS ; then
278				md_labeldisk $resp
279			else
280				echo ""
281				echo "The disk $resp does not exist."
282			fi
283			;;
284	esac
285}
286
287addhostent() {
288	# $1 - IP address
289	# $2 - symbolic name
290
291	local fqdn
292
293	# Create an entry in the hosts table.  If no host table
294	# exists, create one.  If the IP address already exists,
295	# replace its entry.
296	if [ ! -f /tmp/hosts ]; then
297		echo "127.0.0.1 localhost" > /tmp/hosts
298	fi
299
300	sed "/^$1 /d" < /tmp/hosts > /tmp/hosts.new
301	mv /tmp/hosts.new /tmp/hosts
302
303	if [ -n "${FQDN}" ]; then
304		fqdn=$2.$FQDN
305	fi
306	echo "$1 $2 $fqdn" >> /tmp/hosts
307}
308
309addifconfig() {
310	# $1 - interface name
311	# $2 - interface symbolic name
312	# $3 - interface IP address
313	# $4 - interface netmask
314	# $5 - (optional) interface link-layer medium, preceded by "media ", else ""
315	# $6 - (optional) interface link-layer directives
316	local _m
317
318	# Create a ifconfig.* file for the interface.
319	echo "inet $2 netmask $4 $5 $6" > /tmp/ifconfig.$1
320
321	addhostent $3 $2
322}
323
324configurenetwork() {
325	local _ifsdone
326	local _ifs
327
328#	_IFS=$(md_get_ifdevs)
329	_IFS=$(ifconfig -l | sed '
330		s/lo0//
331		s/ppp[0-9]//g
332		s/sl[0-9]//g
333		s/tun[0-9]//g')
334
335	_ifsdone=""
336	resp=""		# force at least one iteration
337	while [ "${resp}" != "done" ]; do
338	cat << \__configurenetwork_1
339
340You may configure the following network interfaces (the interfaces
341marked with [X] have been successfully configured):
342
343__configurenetwork_1
344
345		for _ifs in $_IFS; do
346			if isin $_ifs $_ifsdone ; then
347				echo -n "[X] "
348			else
349				echo -n "    "
350			fi
351			echo $_ifs
352		done
353		echo	""
354		echo -n	"Configure which interface? [done] "
355		getresp "done"
356		case "$resp" in
357		"done")
358			;;
359		*)
360			_ifs=$resp
361			if isin $_ifs $_IFS ; then
362				if configure_ifs $_ifs ; then
363					_ifsdone="$_ifs $_ifsdone"
364				fi
365			else
366				echo "Invalid response: \"$resp\" is not in list"
367			fi
368			;;
369		esac
370	done
371}
372
373configure_ifs() {
374
375	local _up
376	local _interface_name
377	local _interface_ip
378	local _interface_mask
379	local _interface_symname
380	local _interface_extra
381	local _interface_mediumtype
382	local _interface_supported_media
383	local _m
384	local _t
385
386	_interface_name=$1
387	_up=DOWN
388	if isin $_interface_name $(ifconfig -l -u); then
389		_up=UP
390	fi
391
392	_interface_supported_media=$(ifconfig -m $_interface_name | sed -n '
393		/^[ 	]*media autoselect/d
394		4,$s/[ 	]*media //p')
395
396	# get current "media" "ip" and "netmask" ("broadcast")
397	_t=$(ifconfig $_interface_name | sed -n '
398		s/^[ 	]*media: [^ 	]* \([^ ][^ ]*\).*/\1/p')
399
400	if [ "$_t" != "manual" ] && [ "$_t" != "media:" ] && [ "$_t" != "autoselect" ];
401	then
402		_interface_mediumtype=$1
403	fi
404
405	set -- $(ifconfig $_interface_name | sed -n '
406		/^[ 	]*inet /{
407		s/inet//
408		s,/[0-9]*,,
409		s/--> [0-9.][0-9.]*//
410		s/netmask//
411		s/broadcast//
412		p;}')
413
414	_interface_ip=$1
415	_interface_mask=$2
416
417	# Get IP address
418	resp=""		# force one iteration
419	while [ -z "${resp}" ]; do
420		echo -n "IP address? [$_interface_ip] "
421		getresp "$_interface_ip"
422		_interface_ip=$resp
423	done
424
425	# Get symbolic name
426	resp=""		# force one iteration
427	while [ -z "${resp}" ]; do
428		echo -n "Symbolic (host) name? "
429		getresp ""
430		_interface_symname=$resp
431	done
432
433	# Get netmask
434	resp=""		# force one iteration
435	while [ -z "${resp}" ]; do
436		echo -n "Netmask? [$_interface_mask] "
437		getresp "$_interface_mask"
438		_interface_mask=$resp
439	done
440
441	echo "Your network interface might require explicit selection"
442	echo "of the type of network medium attached. Supported media:"
443	echo "$_interface_supported_media"
444	echo -n "Additional media type arguments (none)? [$_interface_mediumtype] "
445	getresp "$_interface_mediumtype"
446	_m=""
447	if [ "${resp:-none}" != "none" ]; then
448		_interface_mediumtype=$resp
449		_m="media ${resp}"
450	fi
451
452
453	echo "Your network interface might require additional link-layer"
454	echo "directives (like 'link0'). If this is the case you can enter"
455	echo "these at the next prompt."
456	echo ""
457	echo -n "Additional link-layer arguments (none)? [$_interface_extra] "
458	getresp "$_interface_extra"
459	if [ "${resp:-none}" != "none" ]; then
460		_interface_extra=$resp
461	fi
462
463	# Configure the interface.  If it
464	# succeeds, add it to the permanent
465	# network configuration info.
466	if [ $_up != "UP" ]; then
467		ifconfig ${_interface_name} down
468		if ifconfig ${_interface_name} inet \
469		    ${_interface_ip} \
470		    netmask ${_interface_mask} \
471		    ${_interface_extra} ${_m} up ; then
472			addifconfig \
473			    "${_interface_name}" \
474			    "${_interface_symname}" \
475			    "${_interface_ip}" \
476			    "${_interface_mask}" \
477			    "${_m}" \
478			    "${_interface_extra}"
479			return 0
480		fi
481	else
482		echo "Interface ${_interface_name} is already active."
483		echo "Just saving configuration on new root filesystem."
484		addifconfig \
485		    "${_interface_name}" \
486		    "${_interface_symname}" \
487		    "${_interface_ip}" \
488		    "${_interface_mask}" \
489		    "${_m}" \
490		    "${_interface_extra}"
491	fi
492	return 1
493}
494
495# Much of this is gratuitously stolen from /etc/rc.d/network.
496enable_network() {
497
498	# Set up the hostname.
499	if [ -f /mnt/etc/myname ]; then
500		hostname=$(cat /mnt/etc/myname)
501	elif [ -f /mnt/etc/rc.conf ];then
502		hostname=$(sh -c '. /mnt/etc/rc.conf ; echo $hostname')
503	else
504		echo "ERROR: no /etc/myname!"
505		return 1
506	fi
507	if [ -z "$hostname" ];then
508		echo "ERROR: hostname not set in /etc/myname or /etc/rc.conf!"
509		return 1
510	fi
511	hostname $hostname
512
513	# configure all the interfaces which we know about.
514if [ -f /mnt/etc/rc.conf ]; then
515(
516	# assume network interface configuration style 1.2D and up
517	if [ -f /mnt/etc/defaults/rc.conf ]; then
518		. /mnt/etc/defaults/rc.conf
519	fi
520	. /mnt/etc/rc.conf
521
522	if [ "$net_interfaces" != NO ]; then
523		if [ "$auto_ifconfig" = YES ]; then
524			tmp="$(ifconfig -l)"
525		else
526			tmp="$net_interfaces"
527		fi
528		echo -n "configuring network interfaces:"
529		for i in $tmp; do
530			eval $(echo 'args=$ifconfig_'$i)
531			if [ -n "$args" ]; then
532				echo -n " $i"
533				ifconfig $i $args
534			elif [ -f /mnt/etc/ifconfig.$i ]; then
535				echo -n " $i"
536				(while read args; do
537					ifconfig $i $args
538				done) < /mnt/etc/ifconfig.$i
539			elif [ "$auto_ifconfig" != YES ]; then
540				echo
541				echo -n "/mnt/etc/ifconfig.$i missing"
542				echo -n "& ifconfig_$i not set"
543				echo "; interface $i can't be configured"
544			fi
545		done
546		echo "."
547	fi
548)
549else
550(
551	tmp="$IFS"
552	IFS="$IFS."
553	set -- $(echo /mnt/etc/hostname*)
554	IFS=$tmp
555	unset tmp
556
557	while [ $# -ge 2 ] ; do
558		shift		# get rid of "hostname"
559		(
560			read af name mask bcaddr extras
561			read dt dtaddr
562
563			if [ -z "$name" ]; then
564		    echo "/etc/hostname.$1: invalid network configuration file"
565				exit
566			fi
567
568			cmd="ifconfig $1 $af $name "
569			if [ "${dt}" = "dest" ]; then cmd="$cmd $dtaddr"; fi
570			if [ -n "$mask" ]; then cmd="$cmd netmask $mask"; fi
571			if [ "${bcaddr:-NONE}" != "NONE" ]; then
572				cmd="$cmd broadcast $bcaddr";
573			fi
574			cmd="$cmd $extras"
575
576			$cmd
577		) < /mnt/etc/hostname.$1
578		shift
579	done
580)
581fi
582
583	# set the address for the loopback interface
584	ifconfig lo0 inet localhost
585
586	# use loopback, not the wire
587	route add $hostname localhost
588
589	# /etc/mygate, if it exists, contains the name of my gateway host
590	# that name must be in /etc/hosts.
591	if [ -f /mnt/etc/mygate ]; then
592		route delete default > /dev/null 2>&1
593		route add default $(cat /mnt/etc/mygate)
594	fi
595
596	# enable the resolver, if appropriate.
597	if [ -f /mnt/etc/resolv.conf ]; then
598		_resolver_enabled="TRUE"
599		cp /mnt/etc/resolv.conf /tmp/resolv.conf.shadow
600	fi
601
602	# Display results...
603	echo	"Network interface configuration:"
604	ifconfig -a
605
606	echo	""
607
608	if [ "${_resolver_enabled:-FALSE}" = "TRUE" ]; then
609		echo	"Resolver enabled."
610	else
611		echo	"Resolver not enabled."
612	fi
613
614	return 0
615}
616
617install_ftp() {
618	local	_f
619	local	_sets
620	local	_next
621
622	# Build a script to extract valid files from a list
623	# of filenames on stdin.
624	# XXX : Can we use this on more places? Leo.
625
626	echo "#!/bin/sh" > /tmp/fname_filter.sh
627	echo "while read line; do"	>> /tmp/fname_filter.sh
628	echo "    case \$line in"	>> /tmp/fname_filter.sh
629	for _f in $THESETS; do
630		echo "    $_f.tar.gz|$_f.tgz|$_f.tar|$_f.${VERSION}.aa)" \
631					>> /tmp/fname_filter.sh
632		echo '        echo -n "$line ";;' \
633					>> /tmp/fname_filter.sh
634	done
635	echo "        *) ;;"		>> /tmp/fname_filter.sh
636	echo "    esac"			>> /tmp/fname_filter.sh
637	echo "done"			>> /tmp/fname_filter.sh
638
639	# Get several parameters from the user, and create
640	# a shell script that directs the appropriate
641	# commands into ftp.
642	cat << \__install_ftp_1
643
644This is an automated ftp-based installation process.  You will be asked
645several questions.  The correct set of commands will be placed in a script
646that will be fed to ftp(1).
647
648__install_ftp_1
649	# Get server IP address
650	resp=""		# force one iteration
651	while [ -z "${resp}" ]; do
652		echo -n "Server IP? [${_ftp_server_ip}] "
653		getresp "${_ftp_server_ip}"
654		_ftp_server_ip=$resp
655	done
656
657	# Get login name
658	resp=""		# force one iteration
659	while [ -z "${resp}" ]; do
660		echo -n "Login? [${_ftp_server_login}] "
661		getresp "${_ftp_server_login}"
662		_ftp_server_login=$resp
663	done
664
665	# Get password
666	resp=""		# force one iteration
667	while [ -z "${resp}" ]; do
668		echo -n "Password? "
669		stty -echo
670		getresp ""
671		echo ""
672		stty echo
673		_ftp_server_password=$resp
674	done
675
676	cat << \__install_ftp_2
677
678You will be asked to enter the name of the directory that contains the
679installation sets. When you enter a '?' you will see a listing of the
680current directory on the server.
681__install_ftp_2
682	echo ""
683	echo "The default installation directory in the official ftp server is:"
684	echo "/pub/NetBSD/NetBSD-${RELEASE}/${MACHINE}/binary/sets"
685
686	_sets=""
687	while [ -z "$_sets" ]
688	do
689		resp=""		# force one iteration
690		while [ -z "${resp}" ]; do
691			echo -n "Server directory? [${_ftp_server_dir}] "
692		    getresp "${_ftp_server_dir}"
693		    if [ -z "$resp" ] && [ -z "$_ftp_server_dir" ]; then
694			resp=""
695		    fi
696		done
697		if [ $resp != '?' ]; then
698			_ftp_server_dir=$resp
699		fi
700
701		# Build the basics of an ftp-script...
702		echo "#!/bin/sh" > /tmp/ftp-script.sh
703		echo "cd /mnt" >> /tmp/ftp-script.sh
704		echo "ftp -e -i -n $_ftp_server_ip << \__end_commands" >> \
705		    /tmp/ftp-script.sh
706		echo "user $_ftp_server_login $_ftp_server_password" >> \
707		    /tmp/ftp-script.sh
708		echo "bin" >> /tmp/ftp-script.sh
709		echo "cd $_ftp_server_dir" >> /tmp/ftp-script.sh
710
711		# Make a copy of this script that lists the directory
712		# contents, and use that to determine the files to get.
713		cat /tmp/ftp-script.sh	>  /tmp/ftp-dir.sh
714		echo "nlist"		>> /tmp/ftp-dir.sh
715		echo "quit"		>> /tmp/ftp-dir.sh
716		echo "__end_commands"	>> /tmp/ftp-dir.sh
717
718		if [ $resp = '?' ]; then
719			sh /tmp/ftp-dir.sh
720		else
721			_sets=$(sh /tmp/ftp-dir.sh | sort -u | sh /tmp/fname_filter.sh)
722		fi
723	done
724	rm -f /tmp/ftp-dir.sh /tmp/fname_filter.sh
725	rm -f /tmp/ftp-script.sh
726
727	# Prepare ftp-fetch script to fetch binary sets
728	_download_dir=INSTALL
729	_ftp_opts=""
730	_ftp_url="ftp://$_ftp_server_login:$_ftp_server_password@$_ftp_server_ip$_ftp_server_dir/"
731	echo "#!/bin/sh" > /tmp/ftp-fetch.sh
732	echo "cd /mnt" >> /tmp/ftp-fetch.sh
733	echo "mkdir -p $_download_dir" >> /tmp/ftp-fetch.sh
734
735	while : ; do
736		echo "The following sets are available for extraction:"
737		echo "(marked sets are already on the extraction list)"
738		echo ""
739
740		_next=""
741		for _f in $_sets ; do
742			if isin $_f $_setsdone; then
743				echo -n "[X] "
744				_next=""
745			else
746				echo -n "    "
747				if [ -z "$_next" ]; then _next=$_f; fi
748			fi
749			echo $_f
750		done
751		echo ""
752
753		# Get name of the file and add extraction command
754		# to the ftp-fetch script.
755		if [ -z "$_next" ]; then resp=n; else resp=y; fi
756		echo -n "Continue to add filenames [$resp]? "
757		getresp "$resp"
758		if [ "$resp" = "n" ]; then
759			break
760		fi
761
762		echo -n "File name [$_next]? "
763		getresp "$_next"
764		if isin $resp $_sets; then
765			echo "echo Fetching $resp:" >> \
766					/tmp/ftp-fetch.sh
767			echo "ftp ${_ftp_opts} -o $_download_dir/$resp ${_ftp_url}$resp" >> \
768					/tmp/ftp-fetch.sh
769			echo "echo Extracting $resp:" >> \
770					/tmp/ftp-fetch.sh
771			echo "pax -zr${verbose_flag}pe -f $_download_dir/$resp" >> \
772					/tmp/ftp-fetch.sh
773			echo "rm -f $_download_dir/$resp" >> \
774					/tmp/ftp-fetch.sh
775			_setsdone="$resp $_setsdone"
776		else
777			echo "You entered an invalid filename."
778			echo ""
779		fi
780	done
781
782	sh /tmp/ftp-fetch.sh
783	rm -f /tmp/ftp-fetch.sh
784	echo "Extraction complete."
785}
786
787install_from_mounted_fs() {
788	# $1 - directory containing installation sets
789	local _filename
790	local _sets
791	local _next
792	local _all
793	local _f
794	local _dirname
795
796	_dirname=$1
797	_sets=""
798
799	if ! dir_has_sets ${_dirname} $THESETS
800	then
801
802		echo ""
803		echo "The directory at the mount point, \"${_dirname}\", contains: "
804		echo ""
805		ls -F ${_dirname}
806		echo ""
807		echo    "Enter the subdirectory relative to the mountpoint, that"
808		echo -n "contains the savesets: [try this directory] "
809		getresp ""
810		if [ -n "${resp}" ]; then
811			_dirname=${_dirname}/$resp
812		fi
813
814		while ! dir_has_sets ${_dirname} $THESETS; do
815			echo ""
816			echo -n "There are no NetBSD install sets available in "
817			echo "\"${_dirname}\"."
818			echo "\"${_dirname}\" contains: "
819			echo ""
820			ls -F ${_dirname}
821			echo ""
822			echo -n "Enter subdirectory: [try other install media] "
823			getresp ""
824			if [ -z "${resp}" ]; then
825				return
826			fi
827			if [ ! -d ${_dirname}/${resp} ]; then
828				echo "\"${resp}\" is no directory; try again."
829			else
830				_dirname=${_dirname}/$resp
831			fi
832		done
833	fi
834
835	for _f in $THESETS ; do
836		if [ -f ${_dirname}/${_f}.tar.gz ]; then
837			_sets="$_sets ${_f}.tar.gz"
838		elif [ -f ${_dirname}/${_f}.tgz ]; then
839			_sets="$_sets ${_f}.tgz"
840		elif [ -f ${_dirname}/${_f}.tar ]; then
841			_sets="$_sets ${_f}.tar"
842		elif [ -f ${_dirname}/${_f}${VERSION}.aa ]; then
843			_sets="$_sets ${_f}${VERSION}"
844		fi
845	done
846
847	while : ; do
848		echo "The following sets are available for extraction:"
849		echo "(marked sets have already been extracted)"
850		echo ""
851
852		_next=""
853		_all=""
854		for _f in $_sets ; do
855			if isin $_f $_setsdone; then
856				echo -n "[X] "
857				_next=""
858			else
859				echo -n "    "
860				if [ -z "$_next" ]; then
861					_next=$_f;
862				fi
863				_all="$_all $_f"
864			fi
865			echo $_f
866		done
867		echo ""
868
869		# Get the name of the file.
870		if [ -z "$_next" ]; then
871			resp=n
872		else
873			resp=y
874		fi
875		echo -n "Continue extraction [$resp]?"
876		getresp "$resp"
877		if [ "$resp" = "n" ]; then
878			break
879		fi
880
881		echo -n "File name(s) (or "all") [$_next]? "
882		getresp "$_next"
883		if [ "x$resp" = xall ]; then
884			resp="$_all"
885		fi
886
887		for _f in $resp; do
888			_filename="/${_dirname}/$_f"
889
890			# Ensure file exists
891			if [ ! -f $_filename ]; then
892				if [ -f ${_filename}.aa ]; then
893					_filename=${_filename}.\?\?
894				else
895			 echo "File $_filename does not exist.  Check to make"
896			 echo "sure you entered the information properly."
897			 continue 2
898				fi
899			fi
900
901			# Extract file
902			echo "Extracting the $_f set:"
903			case "$_filename" in
904			*.tar)
905				(cd /mnt; pax -r${verbose_flag}pe < $_filename)
906				;;
907			*)
908				cat $_filename | \
909					(cd /mnt; pax -zr${verbose_flag}pe)
910				;;
911			esac
912			echo "Extraction complete."
913			_setsdone="$_f $_setsdone"
914		done
915
916	done
917}
918
919install_cdrom() {
920	local _drive
921	local _partition_range
922	local _partition
923	local _fstype
924	local _directory
925
926	# Get the cdrom device info
927	cat << \__install_cdrom_1
928
929The following CD-ROM devices are installed on your system; please select
930the CD-ROM device containing the partition with the installation sets:
931
932__install_cdrom_1
933	_CDDEVS=$(md_get_cddevs)
934	echo    "$_CDDEVS"
935	echo	""
936	echo -n	"Which is the CD-ROM with the installation media? [abort] "
937	getresp "abort"
938	case "$resp" in
939		abort)
940			echo "Aborting."
941			return
942			;;
943
944		*)
945			if isin $resp $_CDDEVS ; then
946				_drive=$resp
947			else
948				echo ""
949				echo "The CD-ROM $resp does not exist."
950				echo "Aborting."
951				return
952			fi
953			;;
954	esac
955
956	# Get partition
957	_partition_range=$(md_get_partition_range)
958	resp=""		# force one iteration
959	while [ -z "${resp}" ]; do
960		echo -n "Partition? [a] "
961		getresp "a"
962		case "$resp" in
963			$_partition_range)
964				_partition=$resp
965				;;
966
967			*)
968				echo "Invalid response: $resp"
969				resp=""		# force loop to repeat
970				;;
971		esac
972	done
973
974	# Ask for filesystem type
975	cat << \__install_cdrom_2
976
977There are two CD-ROM filesystem types currently supported by this program:
978	1) ISO-9660 (cd9660)
979	2) Berkeley Fast Filesystem (ffs)
980
981__install_cdrom_2
982	resp=""		# force one iteration
983	while [ -z "${resp}" ]; do
984		echo -n "Which filesystem type? [cd9660] "
985		getresp "cd9660"
986		case "$resp" in
987			cd9660|ffs)
988				_fstype=$resp
989				;;
990
991			*)
992				echo "Invalid response: $resp"
993				resp=""		# force loop to repeat
994				;;
995		esac
996	done
997
998	# Mount the CD-ROM
999	if ! mount -t ${_fstype} -o ro \
1000	    /dev/${_drive}${_partition} /mnt2 ; then
1001		echo "Cannot mount CD-ROM drive.  Aborting."
1002		return
1003	fi
1004
1005	install_from_mounted_fs /mnt2
1006	umount -f /mnt2 > /dev/null 2>&1
1007}
1008
1009mount_a_disk() {
1010	# Mount a disk on /mnt2. The set of disk devices to choose from
1011	# is $_DKDEVS.
1012	# returns 0 on failure.
1013
1014	local _drive
1015	local _partition_range
1016	local _partition
1017	local _fstype
1018	local _fsopts
1019	local _directory
1020	local _md_fstype
1021	local _md_fsopts
1022
1023	getresp "abort"
1024	case "$resp" in
1025		abort)
1026			echo "Aborting."
1027			return 0
1028			;;
1029
1030		*)
1031			if isin $resp $_DKDEVS ; then
1032				_drive=$resp
1033			else
1034				echo ""
1035				echo "The disk $resp does not exist."
1036				echo "Aborting."
1037				return 0
1038			fi
1039			;;
1040	esac
1041
1042	# Get partition
1043	_partition_range=$(md_get_partition_range)
1044	resp=""		# force one iteration
1045	while [ -z "${resp}" ]; do
1046		echo -n "Partition? [d] "
1047		getresp "d"
1048		case "$resp" in
1049			$_partition_range)
1050				_partition=$resp
1051				;;
1052
1053			*)
1054				echo "Invalid response: $resp"
1055				resp=""		# force loop to repeat
1056				;;
1057		esac
1058	done
1059
1060	# Ask for filesystem type
1061	cat << \__mount_a_disk_2
1062
1063The following filesystem types are supported:
1064	1) ffs
1065	2) cd9660
1066__mount_a_disk_2
1067	_md_fstype=$(md_native_fstype)
1068	_md_fsopts=$(md_native_fsopts)
1069	if [ -n "$_md_fstype" ]; then
1070		echo "	3) $_md_fstype"
1071	else
1072		_md_fstype="_undefined_"
1073	fi
1074	resp=""		# force one iteration
1075	while [ -z "${resp}" ]; do
1076		echo -n "Which filesystem type? [ffs] "
1077		getresp "ffs"
1078		case "$resp" in
1079			ffs|cd9660)
1080				_fstype=$resp
1081				_fsopts="ro"
1082				;;
1083			$_md_fstype)
1084				_fstype=$resp
1085				_fsopts=$_md_fsopts
1086				;;
1087			*)
1088				echo "Invalid response: $resp"
1089				resp=""		# force loop to repeat
1090				;;
1091		esac
1092	done
1093
1094	# Mount the disk
1095	if ! mount -t ${_fstype} -o $_fsopts \
1096	    /dev/${_drive}${_partition} /mnt2 ; then
1097		echo "Cannot mount disk.  Aborting."
1098		return 0
1099	fi
1100	return 1
1101}
1102
1103install_disk() {
1104	local _directory
1105
1106	cat << \__install_disk_1
1107
1108Ok, lets install from a disk.  The file-system the install sets on may
1109already mounted, or we might have to mount the filesystem to get to it.
1110
1111__install_disk_1
1112
1113	echo -n "Is the file-system with the install sets already mounted? [n] "
1114	getresp "n"
1115	case $resp in
1116	y*|Y*)
1117		echo "What mount point are the sets located in? [] "
1118		getresp ""
1119		if [ -d "$resp" ]; then
1120			install_from_mounted_fs $resp
1121		else
1122			echo "$resp: Not a directory, aborting..."
1123		fi
1124		return
1125		;;
1126	*)
1127		;;
1128	esac
1129
1130	cat << \__install_disk_2
1131
1132The following disk devices are installed on your system; please select
1133the disk device containing the partition with the installation sets:
1134
1135__install_disk_2
1136	_DKDEVS=$(md_get_diskdevs)
1137	echo    "$_DKDEVS"
1138	echo	""
1139	echo -n	"Which is the disk with the installation sets? [abort] "
1140
1141	if mount_a_disk ; then
1142		return
1143	fi
1144
1145	install_from_mounted_fs /mnt2
1146	umount -f /mnt2 > /dev/null 2>&1
1147}
1148
1149install_nfs() {
1150	# Get the IP address of the server
1151	resp=""		# force one iteration
1152	while [ -z "${resp}" ]; do
1153		echo -n "Server IP address? [${_nfs_server_ip}] "
1154		getresp "${_nfs_server_ip}"
1155	done
1156	_nfs_server_ip=$resp
1157
1158	# Get server path to mount
1159	resp=""		# force one iteration
1160	while [ -z "${resp}" ]; do
1161		echo -n "Filesystem on server to mount? [${_nfs_server_path}] "
1162		getresp "${_nfs_server_path}"
1163	done
1164	_nfs_server_path=$resp
1165
1166	# Check mount_nfs(8) options
1167	echo "Use small NFS transfers (needed when server or client"
1168	echo -n "has a slow network card)? [n] "
1169	getresp "n"
1170	case "$resp" in
1171		y*|Y*)
1172			_nfs_tcp="-r 1024 -w 1024"
1173			;;
1174
1175		*)
1176			_nfs_tcp=""
1177			;;
1178	esac
1179
1180	# Mount the server
1181	mkdir /mnt2 > /dev/null 2>&1
1182	if ! mount_nfs $_nfs_tcp ${_nfs_server_ip}:${_nfs_server_path} \
1183	    /mnt2 ; then
1184		echo "Cannot mount NFS server.  Aborting."
1185		return
1186	fi
1187
1188	install_from_mounted_fs /mnt2
1189	umount -f /mnt2 > /dev/null 2>&1
1190}
1191
1192install_tape() {
1193	local _xcmd
1194
1195	# Get the name of the tape from the user.
1196	cat << \__install_tape_1
1197
1198The installation program needs to know which tape device to use.  Make
1199sure you use a "no rewind on close" device.
1200
1201__install_tape_1
1202	_tape=$(basename $TAPE)
1203	resp=""		# force one iteration
1204	while [ -z "${resp}" ]; do
1205		echo -n "Name of tape device? [${_tape}]"
1206		getresp "${_tape}"
1207	done
1208	_tape=$(basename $resp)
1209	TAPE="/dev/${_tape}"
1210	if [ ! -c $TAPE ]; then
1211		echo "$TAPE does not exist or is not a character special file."
1212		echo "Aborting."
1213		return
1214	fi
1215	export TAPE
1216
1217	# Rewind the tape device
1218	echo -n "Rewinding tape..."
1219	if ! mt rewind ; then
1220		echo "$TAPE may not be attached to the system or may not be"
1221		echo "a tape device.  Aborting."
1222		return
1223	fi
1224	echo "done."
1225
1226	# Get the file number
1227	resp=""		# force one iteration
1228	while [ -z "${resp}" ]; do
1229		echo -n "File number? "
1230		getresp ""
1231		case "$resp" in
1232			[1-9]*)
1233				_nskip=$(expr $resp - 1)
1234				;;
1235
1236			*)
1237				echo "Invalid file number ${resp}."
1238				resp=""		# fore loop to repeat
1239				;;
1240		esac
1241	done
1242
1243	# Skip to correct file.
1244	echo -n "Skipping to source file..."
1245	if [ "${_nskip}" != "0" ]; then
1246		if ! mt fsf $_nskip ; then
1247			echo "Could not skip $_nskip files.  Aborting."
1248			return
1249		fi
1250	fi
1251	echo "done."
1252
1253	cat << \__install_tape_2
1254
1255There are 2 different ways the file can be stored on tape:
1256
1257	1) an image of a gzipped tar file
1258	2) a standard tar image
1259
1260__install_tape_2
1261	resp=""		# force one iteration
1262	while [ -z "${resp}" ]; do
1263		echo -n "Which way is it? [1] "
1264		getresp "1"
1265		case "$resp" in
1266		1)
1267			_xcmd="pax -zr${verbose_flag}pe"
1268			;;
1269
1270		2)
1271			_xcmd="pax -r${verbose_flag}pe"
1272			;;
1273
1274		*)
1275			echo "Invalid response: $resp."
1276			resp=""		# force loop to repeat
1277			;;
1278		esac
1279		( cd /mnt; dd if=$TAPE | $_xcmd )
1280	done
1281	echo "Extraction complete."
1282}
1283
1284get_timezone() {
1285	local _a
1286	local _zonepath
1287
1288	#
1289	# If the zoneinfo is not on the installation medium or on the
1290	# installed filesystem, set TZ to GMT and return immediately.
1291	#
1292	if [ ! -e /usr/share/zoneinfo ] && [ ! -e /mnt/usr/share/zoneinfo ]; then
1293		TZ=GMT
1294		return
1295	fi
1296	if [ ! -d /usr/share/zoneinfo ]; then
1297		_zonepath=/mnt
1298	else
1299		_zonepath=""
1300	fi
1301
1302cat << \__get_timezone_1
1303
1304Select a time zone for your location. Timezones are represented on the
1305system by a directory structure rooted in "/usr/share/zoneinfo". Most
1306timezones can be selected by entering a token like "MET" or "GMT-6".
1307Other zones are grouped by continent, with detailed zone information
1308separated by a slash ("/"), e.g. "US/Pacific".
1309
1310To get a listing of what's available in /usr/share/zoneinfo, enter "?"
1311at the prompts below.
1312
1313__get_timezone_1
1314	if [ -z "$TZ" ]; then
1315		TZ=$(ls -l /mnt/etc/localtime 2>/dev/null | cutlast)
1316		TZ=${TZ#/usr/share/zoneinfo/}
1317	fi
1318	while :; do
1319		echo -n	"What timezone are you in ['?' for list] [$TZ]? "
1320		getresp "$TZ"
1321		case "$resp" in
1322		"")
1323			echo "Timezone defaults to GMT"
1324			TZ="GMT"
1325			break;
1326			;;
1327		"?")
1328			ls ${_zonepath}/usr/share/zoneinfo
1329			;;
1330		*)
1331			_a=$resp
1332			while [ -d ${_zonepath}/usr/share/zoneinfo/$_a ]; do
1333				echo -n "There are several timezones available"
1334				echo " within zone '$_a'"
1335				echo -n "Select a sub-timezone ['?' for list]: "
1336				getresp ""
1337				case "$resp" in
1338				"?") ls ${_zonepath}/usr/share/zoneinfo/$_a ;;
1339				*)	_a=${_a}/${resp}
1340					if [ -f ${_zonepath}/usr/share/zoneinfo/$_a ]; then
1341						break;
1342					fi
1343					;;
1344				esac
1345			done
1346			if [ -f ${_zonepath}/usr/share/zoneinfo/$_a ]; then
1347				TZ="$_a"
1348				echo "You have selected timezone \"$_a\"".
1349				break 2
1350			fi
1351			echo "'/usr/share/zoneinfo/$_a' is not a valid timezone on this system."
1352			;;
1353		esac
1354	done
1355}
1356
1357install_sets()
1358{
1359	local _yup
1360	_yup="FALSE"
1361
1362	# Ask the user which media to load the distribution from.
1363	# Ask the user if they want verbose extraction.  They might not want
1364	# it on, eg, SPARC frame buffer console.
1365	cat << \__install_sets_1
1366
1367It is now time to extract the installation sets onto the hard disk.
1368Make sure the sets are either on a local device (i.e. tape, CD-ROM) or on a
1369network server.
1370
1371Would you like to see each file listed during extraction (verbose) mode?
1372On some console hardware, such as serial consoles and Sun frame buffers,
1373this can extend the total extraction time.
1374__install_sets_1
1375	echo -n "Use verbose listing for extractions? [y] "
1376	getresp "y"
1377	case "$resp" in
1378	y*|Y*)
1379		verbose_flag=v
1380		;;
1381	*)
1382		echo "Not using verbose listing."
1383		verbose_flag=""
1384		;;
1385	esac
1386
1387	if [ -d ${Default_sets_dir:-/dev/null} ]; then
1388		if dir_has_sets $Default_sets_dir $THESETS; then
1389			local_sets_dir=$Default_sets_dir
1390		fi
1391	fi
1392	if [ -n "${local_sets_dir}" ]; then
1393		install_from_mounted_fs ${local_sets_dir}
1394		if [ -n "$_setsdone" ]; then
1395			_yup="TRUE"
1396		fi
1397	fi
1398
1399	# Go on prodding for alternate locations
1400	resp=""		# force at least one iteration
1401	while [ -z "${resp}" ]; do
1402		# If _yup is not FALSE, it means that we extracted sets above.
1403		# If that's the case, bypass the menu the first time.
1404		if [ "${_yup}" = "FALSE" ]; then
1405			echo -n	"Install from (f)tp, (t)ape, (C)D-ROM, (N)FS"
1406			echo -n " or local (d)isk? "
1407			getresp ""
1408			case "$resp" in
1409			d*|D*)
1410				install_disk
1411				;;
1412			f*|F*)
1413				install_ftp
1414				;;
1415			t*|T*)
1416				install_tape
1417				;;
1418			c*|C*)
1419				install_cdrom
1420				;;
1421			n*|N*)
1422				install_nfs
1423				;;
1424			*)
1425				echo "Invalid response: $resp"
1426				resp=""
1427				;;
1428			esac
1429		else
1430			_yup="FALSE"	# So we'll ask next time
1431		fi
1432
1433		# Give the user the opportunity to extract more sets. They
1434		# don't necessarily have to come from the same media.
1435		echo	""
1436		echo -n	"Extract more sets? [n] "
1437		getresp "n"
1438		case "$resp" in
1439		y*|Y*)
1440			# Force loop to repeat
1441			resp=""
1442			;;
1443
1444		*)
1445			;;
1446		esac
1447	done
1448}
1449
1450munge_fstab()
1451{
1452	local _fstab
1453	local _fstab_shadow
1454	local _dev
1455	local _mp
1456	local _fstype
1457	local _rest
1458
1459	# Now that the 'real' fstab is configured, we munge it into a 'shadow'
1460	# fstab which we'll use for mounting and unmounting all of the target
1461	# filesystems relative to /mnt.  Mount all filesystems.
1462	_fstab=$1
1463	_fstab_shadow=$2
1464	( while read _dev _mp _fstype _rest; do
1465		# Skip comment lines
1466		case "$_dev" in
1467			\#*)	continue;;
1468			*)	;;
1469		esac
1470		# and some filesystem types (like there are swap,kernfs,...)
1471		case "$_fstype" in
1472			ffs|ufs|nfs)	;;
1473			*)	continue;;
1474		esac
1475		if [ "$_mp" = "/" ]; then
1476			echo $_dev /mnt $_fstype $_rest
1477		else
1478			echo $_dev /mnt$_mp $_fstype $_rest
1479		fi
1480	    done ) < $_fstab > $_fstab_shadow
1481}
1482
1483mount_fs()
1484{
1485	# Must mount filesystems manually, one at a time, so we can make
1486	# sure the mount points exist.
1487	# $1 is a file in fstab format
1488	local _fstab
1489
1490	_fstab=$1
1491
1492	( while read line; do
1493		set -- $line
1494		_dev=$1
1495		_mp=$2
1496		_fstype=$3
1497		_opt=$4
1498
1499		# If not the root filesystem, make sure the mount
1500		# point is present.
1501		if [ "$_mp" != "/mnt" ]; then
1502			mkdir -p $_mp
1503		fi
1504
1505		# Mount the filesystem.  If the mount fails, exit
1506		# with an error condition to tell the outer
1507		# later to bail.
1508		if ! mount -v -t $_fstype -o async -o $_opt $_dev $_mp ; then
1509			# error message displayed by mount
1510			exit 1
1511		fi
1512	done ) < $_fstab
1513
1514	if [ "$?" != "0" ]; then
1515		cat << \__mount_filesystems_1
1516
1517FATAL ERROR:  Cannot mount filesystems.  Double-check your configuration
1518and restart the installation process.
1519__mount_filesystems_1
1520		exit
1521	fi
1522}
1523
1524unmount_fs()
1525{
1526	# Unmount all filesystems and check their integrity.
1527	# Usage: [-fast] <fstab file>
1528	local _fast
1529	local _fstab
1530	local _pid
1531
1532	if [ "$1" = "-fast" ]; then
1533		_fast=1
1534		_fstab=$2
1535	else
1536		_fast=0
1537		_fstab=$1
1538	fi
1539
1540	if ! [ -f "${_fstab}" ] || ! [ -s "${_fstab}" ]; then
1541		echo "fstab empty" > /dev/tty
1542		return
1543	fi
1544
1545	if [ $_fast = 0 ]; then
1546		echo -n	"Syncing disks..."
1547		_pid=$(twiddle)
1548		sync; sleep 4; sync; sleep 2; sync; sleep 2
1549		kill $_pid
1550		echo	"done."
1551	fi
1552
1553	(
1554		_devs=""
1555		_mps=""
1556		# maintain reverse order
1557		while read line; do
1558			set -- $line
1559			_devs="$1 ${_devs}"
1560			_mps="$2 ${_mps}"
1561		done
1562		echo -n "Unmounting filesystems... "
1563		for _mp in ${_mps}; do
1564			echo -n "${_mp} "
1565			umount ${_mp}
1566		done
1567		echo "Done."
1568
1569		if [ $_fast = 0 ]; then
1570			exit
1571		fi
1572		echo "Checking filesystem integrity..."
1573		for _dev in ${_devs}; do
1574			echo  "${_dev}"
1575			fsck -f ${_dev}
1576		done
1577		echo "Done."
1578	) < $_fstab
1579}
1580
1581check_fs()
1582{
1583	# Check filesystem integrity.
1584	# $1 is a file in fstab format
1585	local _fstab
1586
1587	_fstab=$1
1588
1589	(
1590		_devs=""
1591		_mps=""
1592		while read line; do
1593			set -- $line
1594			_devs="$1 ${_devs}"
1595			_mps="$2 ${_mps}"
1596		done
1597
1598		echo "Checking filesystem integrity..."
1599		for _dev in ${_devs}; do
1600			echo  "${_dev}"
1601			fsck -f ${_dev}
1602		done
1603		echo "Done."
1604	) < $_fstab
1605}
1606
1607mi_mount_kernfs() {
1608	# Make sure kernfs is mounted.
1609	if [ ! -d /kern ] || [ ! -e /kern/msgbuf ]; then
1610		mkdir /kern > /dev/null 2>&1
1611		/sbin/mount_kernfs /kern /kern
1612	fi
1613}
1614
1615mi_filter_msgbuf() {
1616	# Remove timestamps, sort.
1617	sed -e 's/^\[[0-9. ]*\] //' < /kern/msgbuf | sort -u
1618}
1619
1620mi_filter_dmesg() {
1621	# Remove timestamps, sort.
1622	dmesg | awk '{ h=$0; gsub("^[[0-9. ]*] ", "", h); print h; }' \
1623	    | sort -u
1624}
1625