xref: /netbsd/distrib/miniroot/install.sub (revision c4a72b64)
1#!/bin/sh
2#	$NetBSD: install.sub,v 1.43 2002/06/12 09:10:02 bouyer 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# 3. All advertising materials mentioning features or use of this software
19#    must display the following acknowledgement:
20#        This product includes software developed by the NetBSD
21#        Foundation, Inc. and its contributors.
22# 4. Neither the name of The NetBSD Foundation nor the names of its
23#    contributors may be used to endorse or promote products derived
24#    from this software without specific prior written permission.
25#
26# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36# POSSIBILITY OF SUCH DAMAGE.
37#
38
39#	NetBSD installation/upgrade script - common subroutines.
40
41ROOTDISK=""				# filled in below
42VERSION=				# filled in automatically (see list)
43export VERSION
44
45ALLSETS="base comp etc games man misc text"	# default install sets
46UPGRSETS="base comp games man misc text"	# default upgrade sets
47THESETS=					# one of the above
48
49local_sets_dir=""			# Path searched for sets by install_sets
50					# on the local filesystems
51
52# decide upon an editor
53if [ X$EDITOR = X ]; then
54	if [ -x /usr/bin/vi ]; then
55		EDITOR=vi
56	else
57		EDITOR=ed
58	fi
59fi
60
61getresp() {
62	read resp
63	if [ "X$resp" = "X" ]; then
64		resp=$1
65	fi
66}
67
68isin() {
69# test the first argument against the remaining ones, return succes on a match
70	_a=$1; shift
71	while [ $# != 0 ]; do
72		if [ "$_a" = "$1" ]; then return 0; fi
73		shift
74	done
75	return 1
76}
77
78rmel() {
79# remove first argument from list formed by the remaining arguments
80	local	_a
81
82	_a=$1; shift
83	while [ $# != 0 ]; do
84		if [ "$_a" != "$1" ]; then
85			echo "$1";
86		fi
87		shift
88	done
89}
90
91cutword () {
92# read a line of data, return Nth element.
93	local _a
94	local _n
95	local _oifs
96
97	# optional field separator
98	_oifs="$IFS"
99	case "$1" in
100		-t?*) IFS=${1#-t}; shift;;
101	esac
102
103	_n=$1
104	read _a; set -- $_a
105	IFS="$_oifs"
106	if [ "$1" = "" ]; then return; fi
107	eval echo \$$_n
108}
109
110cutlast () {
111# read a line of data, return last element. Equiv. of awk '{print $NF}'.
112	local _a
113	local _oifs
114
115	# optional field separator
116	_oifs="$IFS"
117	case "$1" in
118		-t?*) IFS=${1#-t}; shift;;
119	esac
120
121	read _a; set -- $_a
122	IFS="$_oifs"
123	if [ "$1" = "" ]; then return; fi
124	while [ "$#" -gt 10 ]; do shift 10; done
125	eval echo \$$#
126}
127
128firstchar () {
129# return first character of argument
130	local _a
131	_a=$1
132	while [ ${#_a} != 1 ]; do
133		_a=${_a%?}
134	done
135	echo $_a
136}
137
138basename () {
139	local _oifs
140	if [ "$1" = "" ]; then return; fi
141	_oifs="$IFS"
142	IFS="/"
143	set -- $1
144	IFS="$_oifs"
145	while [ "$#" -gt 10 ]; do shift 10; done
146	eval echo \$$#
147}
148
149dir_has_sets() {
150	# return true when the directory $1 contains a set for $2...$n
151	local _dir
152	local _file
153
154	_dir=$1; shift
155	for _file in $*
156	do
157		if [ -f $_dir/${_file}.tar.gz ]; then
158			return 0
159		fi
160		# Try for stupid msdos convention
161		if [ -f $_dir/${_file}.tgz ]; then
162			return 0
163		fi
164		# Try for uncompressed files
165		if [ -f $_dir/${_file}.tar ]; then
166			return 0
167		fi
168		# Try for split files
169		if [ -f $_dir/${_file}${VERSION}.aa ]; then
170			return 0
171		fi
172	done
173	return 1
174}
175
176twiddle() {
177# spin the propeller so we don't get bored
178	while : ; do
179		sleep 1; echo -n "/";
180		sleep 1; echo -n "-";
181		sleep 1; echo -n "\\";
182		sleep 1; echo -n "|";
183	done > /dev/tty & echo $!
184}
185
186get_localdir() {
187	# $1 is relative mountpoint
188	local _mp
189	local _dir
190
191	_mp=$1
192	_dir=
193	while : ; do
194	    if [ X$_mp != X ]; then
195		cat << __get_localdir_1
196Note: your filesystems are mounted under the temporary mount point \"$_mp\".
197The pathname you are requested to enter below should NOT include the \"$_mp\"
198prefix.
199__get_localdir_1
200	    fi
201	    echo -n "Enter the pathname where the sets are stored [$_dir] "
202	    getresp "$_dir"
203	    _dir=$resp
204
205	    # Allow break-out with empty response
206	    if [ -z "$_dir" ]; then
207		echo -n "Are you sure you don't want to set the pathname? [n] "
208		getresp "n"
209		case "$resp" in
210			y*|Y*)
211				break
212				;;
213			*)
214				continue
215				;;
216		esac
217	    fi
218
219	    if dir_has_sets "$_mp/$_dir" $THESETS
220	    then
221		local_sets_dir="$_mp/$_dir"
222		break
223	    else
224		cat << __get_localdir_2
225The directory \"$_mp/$_dir\" does not exist, or does not hold any of the
226upgrade sets.
227__get_localdir_2
228		echo -n "Re-enter pathname? [y] "
229		getresp "y"
230		case "$resp" in
231			y*|Y*)
232				;;
233			*)
234				local_sets_dir=""
235				break
236				;;
237		esac
238	    fi
239	done
240}
241
242getrootdisk() {
243	cat << \__getrootdisk_1
244
245The installation program needs to know which disk to consider
246the root disk.  Note the unit number may be different than
247the unit number you used in the standalone installation
248program.
249
250Available disks are:
251
252__getrootdisk_1
253	_DKDEVS=`md_get_diskdevs`
254	echo	"$_DKDEVS"
255	echo	""
256	echo -n	"Which disk is the root disk? "
257	getresp ""
258	if isin $resp $_DKDEVS ; then
259		ROOTDISK="$resp"
260	else
261		echo ""
262		echo "The disk $resp does not exist."
263		ROOTDISK=""
264	fi
265}
266
267labelmoredisks() {
268	cat << \__labelmoredisks_1
269
270You may label the following disks:
271
272__labelmoredisks_1
273	echo "$_DKDEVS"
274	echo	""
275	echo -n	"Label which disk? [done] "
276	getresp "done"
277	case "$resp" in
278		"done")
279			;;
280
281		*)
282			if isin $resp $_DKDEVS ; then
283				md_labeldisk $resp
284			else
285				echo ""
286				echo "The disk $resp does not exist."
287			fi
288			;;
289	esac
290}
291
292addhostent() {
293	# $1 - IP address
294	# $2 - symbolic name
295
296	local fqdn
297
298	# Create an entry in the hosts table.  If no host table
299	# exists, create one.  If the IP address already exists,
300	# replace it's entry.
301	if [ ! -f /tmp/hosts ]; then
302		echo "127.0.0.1 localhost" > /tmp/hosts
303	fi
304
305	sed "/^$1 /d" < /tmp/hosts > /tmp/hosts.new
306	mv /tmp/hosts.new /tmp/hosts
307
308	if [ X${FQDN} != X ]; then
309		fqdn=$2.$FQDN
310	fi
311	echo "$1 $2 $fqdn" >> /tmp/hosts
312}
313
314addifconfig() {
315	# $1 - interface name
316	# $2 - interface symbolic name
317	# $3 - interface IP address
318	# $4 - interface netmask
319	# $5 - (optional) interface link-layer medium, preceded by "media ", else ""
320	# $6 - (optional) interface link-layer directives
321	local _m
322
323	# Create a ifconfig.* file for the interface.
324	echo "inet $2 netmask $4 $5 $6" > /tmp/ifconfig.$1
325
326	addhostent $3 $2
327}
328
329configurenetwork() {
330	local _ifsdone
331	local _ifs
332
333#	_IFS=`md_get_ifdevs`
334	_IFS=`ifconfig -l | sed '
335		s/lo0//
336		s/ppp[0-9]//g
337		s/sl[0-9]//g
338		s/tun[0-9]//g'`
339
340	_ifsdone=""
341	resp=""		# force at least one iteration
342	while [ "X${resp}" != X"done" ]; do
343	cat << \__configurenetwork_1
344
345You may configure the following network interfaces (the interfaces
346marked with [X] have been successfully configured):
347
348__configurenetwork_1
349
350		for _ifs in $_IFS; do
351			if isin $_ifs $_ifsdone ; then
352				echo -n "[X] "
353			else
354				echo -n "    "
355			fi
356			echo $_ifs
357		done
358		echo	""
359		echo -n	"Configure which interface? [done] "
360		getresp "done"
361		case "$resp" in
362		"done")
363			;;
364		*)
365			_ifs=$resp
366			if isin $_ifs $_IFS ; then
367				if configure_ifs $_ifs ; then
368					_ifsdone="$_ifs $_ifsdone"
369				fi
370			else
371				echo "Invalid response: \"$resp\" is not in list"
372			fi
373			;;
374		esac
375	done
376}
377
378configure_ifs() {
379
380	local _up
381	local _interface_name
382	local _interface_ip
383	local _interface_mask
384	local _interface_symname
385	local _interface_extra
386	local _interface_mediumtype
387	local _interface_supported_media
388	local _m
389	local _t
390
391	_interface_name=$1
392	_up=DOWN
393	if isin $_interface_name `ifconfig -l -u` ; then
394		_up=UP
395	fi
396
397	_interface_supported_media=`ifconfig -m $_interface_name | sed -n '
398		/^[ 	]*media autoselect/d
399		4,$s/[ 	]*media //p'`
400
401	# get current "media" "ip" and "netmask" ("broadcast")
402	_t=`ifconfig $_interface_name | sed -n '
403		s/^[ 	]*media: [^ 	]* \([^ ][^ ]*\).*/\1/p'`
404
405	if [ "$_t" != "manual" -a "$_t" != "media:" -a "$_t" != "autoselect" ];
406	then
407		_interface_mediumtype=$1
408	fi
409
410	set -- `ifconfig $_interface_name | sed -n '
411		/^[ 	]*inet/{
412		s/inet//
413		s/--> [0-9.][0-9.]*//
414		s/netmask//
415		s/broadcast//
416		p;}'`
417
418	_interface_ip=$1
419	_interface_mask=$2
420
421	# Get IP address
422	resp=""		# force one iteration
423	while [ "X${resp}" = X"" ]; do
424		echo -n "IP address? [$_interface_ip] "
425		getresp "$_interface_ip"
426		_interface_ip=$resp
427	done
428
429	# Get symbolic name
430	resp=""		# force one iteration
431	while [ "X${resp}" = X"" ]; do
432		echo -n "Symbolic (host) name? "
433		getresp ""
434		_interface_symname=$resp
435	done
436
437	# Get netmask
438	resp=""		# force one iteration
439	while [ "X${resp}" = X"" ]; do
440		echo -n "Netmask? [$_interface_mask] "
441		getresp "$_interface_mask"
442		_interface_mask=$resp
443	done
444
445	echo "Your network interface might require explicit selection"
446	echo "of the type of network medium attached. Supported media:"
447	echo "$_interface_supported_media"
448	echo -n "Additional media type arguments (none)? [$_interface_mediumtype] "
449	getresp "$_interface_mediumtype"
450	_m=""
451	if [ "X${resp}" != X"" -a "X${resp}" != Xnone ]; then
452		_interface_mediumtype=$resp
453		_m="media ${resp}"
454	fi
455
456
457	echo "Your network interface might require additional link-layer"
458	echo "directives (like \`link0'). If this is the case you can enter"
459	echo "these at the next prompt."
460	echo ""
461	echo -n "Additional link-layer arguments (none)? [$_interface_extra] "
462	getresp "$_interface_extra"
463	if [ "X${resp}" != X"" -a "X${resp}" != Xnone ]; then
464		_interface_extra=$resp
465	fi
466
467	# Configure the interface.  If it
468	# succeeds, add it to the permanent
469	# network configuration info.
470	if [ $_up != "UP" ]; then
471		ifconfig ${_interface_name} down
472		if ifconfig ${_interface_name} inet \
473		    ${_interface_ip} \
474		    netmask ${_interface_mask} \
475		    ${_interface_extra} ${_m} up ; then
476			addifconfig \
477			    "${_interface_name}" \
478			    "${_interface_symname}" \
479			    "${_interface_ip}" \
480			    "${_interface_mask}" \
481			    "${_m}" \
482			    "${_interface_extra}"
483			return 0
484		fi
485	else
486		echo "Interface ${_interface_name} is already active."
487		echo "Just saving configuration on new root filesystem."
488		addifconfig \
489		    "${_interface_name}" \
490		    "${_interface_symname}" \
491		    "${_interface_ip}" \
492		    "${_interface_mask}" \
493		    "${_m}" \
494		    "${_interface_extra}"
495	fi
496	return 1
497}
498
499# Much of this is gratuitously stolen from /etc/rc.d/network.
500enable_network() {
501
502	# Set up the hostname.
503	if [ -f /mnt/etc/myname ]; then
504		hostname=`cat /mnt/etc/myname`
505	elif [ -f /mnt/etc/rc.conf ];then
506		hostname=`sh -c '. /mnt/etc/rc.conf ; echo $hostname'`
507	else
508		echo "ERROR: no /etc/myname!"
509		return 1
510	fi
511	if [ -z "$hostname" ];then
512		echo "ERROR: hostname not set in /etc/myname or /etc/rc.conf!"
513		return 1
514	fi
515	hostname $hostname
516
517	# configure all the interfaces which we know about.
518if [ -f /mnt/etc/rc.conf ]; then
519(
520	# assume network interface configuration style 1.2D and up
521	if [ -f /mnt/etc/defaults/rc.conf ]; then
522		. /mnt/etc/defaults/rc.conf
523	fi
524	. /mnt/etc/rc.conf
525
526	if [ "$net_interfaces" != NO ]; then
527		if [ "$auto_ifconfig" = YES ]; then
528			tmp="`ifconfig -l`"
529		else
530			tmp="$net_interfaces"
531		fi
532		echo -n "configuring network interfaces:"
533		for i in $tmp; do
534			eval `echo 'args=$ifconfig_'$i`
535			if [ ! -z "$args" ]; then
536				echo -n " $i"
537				ifconfig $i $args
538			elif [ -f /mnt/etc/ifconfig.$i ]; then
539				echo -n " $i"
540				(while read args; do
541					ifconfig $i $args
542				done) < /mnt/etc/ifconfig.$i
543			elif [ "$auto_ifconfig" != YES ]; then
544				echo
545				echo -n "/mnt/etc/ifconfig.$i missing"
546				echo -n "& ifconfig_$i not set"
547				echo "; interface $i can't be configured"
548			fi
549		done
550		echo "."
551	fi
552)
553else
554(
555	tmp="$IFS"
556	IFS="$IFS."
557	set -- `echo /mnt/etc/hostname*`
558	IFS=$tmp
559	unset tmp
560
561	while [ $# -ge 2 ] ; do
562		shift		# get rid of "hostname"
563		(
564			read af name mask bcaddr extras
565			read dt dtaddr
566
567			if [ ! -n "$name" ]; then
568		    echo "/etc/hostname.$1: invalid network configuration file"
569				exit
570			fi
571
572			cmd="ifconfig $1 $af $name "
573			if [ "${dt}" = "dest" ]; then cmd="$cmd $dtaddr"; fi
574			if [ -n "$mask" ]; then cmd="$cmd netmask $mask"; fi
575			if [ -n "$bcaddr" -a "X$bcaddr" != "XNONE" ]; then
576				cmd="$cmd broadcast $bcaddr";
577			fi
578			cmd="$cmd $extras"
579
580			$cmd
581		) < /mnt/etc/hostname.$1
582		shift
583	done
584)
585fi
586
587	# set the address for the loopback interface
588	ifconfig lo0 inet localhost
589
590	# use loopback, not the wire
591	route add $hostname localhost
592
593	# /etc/mygate, if it exists, contains the name of my gateway host
594	# that name must be in /etc/hosts.
595	if [ -f /mnt/etc/mygate ]; then
596		route delete default > /dev/null 2>&1
597		route add default `cat /mnt/etc/mygate`
598	fi
599
600	# enable the resolver, if appropriate.
601	if [ -f /mnt/etc/resolv.conf ]; then
602		_resolver_enabled="TRUE"
603		cp /mnt/etc/resolv.conf /tmp/resolv.conf.shadow
604	fi
605
606	# Display results...
607	echo	"Network interface configuration:"
608	ifconfig -a
609
610	echo	""
611
612	if [ "X${_resolver_enabled}" = X"TRUE" ]; then
613		netstat -r
614		echo	""
615		echo	"Resolver enabled."
616	else
617		netstat -rn
618		echo	""
619		echo	"Resolver not enabled."
620	fi
621
622	return 0
623}
624
625install_ftp() {
626	local	_f
627	local	_sets
628	local	_next
629
630	# Build a script to extract valid files from a list
631	# of filenames on stdin.
632	# XXX : Can we use this on more places? Leo.
633
634	echo "#!/bin/sh" > /tmp/fname_filter.sh
635	echo "while read line; do"	>> /tmp/fname_filter.sh
636	echo "    case \$line in"	>> /tmp/fname_filter.sh
637	for _f in $THESETS; do
638		echo "    $_f.tar.gz|$_f.tgz|$_f.tar|$_f.${VERSION}.aa)" \
639					>> /tmp/fname_filter.sh
640		echo '        echo -n "$line ";;' \
641					>> /tmp/fname_filter.sh
642	done
643	echo "        *) ;;"		>> /tmp/fname_filter.sh
644	echo "    esac"			>> /tmp/fname_filter.sh
645	echo "done"			>> /tmp/fname_filter.sh
646
647	# Get several parameters from the user, and create
648	# a shell script that directs the appropriate
649	# commands into ftp.
650	cat << \__install_ftp_1
651
652This is an automated ftp-based installation process.  You will be asked
653several questions.  The correct set of commands will be placed in a script
654that will be fed to ftp(1).
655
656__install_ftp_1
657	# Get server IP address
658	resp=""		# force one iteration
659	while [ "X${resp}" = X"" ]; do
660		echo -n "Server IP? [${_ftp_server_ip}] "
661		getresp "${_ftp_server_ip}"
662		_ftp_server_ip=$resp
663	done
664
665	# Get login name
666	resp=""		# force one iteration
667	while [ "X${resp}" = X"" ]; do
668		echo -n "Login? [${_ftp_server_login}] "
669		getresp "${_ftp_server_login}"
670		_ftp_server_login=$resp
671	done
672
673	# Get password
674	resp=""		# force one iteration
675	while [ "X${resp}" = X"" ]; do
676		echo -n "Password? "
677		stty -echo
678		getresp ""
679		echo ""
680		stty echo
681		_ftp_server_password=$resp
682	done
683
684	cat << \__install_ftp_2
685
686You will be asked to enter the name of the directory that contains the
687installation sets. When you enter a '?' you will see a listing of the
688current directory on the server.
689__install_ftp_2
690	_sets=""
691	while [ -z "$_sets" ]
692	do
693		resp=""		# force one iteration
694		while [ "X${resp}" = X"" ]; do
695			echo -n "Server directory? [${_ftp_server_dir}] "
696		    getresp "${_ftp_server_dir}"
697		    if [ "X$resp" = 'X?' -a -z "$_ftp_server_dir" ]; then
698			resp=""
699		    fi
700		done
701		if [ $resp != '?' ]; then
702			_ftp_server_dir=$resp
703		fi
704
705		# Build the basics of an ftp-script...
706		echo "#!/bin/sh" > /tmp/ftp-script.sh
707		echo "cd /mnt" >> /tmp/ftp-script.sh
708		echo "ftp -e -i -n $_ftp_server_ip << \__end_commands" >> \
709		    /tmp/ftp-script.sh
710		echo "user $_ftp_server_login $_ftp_server_password" >> \
711		    /tmp/ftp-script.sh
712		echo "bin" >> /tmp/ftp-script.sh
713		echo "cd $_ftp_server_dir" >> /tmp/ftp-script.sh
714
715		# Make a copy of this script that lists the directory
716		# contents, and use that to determine the files to get.
717		cat /tmp/ftp-script.sh	>  /tmp/ftp-dir.sh
718		echo "nlist"		>> /tmp/ftp-dir.sh
719		echo "quit"		>> /tmp/ftp-dir.sh
720		echo "__end_commands"	>> /tmp/ftp-dir.sh
721
722		if [ $resp = '?' ]; then
723			sh /tmp/ftp-dir.sh
724		else
725			_sets=`sh /tmp/ftp-dir.sh | sh /tmp/fname_filter.sh`
726		fi
727	done
728	rm -f /tmp/ftp-dir.sh /tmp/fname_filter.sh
729
730	while : ; do
731		echo "The following sets are available for extraction:"
732		echo "(marked sets are already on the extraction list)"
733		echo ""
734
735		_next=""
736		for _f in $_sets ; do
737			if isin $_f $_setsdone; then
738				echo -n "[X] "
739				_next=""
740			else
741				echo -n "    "
742				if [ -z "$_next" ]; then _next=$_f; fi
743			fi
744			echo $_f
745		done
746		echo ""
747
748		# Get name of the file and add extraction command
749		# to the ftp-script.
750		if [ "X$_next" = "X" ]; then resp=n; else resp=y; fi
751		echo -n "Continue to add filenames [$resp]? "
752		getresp "$resp"
753		if [ "$resp" = "n" ]; then
754			break
755		fi
756
757		echo -n "File name [$_next]? "
758		getresp "$_next"
759		if isin $resp $_sets; then
760			echo "get $resp |\"pax -zr${verbose_flag}pe\"" >> \
761					/tmp/ftp-script.sh
762			_setsdone="$resp $_setsdone"
763		else
764			echo "You entered an invalid filename."
765			echo ""
766		fi
767	done
768
769	echo "quit" >> /tmp/ftp-script.sh
770	echo "__end_commands" >> /tmp/ftp-script.sh
771
772	sh /tmp/ftp-script.sh
773	rm -f /tmp/ftp-script.sh
774	echo "Extraction complete."
775}
776
777install_from_mounted_fs() {
778	# $1 - directory containing installation sets
779	local _filename
780	local _sets
781	local _next
782	local _all
783	local _f
784	local _dirname
785
786	_dirname=$1
787	_sets=""
788
789	if ! dir_has_sets ${_dirname} $THESETS
790	then
791
792		echo ""
793		echo "The directory at the mount point, \"${_dirname}\", contains: "
794		echo ""
795		ls -F ${_dirname}
796		echo ""
797		echo    "Enter the subdirectory relative to the mountpoint, that"
798		echo -n "contains the savesets: [try this directory] "
799		getresp ""
800		if [ "X${resp}" != "X" ]; then
801			_dirname=${_dirname}/$resp
802		fi
803
804		while ! dir_has_sets ${_dirname} $THESETS; do
805			echo ""
806			echo -n "There are no NetBSD install sets available in "
807			echo "\"${_dirname}\"."
808			echo "\"${_dirname}\" contains: "
809			echo ""
810			ls -F ${_dirname}
811			echo ""
812			echo -n "Enter subdirectory: [try other install media] "
813			getresp ""
814			if [ "X${resp}" = "X" ]; then
815				return
816			fi
817			if [ ! -d ${_dirname}/${resp} ]; then
818				echo "\"${resp}\" is no directory; try again."
819			else
820				_dirname=${_dirname}/$resp
821			fi
822		done
823	fi
824
825	for _f in $THESETS ; do
826		if [ -f ${_dirname}/${_f}.tar.gz ]; then
827			_sets="$_sets ${_f}.tar.gz"
828		elif [ -f ${_dirname}/${_f}.tgz ]; then
829			_sets="$_sets ${_f}.tgz"
830		elif [ -f ${_dirname}/${_f}.tar ]; then
831			_sets="$_sets ${_f}.tar"
832		elif [ -f ${_dirname}/${_f}${VERSION}.aa ]; then
833			_sets="$_sets ${_f}${VERSION}"
834		fi
835	done
836
837	while : ; do
838		echo "The following sets are available for extraction:"
839		echo "(marked sets have already been extracted)"
840		echo ""
841
842		_next=""
843		_all=""
844		for _f in $_sets ; do
845			if isin $_f $_setsdone; then
846				echo -n "[X] "
847				_next=""
848			else
849				echo -n "    "
850				if [ -z "$_next" ]; then
851					_next=$_f;
852				fi
853				_all="$_all $_f"
854			fi
855			echo $_f
856		done
857		echo ""
858
859		# Get the name of the file.
860		if [ "X$_next" = "X" ]; then
861			resp=n
862		else
863			resp=y
864		fi
865		echo -n "Continue extraction [$resp]?"
866		getresp "$resp"
867		if [ "$resp" = "n" ]; then
868			break
869		fi
870
871		echo -n "File name(s) (or "all") [$_next]? "
872		getresp "$_next"
873		if [ "x$resp" = xall ]; then
874			resp="$_all"
875		fi
876
877		for _f in $resp; do
878			_filename="/${_dirname}/$_f"
879
880			# Ensure file exists
881			if [ ! -f $_filename ]; then
882				if [ -f ${_filename}.aa ]; then
883					_filename=${_filename}.\?\?
884				else
885			 echo "File $_filename does not exist.  Check to make"
886			 echo "sure you entered the information properly."
887			 continue 2
888				fi
889			fi
890
891			# Extract file
892			echo "Extracting the $_f set:"
893			case "$_filename" in
894			*.tar)
895				(cd /mnt; pax -r${verbose_flag}pe < $_filename)
896				;;
897			*)
898				cat $_filename | \
899					(cd /mnt; pax -zr${verbose_flag}pe)
900				;;
901			esac
902			echo "Extraction complete."
903			_setsdone="$_f $_setsdone"
904		done
905
906	done
907}
908
909install_cdrom() {
910	local _drive
911	local _partition_range
912	local _partition
913	local _fstype
914	local _directory
915
916	# Get the cdrom device info
917	cat << \__install_cdrom_1
918
919The following CD-ROM devices are installed on your system; please select
920the CD-ROM device containing the partition with the installation sets:
921
922__install_cdrom_1
923	_CDDEVS=`md_get_cddevs`
924	echo    "$_CDDEVS"
925	echo	""
926	echo -n	"Which is the CD-ROM with the installation media? [abort] "
927	getresp "abort"
928	case "$resp" in
929		abort)
930			echo "Aborting."
931			return
932			;;
933
934		*)
935			if isin $resp $_CDDEVS ; then
936				_drive=$resp
937			else
938				echo ""
939				echo "The CD-ROM $resp does not exist."
940				echo "Aborting."
941				return
942			fi
943			;;
944	esac
945
946	# Get partition
947	_partition_range=`md_get_partition_range`
948	resp=""		# force one iteration
949	while [ "X${resp}" = X"" ]; do
950		echo -n "Partition? [a] "
951		getresp "a"
952		case "$resp" in
953			$_partition_range)
954				_partition=$resp
955				;;
956
957			*)
958				echo "Invalid response: $resp"
959				resp=""		# force loop to repeat
960				;;
961		esac
962	done
963
964	# Ask for filesystem type
965	cat << \__install_cdrom_2
966
967There are two CD-ROM filesystem types currently supported by this program:
968	1) ISO-9660 (cd9660)
969	2) Berkeley Fast Filesystem (ffs)
970
971__install_cdrom_2
972	resp=""		# force one iteration
973	while [ "X${resp}" = X"" ]; do
974		echo -n "Which filesystem type? [cd9660] "
975		getresp "cd9660"
976		case "$resp" in
977			cd9660|ffs)
978				_fstype=$resp
979				;;
980
981			*)
982				echo "Invalid response: $resp"
983				resp=""		# force loop to repeat
984				;;
985		esac
986	done
987
988	# Mount the CD-ROM
989	if ! mount -t ${_fstype} -o ro \
990	    /dev/${_drive}${_partition} /mnt2 ; then
991		echo "Cannot mount CD-ROM drive.  Aborting."
992		return
993	fi
994
995	install_from_mounted_fs /mnt2
996	umount -f /mnt2 > /dev/null 2>&1
997}
998
999mount_a_disk() {
1000	# Mount a disk on /mnt2. The set of disk devices to choose from
1001	# is $_DKDEVS.
1002	# returns 0 on failure.
1003
1004	local _drive
1005	local _partition_range
1006	local _partition
1007	local _fstype
1008	local _fsopts
1009	local _directory
1010	local _md_fstype
1011	local _md_fsopts
1012
1013	getresp "abort"
1014	case "$resp" in
1015		abort)
1016			echo "Aborting."
1017			return 0
1018			;;
1019
1020		*)
1021			if isin $resp $_DKDEVS ; then
1022				_drive=$resp
1023			else
1024				echo ""
1025				echo "The disk $resp does not exist."
1026				echo "Aborting."
1027				return 0
1028			fi
1029			;;
1030	esac
1031
1032	# Get partition
1033	_partition_range=`md_get_partition_range`
1034	resp=""		# force one iteration
1035	while [ "X${resp}" = X"" ]; do
1036		echo -n "Partition? [d] "
1037		getresp "d"
1038		case "$resp" in
1039			$_partition_range)
1040				_partition=$resp
1041				;;
1042
1043			*)
1044				echo "Invalid response: $resp"
1045				resp=""		# force loop to repeat
1046				;;
1047		esac
1048	done
1049
1050	# Ask for filesystem type
1051	cat << \__mount_a_disk_2
1052
1053The following filesystem types are supported:
1054	1) ffs
1055__mount_a_disk_2
1056	_md_fstype=`md_native_fstype`
1057	_md_fsopts=`md_native_fsopts`
1058	if [ ! -z "$_md_fstype" ]; then
1059		echo "	2) $_md_fstype"
1060	else
1061		_md_fstype="_undefined_"
1062	fi
1063	resp=""		# force one iteration
1064	while [ "X${resp}" = X"" ]; do
1065		echo -n "Which filesystem type? [ffs] "
1066		getresp "ffs"
1067		case "$resp" in
1068			ffs)
1069				_fstype=$resp
1070				_fsopts="ro"
1071				;;
1072			$_md_fstype)
1073				_fstype=$resp
1074				_fsopts=$_md_fsopts
1075				;;
1076			*)
1077				echo "Invalid response: $resp"
1078				resp=""		# force loop to repeat
1079				;;
1080		esac
1081	done
1082
1083	# Mount the disk
1084	if ! mount -t ${_fstype} -o $_fsopts \
1085	    /dev/${_drive}${_partition} /mnt2 ; then
1086		echo "Cannot mount disk.  Aborting."
1087		return 0
1088	fi
1089	return 1
1090}
1091
1092install_disk() {
1093	local _directory
1094
1095	cat << \__install_disk_1
1096
1097Ok, lets install from a disk.  The file-system the install sets on may
1098already mounted, or we might have to mount the filesystem to get to it.
1099
1100__install_disk_1
1101
1102	echo -n "Is the file-system with the install sets already mounted? [n] "
1103	getresp "n"
1104	case $resp in
1105	y*|Y*)
1106		echo "What mount point are the sets located in? [] "
1107		getresp ""
1108		if [ -d "$resp" ]; then
1109			install_from_mounted_fs $resp
1110		else
1111			echo "$resp: Not a directory, aborting..."
1112		fi
1113		return
1114		;;
1115	*)
1116		;;
1117	esac
1118
1119	cat << \__install_disk_2
1120
1121The following disk devices are installed on your system; please select
1122the disk device containing the partition with the installation sets:
1123
1124__install_disk_2
1125	_DKDEVS=`md_get_diskdevs`
1126	echo    "$_DKDEVS"
1127	echo	""
1128	echo -n	"Which is the disk with the installation sets? [abort] "
1129
1130	if mount_a_disk ; then
1131		return
1132	fi
1133
1134	install_from_mounted_fs /mnt2
1135	umount -f /mnt2 > /dev/null 2>&1
1136}
1137
1138install_nfs() {
1139	# Get the IP address of the server
1140	resp=""		# force one iteration
1141	while [ "X${resp}" = X"" ]; do
1142		echo -n "Server IP address? [${_nfs_server_ip}] "
1143		getresp "${_nfs_server_ip}"
1144	done
1145	_nfs_server_ip=$resp
1146
1147	# Get server path to mount
1148	resp=""		# force one iteration
1149	while [ "X${resp}" = X"" ]; do
1150		echo -n "Filesystem on server to mount? [${_nfs_server_path}] "
1151		getresp "${_nfs_server_path}"
1152	done
1153	_nfs_server_path=$resp
1154
1155	# Determine use of TCP
1156	echo -n "Use TCP transport (only works with capable NFS server)? [n] "
1157	getresp "n"
1158	case "$resp" in
1159		y*|Y*)
1160			_nfs_tcp="-T"
1161			;;
1162
1163		*)
1164			echo -n "Use small NFS transfers (needed when server "
1165			echo "or client"
1166			echo -n "has a slow network card)? [n] "
1167			getresp "n"
1168			case "$resp" in
1169			y*|Y*)
1170				_nfs_tcp="-r 1024 -w 1024"
1171				;;
1172
1173			*)
1174				_nfs_tcp=""
1175				;;
1176			esac
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 [ "X${resp}" = X"" ]; 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 [ "X${resp}" = X"" ]; 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 [ "X${_nskip}" != X"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 [ "X${resp}" = X"" ]; 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 immediatly.
1291	#
1292	if [ ! -e /usr/share/zoneinfo -a ! -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 [ X$TZ = X ]; 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 [ "X$local_sets_dir" != "X" ]; then
1393		install_from_mounted_fs ${local_sets_dir}
1394		if [ X"$_setsdone" != X ]; 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 [ X"${resp}" = X ]; 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 [ X"$_yup" = X"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 [ "X{$_mp}" != X"/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 displated by mount
1510			exit 1
1511		fi
1512	done ) < $_fstab
1513
1514	if [ "X${?}" != X"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 -a -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 "Umounting 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