xref: /netbsd-src/distrib/miniroot/install.sub (revision 8a8f936f250a330d54f8a24ed0e92aadf9743a7b)
1#!/bin/sh
2#	$NetBSD: install.sub,v 1.39 2000/11/22 21:04:46 abs 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 split files
165		if [ -f $_dir/${_file}${VERSION}.aa ]; then
166			return 0
167		fi
168	done
169	return 1
170}
171
172twiddle() {
173# spin the propeller so we don't get bored
174	while : ; do
175		sleep 1; echo -n "/";
176		sleep 1; echo -n "-";
177		sleep 1; echo -n "\\";
178		sleep 1; echo -n "|";
179	done > /dev/tty & echo $!
180}
181
182get_localdir() {
183	# $1 is relative mountpoint
184	local _mp
185	local _dir
186
187	_mp=$1
188	_dir=
189	while : ; do
190	    if [ X$_mp != X ]; then
191		cat << __get_localdir_1
192Note: your filesystems are mounted under the temporary mount point \"$_mp\".
193The pathname you are requested to enter below should NOT include the \"$_mp\"
194prefix.
195__get_localdir_1
196	    fi
197	    echo -n "Enter the pathname where the sets are stored [$_dir] "
198	    getresp "$_dir"
199	    _dir=$resp
200
201	    # Allow break-out with empty response
202	    if [ -z "$_dir" ]; then
203		echo -n "Are you sure you don't want to set the pathname? [n] "
204		getresp "n"
205		case "$resp" in
206			y*|Y*)
207				break
208				;;
209			*)
210				continue
211				;;
212		esac
213	    fi
214
215	    if dir_has_sets "$_mp/$_dir" $THESETS
216	    then
217		local_sets_dir="$_mp/$_dir"
218		break
219	    else
220		cat << __get_localdir_2
221The directory \"$_mp/$_dir\" does not exist, or does not hold any of the
222upgrade sets.
223__get_localdir_2
224		echo -n "Re-enter pathname? [y] "
225		getresp "y"
226		case "$resp" in
227			y*|Y*)
228				;;
229			*)
230				local_sets_dir=""
231				break
232				;;
233		esac
234	    fi
235	done
236}
237
238getrootdisk() {
239	cat << \__getrootdisk_1
240
241The installation program needs to know which disk to consider
242the root disk.  Note the unit number may be different than
243the unit number you used in the standalone installation
244program.
245
246Available disks are:
247
248__getrootdisk_1
249	_DKDEVS=`md_get_diskdevs`
250	echo	"$_DKDEVS"
251	echo	""
252	echo -n	"Which disk is the root disk? "
253	getresp ""
254	if isin $resp $_DKDEVS ; then
255		ROOTDISK="$resp"
256	else
257		echo ""
258		echo "The disk $resp does not exist."
259		ROOTDISK=""
260	fi
261}
262
263labelmoredisks() {
264	cat << \__labelmoredisks_1
265
266You may label the following disks:
267
268__labelmoredisks_1
269	echo "$_DKDEVS"
270	echo	""
271	echo -n	"Label which disk? [done] "
272	getresp "done"
273	case "$resp" in
274		"done")
275			;;
276
277		*)
278			if isin $resp $_DKDEVS ; then
279				md_labeldisk $resp
280			else
281				echo ""
282				echo "The disk $resp does not exist."
283			fi
284			;;
285	esac
286}
287
288addhostent() {
289	# $1 - IP address
290	# $2 - symbolic name
291
292	local fqdn
293
294	# Create an entry in the hosts table.  If no host table
295	# exists, create one.  If the IP address already exists,
296	# replace it's entry.
297	if [ ! -f /tmp/hosts ]; then
298		echo "127.0.0.1 localhost" > /tmp/hosts
299	fi
300
301	sed "/^$1 /d" < /tmp/hosts > /tmp/hosts.new
302	mv /tmp/hosts.new /tmp/hosts
303
304	if [ X${FQDN} != X ]; then
305		fqdn=$2.$FQDN
306	fi
307	echo "$1 $2 $fqdn" >> /tmp/hosts
308}
309
310addifconfig() {
311	# $1 - interface name
312	# $2 - interface symbolic name
313	# $3 - interface IP address
314	# $4 - interface netmask
315	# $5 - (optional) interface link-layer medium, preceded by "media ", else ""
316	# $6 - (optional) interface link-layer directives
317	local _m
318
319	# Create a ifconfig.* file for the interface.
320	echo "inet $2 netmask $4 $5 $6" > /tmp/ifconfig.$1
321
322	addhostent $3 $2
323}
324
325configurenetwork() {
326	local _ifsdone
327	local _ifs
328
329#	_IFS=`md_get_ifdevs`
330	_IFS=`ifconfig -l | sed '
331		s/lo0//
332		s/ppp[0-9]//g
333		s/sl[0-9]//g
334		s/tun[0-9]//g'`
335
336	_ifsdone=""
337	resp=""		# force at least one iteration
338	while [ "X${resp}" != X"done" ]; do
339	cat << \__configurenetwork_1
340
341You may configure the following network interfaces (the interfaces
342marked with [X] have been succesfully configured):
343
344__configurenetwork_1
345
346		for _ifs in $_IFS; do
347			if isin $_ifs $_ifsdone ; then
348				echo -n "[X] "
349			else
350				echo -n "    "
351			fi
352			echo $_ifs
353		done
354		echo	""
355		echo -n	"Configure which interface? [done] "
356		getresp "done"
357		case "$resp" in
358		"done")
359			;;
360		*)
361			_ifs=$resp
362			if isin $_ifs $_IFS ; then
363				if configure_ifs $_ifs ; then
364					_ifsdone="$_ifs $_ifsdone"
365				fi
366			else
367				echo "Invalid response: \"$resp\" is not in list"
368			fi
369			;;
370		esac
371	done
372}
373
374configure_ifs() {
375
376	local _up
377	local _interface_name
378	local _interface_ip
379	local _interface_mask
380	local _interface_symname
381	local _interface_extra
382	local _interface_mediumtype
383	local _interface_supported_media
384	local _m
385	local _t
386
387	_interface_name=$1
388	_up=DOWN
389	if isin $_interface_name `ifconfig -l -u` ; then
390		_up=UP
391	fi
392
393	_interface_supported_media=`ifconfig -m $_interface_name | sed -n '
394		/^[ 	]*media autoselect/d
395		4,$s/[ 	]*media //p'`
396
397	# get current "media" "ip" and "netmask" ("broadcast")
398	_t=`ifconfig $_interface_name | sed -n '
399		s/^[ 	]*media: [^ 	]* \([^ ][^ ]*\).*/\1/p'`
400
401	if [ "$_t" != "manual" -a "$_t" != "media:" -a "$_t" != "autoselect" ];
402	then
403		_interface_mediumtype=$1
404	fi
405
406	set -- `ifconfig $_interface_name | sed -n '
407		/^[ 	]*inet/{
408		s/inet//
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 [ "X${resp}" = X"" ]; 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 [ "X${resp}" = X"" ]; 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 [ "X${resp}" = X"" ]; 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 [ "X${resp}" != X"" -a "X${resp}" != Xnone ]; 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 [ "X${resp}" != X"" -a "X${resp}" != Xnone ]; 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	. /mnt/etc/rc.conf
518
519	if [ "$net_interfaces" != NO ]; then
520		if [ "$auto_ifconfig" = YES ]; then
521			tmp="`ifconfig -l`"
522		else
523			tmp="$net_interfaces"
524		fi
525		echo -n "configuring network interfaces:"
526		for i in $tmp; do
527			eval `echo 'args=$ifconfig_'$i`
528			if [ ! -z "$args" ]; then
529				echo -n " $i"
530				ifconfig $i $args
531			elif [ -f /mnt/etc/ifconfig.$i ]; then
532				echo -n " $i"
533				(while read args; do
534					ifconfig $i $args
535				done) < /mnt/etc/ifconfig.$i
536			elif [ "$auto_ifconfig" != YES ]; then
537				echo
538				echo -n "/mnt/etc/ifconfig.$i missing"
539				echo -n "& ifconfig_$i not set"
540				echo "; interface $i can't be configured"
541			fi
542		done
543		echo "."
544	fi
545)
546else
547(
548	tmp="$IFS"
549	IFS="$IFS."
550	set -- `echo /mnt/etc/hostname*`
551	IFS=$tmp
552	unset tmp
553
554	while [ $# -ge 2 ] ; do
555		shift		# get rid of "hostname"
556		(
557			read af name mask bcaddr extras
558			read dt dtaddr
559
560			if [ ! -n "$name" ]; then
561		    echo "/etc/hostname.$1: invalid network configuration file"
562				exit
563			fi
564
565			cmd="ifconfig $1 $af $name "
566			if [ "${dt}" = "dest" ]; then cmd="$cmd $dtaddr"; fi
567			if [ -n "$mask" ]; then cmd="$cmd netmask $mask"; fi
568			if [ -n "$bcaddr" -a "X$bcaddr" != "XNONE" ]; then
569				cmd="$cmd broadcast $bcaddr";
570			fi
571			cmd="$cmd $extras"
572
573			$cmd
574		) < /mnt/etc/hostname.$1
575		shift
576	done
577)
578fi
579
580	# set the address for the loopback interface
581	ifconfig lo0 inet localhost
582
583	# use loopback, not the wire
584	route add $hostname localhost
585
586	# /etc/mygate, if it exists, contains the name of my gateway host
587	# that name must be in /etc/hosts.
588	if [ -f /mnt/etc/mygate ]; then
589		route delete default > /dev/null 2>&1
590		route add default `cat /mnt/etc/mygate`
591	fi
592
593	# enable the resolver, if appropriate.
594	if [ -f /mnt/etc/resolv.conf ]; then
595		_resolver_enabled="TRUE"
596		cp /mnt/etc/resolv.conf /tmp/resolv.conf.shadow
597	fi
598
599	# Display results...
600	echo	"Network interface configuration:"
601	ifconfig -a
602
603	echo	""
604
605	if [ "X${_resolver_enabled}" = X"TRUE" ]; then
606		netstat -r
607		echo	""
608		echo	"Resolver enabled."
609	else
610		netstat -rn
611		echo	""
612		echo	"Resolver not enabled."
613	fi
614
615	return 0
616}
617
618install_ftp() {
619	local	_f
620	local	_sets
621	local	_next
622
623	# Build a script to extract valid files from a list
624	# of filenames on stdin.
625	# XXX : Can we use this on more places? Leo.
626
627	echo "#!/bin/sh" > /tmp/fname_filter.sh
628	echo "while read line; do"	>> /tmp/fname_filter.sh
629	echo "    case \$line in"	>> /tmp/fname_filter.sh
630	for _f in $THESETS; do
631		echo "    $_f.tar.gz|$_f.tgz|$_f.${VERSION}.aa)" \
632					>> /tmp/fname_filter.sh
633		echo '        echo -n "$line ";;' \
634					>> /tmp/fname_filter.sh
635	done
636	echo "        *) ;;"		>> /tmp/fname_filter.sh
637	echo "    esac"			>> /tmp/fname_filter.sh
638	echo "done"			>> /tmp/fname_filter.sh
639
640	# Get several parameters from the user, and create
641	# a shell script that directs the appropriate
642	# commands into ftp.
643	cat << \__install_ftp_1
644
645This is an automated ftp-based installation process.  You will be asked
646several questions.  The correct set of commands will be placed in a script
647that will be fed to ftp(1).
648
649__install_ftp_1
650	# Get server IP address
651	resp=""		# force one iteration
652	while [ "X${resp}" = X"" ]; do
653		echo -n "Server IP? [${_ftp_server_ip}] "
654		getresp "${_ftp_server_ip}"
655		_ftp_server_ip=$resp
656	done
657
658	# Get login name
659	resp=""		# force one iteration
660	while [ "X${resp}" = X"" ]; do
661		echo -n "Login? [${_ftp_server_login}] "
662		getresp "${_ftp_server_login}"
663		_ftp_server_login=$resp
664	done
665
666	# Get password
667	resp=""		# force one iteration
668	while [ "X${resp}" = X"" ]; do
669		echo -n "Password? "
670		stty -echo
671		getresp ""
672		echo ""
673		stty echo
674		_ftp_server_password=$resp
675	done
676
677	cat << \__install_ftp_2
678
679You will be asked to enter the name of the directory that contains the
680installation sets. When you enter a '?' you will see a listing of the
681current directory on the server.
682__install_ftp_2
683	_sets=""
684	while [ -z "$_sets" ]
685	do
686		resp=""		# force one iteration
687		while [ "X${resp}" = X"" ]; do
688			echo -n "Server directory? [${_ftp_server_dir}] "
689		    getresp "${_ftp_server_dir}"
690		    if [ "X$resp" = 'X?' -a -z "$_ftp_server_dir" ]; then
691			resp=""
692		    fi
693		done
694		if [ $resp != '?' ]; then
695			_ftp_server_dir=$resp
696		fi
697
698		# Build the basics of an ftp-script...
699		echo "#!/bin/sh" > /tmp/ftp-script.sh
700		echo "cd /mnt" >> /tmp/ftp-script.sh
701		echo "ftp -e -i -n $_ftp_server_ip << \__end_commands" >> \
702		    /tmp/ftp-script.sh
703		echo "user $_ftp_server_login $_ftp_server_password" >> \
704		    /tmp/ftp-script.sh
705		echo "bin" >> /tmp/ftp-script.sh
706		echo "cd $_ftp_server_dir" >> /tmp/ftp-script.sh
707
708		# Make a copy of this script that lists the directory
709		# contents, and use that to determine the files to get.
710		cat /tmp/ftp-script.sh	>  /tmp/ftp-dir.sh
711		echo "nlist"		>> /tmp/ftp-dir.sh
712		echo "quit"		>> /tmp/ftp-dir.sh
713		echo "__end_commands"	>> /tmp/ftp-dir.sh
714
715		if [ $resp = '?' ]; then
716			sh /tmp/ftp-dir.sh
717		else
718			_sets=`sh /tmp/ftp-dir.sh | sh /tmp/fname_filter.sh`
719		fi
720	done
721	rm -f /tmp/ftp-dir.sh /tmp/fname_filter.sh
722
723	while : ; do
724		echo "The following sets are available for extraction:"
725		echo "(marked sets are already on the extraction list)"
726		echo ""
727
728		_next=""
729		for _f in $_sets ; do
730			if isin $_f $_setsdone; then
731				echo -n "[X] "
732				_next=""
733			else
734				echo -n "    "
735				if [ -z "$_next" ]; then _next=$_f; fi
736			fi
737			echo $_f
738		done
739		echo ""
740
741		# Get name of the file and add extraction command
742		# to the ftp-script.
743		if [ "X$_next" = "X" ]; then resp=n; else resp=y; fi
744		echo -n "Continue to add filenames [$resp]? "
745		getresp "$resp"
746		if [ "$resp" = "n" ]; then
747			break
748		fi
749
750		echo -n "File name [$_next]? "
751		getresp "$_next"
752		if isin $resp $_sets; then
753			echo "get $resp |\"pax -zr${verbose_flag}pe\"" >> \
754					/tmp/ftp-script.sh
755			_setsdone="$resp $_setsdone"
756		else
757			echo "You entered an invalid filename."
758			echo ""
759		fi
760	done
761
762	echo "quit" >> /tmp/ftp-script.sh
763	echo "__end_commands" >> /tmp/ftp-script.sh
764
765	sh /tmp/ftp-script.sh
766	rm -f /tmp/ftp-script.sh
767	echo "Extraction complete."
768}
769
770install_from_mounted_fs() {
771	# $1 - directory containing installation sets
772	local _filename
773	local _sets
774	local _next
775	local _all
776	local _f
777	local _dirname
778
779	_dirname=$1
780	_sets=""
781
782	if ! dir_has_sets ${_dirname} $THESETS
783	then
784
785		echo ""
786		echo "The directory at the mount point, \"${_dirname}\", contains: "
787		echo ""
788		ls -F ${_dirname}
789		echo ""
790		echo    "Enter the subdirectory relative to the mountpoint, that"
791		echo -n "contains the savesets: [try this directory] "
792		getresp ""
793		if [ "X${resp}" != "X" ]; then
794			_dirname=${_dirname}/$resp
795		fi
796
797		while ! dir_has_sets ${_dirname} $THESETS; do
798			echo ""
799			echo -n "There are no NetBSD install sets available in "
800			echo "\"${_dirname}\"."
801			echo "\"${_dirname}\" contains: "
802			echo ""
803			ls -F ${_dirname}
804			echo ""
805			echo -n "Enter subdirectory: [try other install media] "
806			getresp ""
807			if [ "X${resp}" = "X" ]; then
808				return
809			fi
810			if [ ! -d ${_dirname}/${resp} ]; then
811				echo "\"${resp}\" is no directory; try again."
812			else
813				_dirname=${_dirname}/$resp
814			fi
815		done
816	fi
817
818	for _f in $THESETS ; do
819		if [ -f ${_dirname}/${_f}.tar.gz ]; then
820			_sets="$_sets ${_f}.tar.gz"
821		elif [ -f ${_dirname}/${_f}.tgz ]; then
822			_sets="$_sets ${_f}.tgz"
823		elif [ -f ${_dirname}/${_f}${VERSION}.aa ]; then
824			_sets="$_sets ${_f}${VERSION}"
825		fi
826	done
827
828	while : ; do
829		echo "The following sets are available for extraction:"
830		echo "(marked sets have already been extracted)"
831		echo ""
832
833		_next=""
834		_all=""
835		for _f in $_sets ; do
836			if isin $_f $_setsdone; then
837				echo -n "[X] "
838				_next=""
839			else
840				echo -n "    "
841				if [ -z "$_next" ]; then
842					_next=$_f;
843				fi
844				_all="$_all $_f"
845			fi
846			echo $_f
847		done
848		echo ""
849
850		# Get the name of the file.
851		if [ "X$_next" = "X" ]; then
852			resp=n
853		else
854			resp=y
855		fi
856		echo -n "Continue extraction [$resp]?"
857		getresp "$resp"
858		if [ "$resp" = "n" ]; then
859			break
860		fi
861
862		echo -n "File name(s) (or "all") [$_next]? "
863		getresp "$_next"
864		if [ "x$resp" = xall ]; then
865			resp="$_all"
866		fi
867
868		for _f in $resp; do
869			_filename="/${_dirname}/$_f"
870
871			# Ensure file exists
872			if [ ! -f $_filename ]; then
873				if [ -f ${_filename}.aa ]; then
874					_filename=${_filename}.\?\?
875				else
876			 echo "File $_filename does not exist.  Check to make"
877			 echo "sure you entered the information properly."
878			 continue 2
879				fi
880			fi
881
882			# Extract file
883			echo "Extracting the $_f set:"
884			cat $_filename | (cd /mnt; pax -zr${verbose_flag}pe)
885			echo "Extraction complete."
886			_setsdone="$_f $_setsdone"
887		done
888
889	done
890}
891
892install_cdrom() {
893	local _drive
894	local _partition_range
895	local _partition
896	local _fstype
897	local _directory
898
899	# Get the cdrom device info
900	cat << \__install_cdrom_1
901
902The following CD-ROM devices are installed on your system; please select
903the CD-ROM device containing the partition with the installation sets:
904
905__install_cdrom_1
906	_CDDEVS=`md_get_cddevs`
907	echo    "$_CDDEVS"
908	echo	""
909	echo -n	"Which is the CD-ROM with the installation media? [abort] "
910	getresp "abort"
911	case "$resp" in
912		abort)
913			echo "Aborting."
914			return
915			;;
916
917		*)
918			if isin $resp $_CDDEVS ; then
919				_drive=$resp
920			else
921				echo ""
922				echo "The CD-ROM $resp does not exist."
923				echo "Aborting."
924				return
925			fi
926			;;
927	esac
928
929	# Get partition
930	_partition_range=`md_get_partition_range`
931	resp=""		# force one iteration
932	while [ "X${resp}" = X"" ]; do
933		echo -n "Partition? [a] "
934		getresp "a"
935		case "$resp" in
936			$_partition_range)
937				_partition=$resp
938				;;
939
940			*)
941				echo "Invalid response: $resp"
942				resp=""		# force loop to repeat
943				;;
944		esac
945	done
946
947	# Ask for filesystem type
948	cat << \__install_cdrom_2
949
950There are two CD-ROM filesystem types currently supported by this program:
951	1) ISO-9660 (cd9660)
952	2) Berkeley Fast Filesystem (ffs)
953
954__install_cdrom_2
955	resp=""		# force one iteration
956	while [ "X${resp}" = X"" ]; do
957		echo -n "Which filesystem type? [cd9660] "
958		getresp "cd9660"
959		case "$resp" in
960			cd9660|ffs)
961				_fstype=$resp
962				;;
963
964			*)
965				echo "Invalid response: $resp"
966				resp=""		# force loop to repeat
967				;;
968		esac
969	done
970
971	# Mount the CD-ROM
972	if ! mount -t ${_fstype} -o ro \
973	    /dev/${_drive}${_partition} /mnt2 ; then
974		echo "Cannot mount CD-ROM drive.  Aborting."
975		return
976	fi
977
978	install_from_mounted_fs /mnt2
979	umount -f /mnt2 > /dev/null 2>&1
980}
981
982mount_a_disk() {
983	# Mount a disk on /mnt2. The set of disk devices to choose from
984	# is $_DKDEVS.
985	# returns 0 on failure.
986
987	local _drive
988	local _partition_range
989	local _partition
990	local _fstype
991	local _fsopts
992	local _directory
993	local _md_fstype
994	local _md_fsopts
995
996	getresp "abort"
997	case "$resp" in
998		abort)
999			echo "Aborting."
1000			return 0
1001			;;
1002
1003		*)
1004			if isin $resp $_DKDEVS ; then
1005				_drive=$resp
1006			else
1007				echo ""
1008				echo "The disk $resp does not exist."
1009				echo "Aborting."
1010				return 0
1011			fi
1012			;;
1013	esac
1014
1015	# Get partition
1016	_partition_range=`md_get_partition_range`
1017	resp=""		# force one iteration
1018	while [ "X${resp}" = X"" ]; do
1019		echo -n "Partition? [d] "
1020		getresp "d"
1021		case "$resp" in
1022			$_partition_range)
1023				_partition=$resp
1024				;;
1025
1026			*)
1027				echo "Invalid response: $resp"
1028				resp=""		# force loop to repeat
1029				;;
1030		esac
1031	done
1032
1033	# Ask for filesystem type
1034	cat << \__mount_a_disk_2
1035
1036The following filesystem types are supported:
1037	1) ffs
1038__mount_a_disk_2
1039	_md_fstype=`md_native_fstype`
1040	_md_fsopts=`md_native_fsopts`
1041	if [ ! -z "$_md_fstype" ]; then
1042		echo "	2) $_md_fstype"
1043	else
1044		_md_fstype="_undefined_"
1045	fi
1046	resp=""		# force one iteration
1047	while [ "X${resp}" = X"" ]; do
1048		echo -n "Which filesystem type? [ffs] "
1049		getresp "ffs"
1050		case "$resp" in
1051			ffs)
1052				_fstype=$resp
1053				_fsopts="ro"
1054				;;
1055			$_md_fstype)
1056				_fstype=$resp
1057				_fsopts=$_md_fsopts
1058				;;
1059			*)
1060				echo "Invalid response: $resp"
1061				resp=""		# force loop to repeat
1062				;;
1063		esac
1064	done
1065
1066	# Mount the disk
1067	if ! mount -t ${_fstype} -o $_fsopts \
1068	    /dev/${_drive}${_partition} /mnt2 ; then
1069		echo "Cannot mount disk.  Aborting."
1070		return 0
1071	fi
1072	return 1
1073}
1074
1075install_disk() {
1076	local _directory
1077
1078	cat << \__install_disk_1
1079
1080Ok, lets install from a disk.  The file-system the install sets on may
1081already mounted, or we might have to mount the filesystem to get to it.
1082
1083__install_disk_1
1084
1085	echo -n "Is the file-system with the install sets already mounted? [n] "
1086	getresp "n"
1087	case $resp in
1088	y*|Y*)
1089		echo "What mount point are the sets located in? [] "
1090		getresp ""
1091		if [ -d "$resp" ]; then
1092			install_from_mounted_fs $resp
1093		else
1094			echo "$resp: Not a directory, aborting..."
1095		fi
1096		return
1097		;;
1098	*)
1099		;;
1100	esac
1101
1102	cat << \__install_disk_2
1103
1104The following disk devices are installed on your system; please select
1105the disk device containing the partition with the installation sets:
1106
1107__install_disk_2
1108	_DKDEVS=`md_get_diskdevs`
1109	echo    "$_DKDEVS"
1110	echo	""
1111	echo -n	"Which is the disk with the installation sets? [abort] "
1112
1113	if mount_a_disk ; then
1114		return
1115	fi
1116
1117	install_from_mounted_fs /mnt2
1118	umount -f /mnt2 > /dev/null 2>&1
1119}
1120
1121install_nfs() {
1122	# Get the IP address of the server
1123	resp=""		# force one iteration
1124	while [ "X${resp}" = X"" ]; do
1125		echo -n "Server IP address? [${_nfs_server_ip}] "
1126		getresp "${_nfs_server_ip}"
1127	done
1128	_nfs_server_ip=$resp
1129
1130	# Get server path to mount
1131	resp=""		# force one iteration
1132	while [ "X${resp}" = X"" ]; do
1133		echo -n "Filesystem on server to mount? [${_nfs_server_path}] "
1134		getresp "${_nfs_server_path}"
1135	done
1136	_nfs_server_path=$resp
1137
1138	# Determine use of TCP
1139	echo -n "Use TCP transport (only works with capable NFS server)? [n] "
1140	getresp "n"
1141	case "$resp" in
1142		y*|Y*)
1143			_nfs_tcp="-T"
1144			;;
1145
1146		*)
1147			_nfs_tcp=""
1148			;;
1149	esac
1150
1151	# Mount the server
1152	mkdir /mnt2 > /dev/null 2>&1
1153	if ! mount_nfs $_nfs_tcp ${_nfs_server_ip}:${_nfs_server_path} \
1154	    /mnt2 ; then
1155		echo "Cannot mount NFS server.  Aborting."
1156		return
1157	fi
1158
1159	install_from_mounted_fs /mnt2
1160	umount -f /mnt2 > /dev/null 2>&1
1161}
1162
1163install_tape() {
1164	local _xcmd
1165
1166	# Get the name of the tape from the user.
1167	cat << \__install_tape_1
1168
1169The installation program needs to know which tape device to use.  Make
1170sure you use a "no rewind on close" device.
1171
1172__install_tape_1
1173	_tape=`basename $TAPE`
1174	resp=""		# force one iteration
1175	while [ "X${resp}" = X"" ]; do
1176		echo -n "Name of tape device? [${_tape}]"
1177		getresp "${_tape}"
1178	done
1179	_tape=`basename $resp`
1180	TAPE="/dev/${_tape}"
1181	if [ ! -c $TAPE ]; then
1182		echo "$TAPE does not exist or is not a character special file."
1183		echo "Aborting."
1184		return
1185	fi
1186	export TAPE
1187
1188	# Rewind the tape device
1189	echo -n "Rewinding tape..."
1190	if ! mt rewind ; then
1191		echo "$TAPE may not be attached to the system or may not be"
1192		echo "a tape device.  Aborting."
1193		return
1194	fi
1195	echo "done."
1196
1197	# Get the file number
1198	resp=""		# force one iteration
1199	while [ "X${resp}" = X"" ]; do
1200		echo -n "File number? "
1201		getresp ""
1202		case "$resp" in
1203			[1-9]*)
1204				_nskip=`expr $resp - 1`
1205				;;
1206
1207			*)
1208				echo "Invalid file number ${resp}."
1209				resp=""		# fore loop to repeat
1210				;;
1211		esac
1212	done
1213
1214	# Skip to correct file.
1215	echo -n "Skipping to source file..."
1216	if [ "X${_nskip}" != X"0" ]; then
1217		if ! mt fsf $_nskip ; then
1218			echo "Could not skip $_nskip files.  Aborting."
1219			return
1220		fi
1221	fi
1222	echo "done."
1223
1224	cat << \__install_tape_2
1225
1226There are 2 different ways the file can be stored on tape:
1227
1228	1) an image of a gzipped tar file
1229	2) a standard tar image
1230
1231__install_tape_2
1232	resp=""		# force one iteration
1233	while [ "X${resp}" = X"" ]; do
1234		echo -n "Which way is it? [1] "
1235		getresp "1"
1236		case "$resp" in
1237		1)
1238			_xcmd="pax -zr${verbose_flag}pe"
1239			;;
1240
1241		2)
1242			_xcmd="pax -r${verbose_flag}pe"
1243			;;
1244
1245		*)
1246			echo "Invalid response: $resp."
1247			resp=""		# force loop to repeat
1248			;;
1249		esac
1250		( cd /mnt; dd if=$TAPE | $_xcmd )
1251	done
1252	echo "Extraction complete."
1253}
1254
1255get_timezone() {
1256	local _a
1257	local _zonepath
1258
1259	#
1260	# If the zoneinfo is not on the installation medium or on the
1261	# installed filesystem, set TZ to GMT and return immediatly.
1262	#
1263	if [ ! -e /usr/share/zoneinfo -a ! -e /mnt/usr/share/zoneinfo ]; then
1264		TZ=GMT
1265		return
1266	fi
1267	if [ ! -d /usr/share/zoneinfo ]; then
1268		_zonepath=/mnt
1269	else
1270		_zonepath=""
1271	fi
1272
1273cat << \__get_timezone_1
1274
1275Select a time zone for your location. Timezones are represented on the
1276system by a directory structure rooted in "/usr/share/timezone". Most
1277timezones can be selected by entering a token like "MET" or "GMT-6".
1278Other zones are grouped by continent, with detailed zone information
1279separated by a slash ("/"), e.g. "US/Pacific".
1280
1281To get a listing of what's available in /usr/share/zoneinfo, enter "?"
1282at the prompts below.
1283
1284__get_timezone_1
1285	if [ X$TZ = X ]; then
1286		TZ=`ls -l /mnt/etc/localtime 2>/dev/null | cutlast`
1287		TZ=${TZ#/usr/share/zoneinfo/}
1288	fi
1289	while :; do
1290		echo -n	"What timezone are you in [\`?' for list] [$TZ]? "
1291		getresp "$TZ"
1292		case "$resp" in
1293		"")
1294			echo "Timezone defaults to GMT"
1295			TZ="GMT"
1296			break;
1297			;;
1298		"?")
1299			ls ${_zonepath}/usr/share/zoneinfo
1300			;;
1301		*)
1302			_a=$resp
1303			while [ -d ${_zonepath}/usr/share/zoneinfo/$_a ]; do
1304				echo -n "There are several timezones available"
1305				echo " within zone '$_a'"
1306				echo -n "Select a sub-timezone [\`?' for list]: "
1307				getresp ""
1308				case "$resp" in
1309				"?") ls ${_zonepath}/usr/share/zoneinfo/$_a ;;
1310				*)	_a=${_a}/${resp}
1311					if [ -f ${_zonepath}/usr/share/zoneinfo/$_a ]; then
1312						break;
1313					fi
1314					;;
1315				esac
1316			done
1317			if [ -f ${_zonepath}/usr/share/zoneinfo/$_a ]; then
1318				TZ="$_a"
1319				echo "You have selected timezone \"$_a\"".
1320				break 2
1321			fi
1322			echo "'/usr/share/zoneinfo/$_a' is not a valid timezone on this system."
1323			;;
1324		esac
1325	done
1326}
1327
1328install_sets()
1329{
1330	local _yup
1331	_yup="FALSE"
1332
1333	# Ask the user which media to load the distribution from.
1334	# Ask the user if they want verbose extraction.  They might not want
1335	# it on, eg, SPARC frame buffer console.
1336	cat << \__install_sets_1
1337
1338It is now time to extract the installation sets onto the hard disk.
1339Make sure the sets are either on a local device (i.e. tape, CD-ROM) or on a
1340network server.
1341
1342Would you like to see each file listed during extraction (verbose) mode?
1343On some console hardware, such as serial consoles and Sun frame buffers,
1344this can extend the total extraction time.
1345__install_sets_1
1346	echo -n "Use verbose listing for extractions? [y] "
1347	getresp "y"
1348	case "$resp" in
1349	y*|Y*)
1350		verbose_flag=v
1351		;;
1352	*)
1353		echo "Not using verbose listing."
1354		verbose_flag=""
1355		;;
1356	esac
1357
1358	if [ -d ${Default_sets_dir:-/dev/null} ]; then
1359		if dir_has_sets $Default_sets_dir $THESETS; then
1360			local_sets_dir=$Default_sets_dir
1361		fi
1362	fi
1363	if [ "X$local_sets_dir" != "X" ]; then
1364		install_from_mounted_fs ${local_sets_dir}
1365		if [ X"$_setsdone" != X ]; then
1366			_yup="TRUE"
1367		fi
1368	fi
1369
1370	# Go on prodding for alternate locations
1371	resp=""		# force at least one iteration
1372	while [ X"${resp}" = X ]; do
1373		# If _yup is not FALSE, it means that we extracted sets above.
1374		# If that's the case, bypass the menu the first time.
1375		if [ X"$_yup" = X"FALSE" ]; then
1376			echo -n	"Install from (f)tp, (t)ape, (C)D-ROM, (N)FS"
1377			echo -n " or local (d)isk? "
1378			getresp ""
1379			case "$resp" in
1380			d*|D*)
1381				install_disk
1382				;;
1383			f*|F*)
1384				install_ftp
1385				;;
1386			t*|T*)
1387				install_tape
1388				;;
1389			c*|C*)
1390				install_cdrom
1391				;;
1392			n*|N*)
1393				install_nfs
1394				;;
1395			*)
1396				echo "Invalid response: $resp"
1397				resp=""
1398				;;
1399			esac
1400		else
1401			_yup="FALSE"	# So we'll ask next time
1402		fi
1403
1404		# Give the user the opportunity to extract more sets. They
1405		# don't necessarily have to come from the same media.
1406		echo	""
1407		echo -n	"Extract more sets? [n] "
1408		getresp "n"
1409		case "$resp" in
1410		y*|Y*)
1411			# Force loop to repeat
1412			resp=""
1413			;;
1414
1415		*)
1416			;;
1417		esac
1418	done
1419}
1420
1421munge_fstab()
1422{
1423	local _fstab
1424	local _fstab_shadow
1425	local _dev
1426	local _mp
1427	local _fstype
1428	local _rest
1429
1430	# Now that the 'real' fstab is configured, we munge it into a 'shadow'
1431	# fstab which we'll use for mounting and unmounting all of the target
1432	# filesystems relative to /mnt.  Mount all filesystems.
1433	_fstab=$1
1434	_fstab_shadow=$2
1435	( while read _dev _mp _fstype _rest; do
1436		# Skip comment lines
1437		case "$_dev" in
1438			\#*)	continue;;
1439			*)	;;
1440		esac
1441		# and some filesystem types (like there are swap,kernfs,...)
1442		case "$_fstype" in
1443			ffs|ufs|nfs)	;;
1444			*)	continue;;
1445		esac
1446		if [ "$_mp" = "/" ]; then
1447			echo $_dev /mnt $_fstype $_rest
1448		else
1449			echo $_dev /mnt$_mp $_fstype $_rest
1450		fi
1451	    done ) < $_fstab > $_fstab_shadow
1452}
1453
1454mount_fs()
1455{
1456	# Must mount filesystems manually, one at a time, so we can make
1457	# sure the mount points exist.
1458	# $1 is a file in fstab format
1459	local _fstab
1460
1461	_fstab=$1
1462
1463	( while read line; do
1464		set -- $line
1465		_dev=$1
1466		_mp=$2
1467		_fstype=$3
1468		_opt=$4
1469
1470		# If not the root filesystem, make sure the mount
1471		# point is present.
1472		if [ "X{$_mp}" != X"/mnt" ]; then
1473			mkdir -p $_mp
1474		fi
1475
1476		# Mount the filesystem.  If the mount fails, exit
1477		# with an error condition to tell the outer
1478		# later to bail.
1479		if ! mount -v -t $_fstype -o async -o $_opt $_dev $_mp ; then
1480			# error message displated by mount
1481			exit 1
1482		fi
1483	done ) < $_fstab
1484
1485	if [ "X${?}" != X"0" ]; then
1486		cat << \__mount_filesystems_1
1487
1488FATAL ERROR:  Cannot mount filesystems.  Double-check your configuration
1489and restart the installation process.
1490__mount_filesystems_1
1491		exit
1492	fi
1493}
1494
1495unmount_fs()
1496{
1497	# Unmount all filesystems and check their integrity.
1498	# Usage: [-fast] <fstab file>
1499	local _fast
1500	local _fstab
1501	local _pid
1502
1503	if [ "$1" = "-fast" ]; then
1504		_fast=1
1505		_fstab=$2
1506	else
1507		_fast=0
1508		_fstab=$1
1509	fi
1510
1511	if [ ! \( -f $_fstab -a -s $_fstab \) ]; then
1512		echo "fstab empty" > /dev/tty
1513		return
1514	fi
1515
1516	if [ $_fast = 0 ]; then
1517		echo -n	"Syncing disks..."
1518		_pid=`twiddle`
1519		sync; sleep 4; sync; sleep 2; sync; sleep 2
1520		kill $_pid
1521		echo	"done."
1522	fi
1523
1524	(
1525		_devs=""
1526		_mps=""
1527		# maintain reverse order
1528		while read line; do
1529			set -- $line
1530			_devs="$1 ${_devs}"
1531			_mps="$2 ${_mps}"
1532		done
1533		echo -n "Umounting filesystems... "
1534		for _mp in ${_mps}; do
1535			echo -n "${_mp} "
1536			umount ${_mp}
1537		done
1538		echo "Done."
1539
1540		if [ $_fast = 0 ]; then
1541			exit
1542		fi
1543		echo "Checking filesystem integrity..."
1544		for _dev in ${_devs}; do
1545			echo  "${_dev}"
1546			fsck -f ${_dev}
1547		done
1548		echo "Done."
1549	) < $_fstab
1550}
1551
1552check_fs()
1553{
1554	# Check filesystem integrity.
1555	# $1 is a file in fstab format
1556	local _fstab
1557
1558	_fstab=$1
1559
1560	(
1561		_devs=""
1562		_mps=""
1563		while read line; do
1564			set -- $line
1565			_devs="$1 ${_devs}"
1566			_mps="$2 ${_mps}"
1567		done
1568
1569		echo "Checking filesystem integrity..."
1570		for _dev in ${_devs}; do
1571			echo  "${_dev}"
1572			fsck -f ${_dev}
1573		done
1574		echo "Done."
1575	) < $_fstab
1576}
1577