xref: /netbsd/distrib/miniroot/install.sub (revision 6550d01e)
1#!/bin/sh
2#	$NetBSD: install.sub,v 1.45 2008/04/30 13:10:48 martin 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
35VERSION=				# filled in automatically (see list)
36export VERSION
37
38ALLSETS="base comp etc games man misc text"	# default install sets
39UPGRSETS="base comp games man misc text"	# default upgrade sets
40THESETS=					# one of the above
41
42local_sets_dir=""			# Path searched for sets by install_sets
43					# on the local filesystems
44
45# decide upon an editor
46if [ X$EDITOR = X ]; then
47	if [ -x /usr/bin/vi ]; then
48		EDITOR=vi
49	else
50		EDITOR=ed
51	fi
52fi
53
54getresp() {
55	read resp
56	if [ "X$resp" = "X" ]; then
57		resp=$1
58	fi
59}
60
61isin() {
62# test the first argument against the remaining ones, return succes on a match
63	_a=$1; shift
64	while [ $# != 0 ]; do
65		if [ "$_a" = "$1" ]; then return 0; fi
66		shift
67	done
68	return 1
69}
70
71rmel() {
72# remove first argument from list formed by the remaining arguments
73	local	_a
74
75	_a=$1; shift
76	while [ $# != 0 ]; do
77		if [ "$_a" != "$1" ]; then
78			echo "$1";
79		fi
80		shift
81	done
82}
83
84cutword () {
85# read a line of data, return Nth element.
86	local _a
87	local _n
88	local _oifs
89
90	# optional field separator
91	_oifs="$IFS"
92	case "$1" in
93		-t?*) IFS=${1#-t}; shift;;
94	esac
95
96	_n=$1
97	read _a; set -- $_a
98	IFS="$_oifs"
99	if [ "$1" = "" ]; then return; fi
100	eval echo \$$_n
101}
102
103cutlast () {
104# read a line of data, return last element. Equiv. of awk '{print $NF}'.
105	local _a
106	local _oifs
107
108	# optional field separator
109	_oifs="$IFS"
110	case "$1" in
111		-t?*) IFS=${1#-t}; shift;;
112	esac
113
114	read _a; set -- $_a
115	IFS="$_oifs"
116	if [ "$1" = "" ]; then return; fi
117	while [ "$#" -gt 10 ]; do shift 10; done
118	eval echo \$$#
119}
120
121firstchar () {
122# return first character of argument
123	local _a
124	_a=$1
125	while [ ${#_a} != 1 ]; do
126		_a=${_a%?}
127	done
128	echo $_a
129}
130
131basename () {
132	local _oifs
133	if [ "$1" = "" ]; then return; fi
134	_oifs="$IFS"
135	IFS="/"
136	set -- $1
137	IFS="$_oifs"
138	while [ "$#" -gt 10 ]; do shift 10; done
139	eval echo \$$#
140}
141
142dir_has_sets() {
143	# return true when the directory $1 contains a set for $2...$n
144	local _dir
145	local _file
146
147	_dir=$1; shift
148	for _file in $*
149	do
150		if [ -f $_dir/${_file}.tar.gz ]; then
151			return 0
152		fi
153		# Try for stupid msdos convention
154		if [ -f $_dir/${_file}.tgz ]; then
155			return 0
156		fi
157		# Try for uncompressed files
158		if [ -f $_dir/${_file}.tar ]; then
159			return 0
160		fi
161		# Try for split files
162		if [ -f $_dir/${_file}${VERSION}.aa ]; then
163			return 0
164		fi
165	done
166	return 1
167}
168
169twiddle() {
170# spin the propeller so we don't get bored
171	while : ; do
172		sleep 1; echo -n "/";
173		sleep 1; echo -n "-";
174		sleep 1; echo -n "\\";
175		sleep 1; echo -n "|";
176	done > /dev/tty & echo $!
177}
178
179get_localdir() {
180	# $1 is relative mountpoint
181	local _mp
182	local _dir
183
184	_mp=$1
185	_dir=
186	while : ; do
187	    if [ X$_mp != X ]; then
188		cat << __get_localdir_1
189Note: your filesystems are mounted under the temporary mount point \"$_mp\".
190The pathname you are requested to enter below should NOT include the \"$_mp\"
191prefix.
192__get_localdir_1
193	    fi
194	    echo -n "Enter the pathname where the sets are stored [$_dir] "
195	    getresp "$_dir"
196	    _dir=$resp
197
198	    # Allow break-out with empty response
199	    if [ -z "$_dir" ]; then
200		echo -n "Are you sure you don't want to set the pathname? [n] "
201		getresp "n"
202		case "$resp" in
203			y*|Y*)
204				break
205				;;
206			*)
207				continue
208				;;
209		esac
210	    fi
211
212	    if dir_has_sets "$_mp/$_dir" $THESETS
213	    then
214		local_sets_dir="$_mp/$_dir"
215		break
216	    else
217		cat << __get_localdir_2
218The directory \"$_mp/$_dir\" does not exist, or does not hold any of the
219upgrade sets.
220__get_localdir_2
221		echo -n "Re-enter pathname? [y] "
222		getresp "y"
223		case "$resp" in
224			y*|Y*)
225				;;
226			*)
227				local_sets_dir=""
228				break
229				;;
230		esac
231	    fi
232	done
233}
234
235getrootdisk() {
236	cat << \__getrootdisk_1
237
238The installation program needs to know which disk to consider
239the root disk.  Note the unit number may be different than
240the unit number you used in the standalone installation
241program.
242
243Available disks are:
244
245__getrootdisk_1
246	_DKDEVS=`md_get_diskdevs`
247	echo	"$_DKDEVS"
248	echo	""
249	echo -n	"Which disk is the root disk? "
250	getresp ""
251	if isin $resp $_DKDEVS ; then
252		ROOTDISK="$resp"
253	else
254		echo ""
255		echo "The disk $resp does not exist."
256		ROOTDISK=""
257	fi
258}
259
260labelmoredisks() {
261	cat << \__labelmoredisks_1
262
263You may label the following disks:
264
265__labelmoredisks_1
266	echo "$_DKDEVS"
267	echo	""
268	echo -n	"Label which disk? [done] "
269	getresp "done"
270	case "$resp" in
271		"done")
272			;;
273
274		*)
275			if isin $resp $_DKDEVS ; then
276				md_labeldisk $resp
277			else
278				echo ""
279				echo "The disk $resp does not exist."
280			fi
281			;;
282	esac
283}
284
285addhostent() {
286	# $1 - IP address
287	# $2 - symbolic name
288
289	local fqdn
290
291	# Create an entry in the hosts table.  If no host table
292	# exists, create one.  If the IP address already exists,
293	# replace its entry.
294	if [ ! -f /tmp/hosts ]; then
295		echo "127.0.0.1 localhost" > /tmp/hosts
296	fi
297
298	sed "/^$1 /d" < /tmp/hosts > /tmp/hosts.new
299	mv /tmp/hosts.new /tmp/hosts
300
301	if [ X${FQDN} != X ]; then
302		fqdn=$2.$FQDN
303	fi
304	echo "$1 $2 $fqdn" >> /tmp/hosts
305}
306
307addifconfig() {
308	# $1 - interface name
309	# $2 - interface symbolic name
310	# $3 - interface IP address
311	# $4 - interface netmask
312	# $5 - (optional) interface link-layer medium, preceded by "media ", else ""
313	# $6 - (optional) interface link-layer directives
314	local _m
315
316	# Create a ifconfig.* file for the interface.
317	echo "inet $2 netmask $4 $5 $6" > /tmp/ifconfig.$1
318
319	addhostent $3 $2
320}
321
322configurenetwork() {
323	local _ifsdone
324	local _ifs
325
326#	_IFS=`md_get_ifdevs`
327	_IFS=`ifconfig -l | sed '
328		s/lo0//
329		s/ppp[0-9]//g
330		s/sl[0-9]//g
331		s/tun[0-9]//g'`
332
333	_ifsdone=""
334	resp=""		# force at least one iteration
335	while [ "X${resp}" != X"done" ]; do
336	cat << \__configurenetwork_1
337
338You may configure the following network interfaces (the interfaces
339marked with [X] have been successfully configured):
340
341__configurenetwork_1
342
343		for _ifs in $_IFS; do
344			if isin $_ifs $_ifsdone ; then
345				echo -n "[X] "
346			else
347				echo -n "    "
348			fi
349			echo $_ifs
350		done
351		echo	""
352		echo -n	"Configure which interface? [done] "
353		getresp "done"
354		case "$resp" in
355		"done")
356			;;
357		*)
358			_ifs=$resp
359			if isin $_ifs $_IFS ; then
360				if configure_ifs $_ifs ; then
361					_ifsdone="$_ifs $_ifsdone"
362				fi
363			else
364				echo "Invalid response: \"$resp\" is not in list"
365			fi
366			;;
367		esac
368	done
369}
370
371configure_ifs() {
372
373	local _up
374	local _interface_name
375	local _interface_ip
376	local _interface_mask
377	local _interface_symname
378	local _interface_extra
379	local _interface_mediumtype
380	local _interface_supported_media
381	local _m
382	local _t
383
384	_interface_name=$1
385	_up=DOWN
386	if isin $_interface_name `ifconfig -l -u` ; then
387		_up=UP
388	fi
389
390	_interface_supported_media=`ifconfig -m $_interface_name | sed -n '
391		/^[ 	]*media autoselect/d
392		4,$s/[ 	]*media //p'`
393
394	# get current "media" "ip" and "netmask" ("broadcast")
395	_t=`ifconfig $_interface_name | sed -n '
396		s/^[ 	]*media: [^ 	]* \([^ ][^ ]*\).*/\1/p'`
397
398	if [ "$_t" != "manual" -a "$_t" != "media:" -a "$_t" != "autoselect" ];
399	then
400		_interface_mediumtype=$1
401	fi
402
403	set -- `ifconfig $_interface_name | sed -n '
404		/^[ 	]*inet/{
405		s/inet//
406		s/--> [0-9.][0-9.]*//
407		s/netmask//
408		s/broadcast//
409		p;}'`
410
411	_interface_ip=$1
412	_interface_mask=$2
413
414	# Get IP address
415	resp=""		# force one iteration
416	while [ "X${resp}" = X"" ]; do
417		echo -n "IP address? [$_interface_ip] "
418		getresp "$_interface_ip"
419		_interface_ip=$resp
420	done
421
422	# Get symbolic name
423	resp=""		# force one iteration
424	while [ "X${resp}" = X"" ]; do
425		echo -n "Symbolic (host) name? "
426		getresp ""
427		_interface_symname=$resp
428	done
429
430	# Get netmask
431	resp=""		# force one iteration
432	while [ "X${resp}" = X"" ]; do
433		echo -n "Netmask? [$_interface_mask] "
434		getresp "$_interface_mask"
435		_interface_mask=$resp
436	done
437
438	echo "Your network interface might require explicit selection"
439	echo "of the type of network medium attached. Supported media:"
440	echo "$_interface_supported_media"
441	echo -n "Additional media type arguments (none)? [$_interface_mediumtype] "
442	getresp "$_interface_mediumtype"
443	_m=""
444	if [ "X${resp}" != X"" -a "X${resp}" != Xnone ]; then
445		_interface_mediumtype=$resp
446		_m="media ${resp}"
447	fi
448
449
450	echo "Your network interface might require additional link-layer"
451	echo "directives (like \`link0'). If this is the case you can enter"
452	echo "these at the next prompt."
453	echo ""
454	echo -n "Additional link-layer arguments (none)? [$_interface_extra] "
455	getresp "$_interface_extra"
456	if [ "X${resp}" != X"" -a "X${resp}" != Xnone ]; then
457		_interface_extra=$resp
458	fi
459
460	# Configure the interface.  If it
461	# succeeds, add it to the permanent
462	# network configuration info.
463	if [ $_up != "UP" ]; then
464		ifconfig ${_interface_name} down
465		if ifconfig ${_interface_name} inet \
466		    ${_interface_ip} \
467		    netmask ${_interface_mask} \
468		    ${_interface_extra} ${_m} up ; then
469			addifconfig \
470			    "${_interface_name}" \
471			    "${_interface_symname}" \
472			    "${_interface_ip}" \
473			    "${_interface_mask}" \
474			    "${_m}" \
475			    "${_interface_extra}"
476			return 0
477		fi
478	else
479		echo "Interface ${_interface_name} is already active."
480		echo "Just saving configuration on new root filesystem."
481		addifconfig \
482		    "${_interface_name}" \
483		    "${_interface_symname}" \
484		    "${_interface_ip}" \
485		    "${_interface_mask}" \
486		    "${_m}" \
487		    "${_interface_extra}"
488	fi
489	return 1
490}
491
492# Much of this is gratuitously stolen from /etc/rc.d/network.
493enable_network() {
494
495	# Set up the hostname.
496	if [ -f /mnt/etc/myname ]; then
497		hostname=`cat /mnt/etc/myname`
498	elif [ -f /mnt/etc/rc.conf ];then
499		hostname=`sh -c '. /mnt/etc/rc.conf ; echo $hostname'`
500	else
501		echo "ERROR: no /etc/myname!"
502		return 1
503	fi
504	if [ -z "$hostname" ];then
505		echo "ERROR: hostname not set in /etc/myname or /etc/rc.conf!"
506		return 1
507	fi
508	hostname $hostname
509
510	# configure all the interfaces which we know about.
511if [ -f /mnt/etc/rc.conf ]; then
512(
513	# assume network interface configuration style 1.2D and up
514	if [ -f /mnt/etc/defaults/rc.conf ]; then
515		. /mnt/etc/defaults/rc.conf
516	fi
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.tar|$_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}.tar ]; then
824			_sets="$_sets ${_f}.tar"
825		elif [ -f ${_dirname}/${_f}${VERSION}.aa ]; then
826			_sets="$_sets ${_f}${VERSION}"
827		fi
828	done
829
830	while : ; do
831		echo "The following sets are available for extraction:"
832		echo "(marked sets have already been extracted)"
833		echo ""
834
835		_next=""
836		_all=""
837		for _f in $_sets ; do
838			if isin $_f $_setsdone; then
839				echo -n "[X] "
840				_next=""
841			else
842				echo -n "    "
843				if [ -z "$_next" ]; then
844					_next=$_f;
845				fi
846				_all="$_all $_f"
847			fi
848			echo $_f
849		done
850		echo ""
851
852		# Get the name of the file.
853		if [ "X$_next" = "X" ]; then
854			resp=n
855		else
856			resp=y
857		fi
858		echo -n "Continue extraction [$resp]?"
859		getresp "$resp"
860		if [ "$resp" = "n" ]; then
861			break
862		fi
863
864		echo -n "File name(s) (or "all") [$_next]? "
865		getresp "$_next"
866		if [ "x$resp" = xall ]; then
867			resp="$_all"
868		fi
869
870		for _f in $resp; do
871			_filename="/${_dirname}/$_f"
872
873			# Ensure file exists
874			if [ ! -f $_filename ]; then
875				if [ -f ${_filename}.aa ]; then
876					_filename=${_filename}.\?\?
877				else
878			 echo "File $_filename does not exist.  Check to make"
879			 echo "sure you entered the information properly."
880			 continue 2
881				fi
882			fi
883
884			# Extract file
885			echo "Extracting the $_f set:"
886			case "$_filename" in
887			*.tar)
888				(cd /mnt; pax -r${verbose_flag}pe < $_filename)
889				;;
890			*)
891				cat $_filename | \
892					(cd /mnt; pax -zr${verbose_flag}pe)
893				;;
894			esac
895			echo "Extraction complete."
896			_setsdone="$_f $_setsdone"
897		done
898
899	done
900}
901
902install_cdrom() {
903	local _drive
904	local _partition_range
905	local _partition
906	local _fstype
907	local _directory
908
909	# Get the cdrom device info
910	cat << \__install_cdrom_1
911
912The following CD-ROM devices are installed on your system; please select
913the CD-ROM device containing the partition with the installation sets:
914
915__install_cdrom_1
916	_CDDEVS=`md_get_cddevs`
917	echo    "$_CDDEVS"
918	echo	""
919	echo -n	"Which is the CD-ROM with the installation media? [abort] "
920	getresp "abort"
921	case "$resp" in
922		abort)
923			echo "Aborting."
924			return
925			;;
926
927		*)
928			if isin $resp $_CDDEVS ; then
929				_drive=$resp
930			else
931				echo ""
932				echo "The CD-ROM $resp does not exist."
933				echo "Aborting."
934				return
935			fi
936			;;
937	esac
938
939	# Get partition
940	_partition_range=`md_get_partition_range`
941	resp=""		# force one iteration
942	while [ "X${resp}" = X"" ]; do
943		echo -n "Partition? [a] "
944		getresp "a"
945		case "$resp" in
946			$_partition_range)
947				_partition=$resp
948				;;
949
950			*)
951				echo "Invalid response: $resp"
952				resp=""		# force loop to repeat
953				;;
954		esac
955	done
956
957	# Ask for filesystem type
958	cat << \__install_cdrom_2
959
960There are two CD-ROM filesystem types currently supported by this program:
961	1) ISO-9660 (cd9660)
962	2) Berkeley Fast Filesystem (ffs)
963
964__install_cdrom_2
965	resp=""		# force one iteration
966	while [ "X${resp}" = X"" ]; do
967		echo -n "Which filesystem type? [cd9660] "
968		getresp "cd9660"
969		case "$resp" in
970			cd9660|ffs)
971				_fstype=$resp
972				;;
973
974			*)
975				echo "Invalid response: $resp"
976				resp=""		# force loop to repeat
977				;;
978		esac
979	done
980
981	# Mount the CD-ROM
982	if ! mount -t ${_fstype} -o ro \
983	    /dev/${_drive}${_partition} /mnt2 ; then
984		echo "Cannot mount CD-ROM drive.  Aborting."
985		return
986	fi
987
988	install_from_mounted_fs /mnt2
989	umount -f /mnt2 > /dev/null 2>&1
990}
991
992mount_a_disk() {
993	# Mount a disk on /mnt2. The set of disk devices to choose from
994	# is $_DKDEVS.
995	# returns 0 on failure.
996
997	local _drive
998	local _partition_range
999	local _partition
1000	local _fstype
1001	local _fsopts
1002	local _directory
1003	local _md_fstype
1004	local _md_fsopts
1005
1006	getresp "abort"
1007	case "$resp" in
1008		abort)
1009			echo "Aborting."
1010			return 0
1011			;;
1012
1013		*)
1014			if isin $resp $_DKDEVS ; then
1015				_drive=$resp
1016			else
1017				echo ""
1018				echo "The disk $resp does not exist."
1019				echo "Aborting."
1020				return 0
1021			fi
1022			;;
1023	esac
1024
1025	# Get partition
1026	_partition_range=`md_get_partition_range`
1027	resp=""		# force one iteration
1028	while [ "X${resp}" = X"" ]; do
1029		echo -n "Partition? [d] "
1030		getresp "d"
1031		case "$resp" in
1032			$_partition_range)
1033				_partition=$resp
1034				;;
1035
1036			*)
1037				echo "Invalid response: $resp"
1038				resp=""		# force loop to repeat
1039				;;
1040		esac
1041	done
1042
1043	# Ask for filesystem type
1044	cat << \__mount_a_disk_2
1045
1046The following filesystem types are supported:
1047	1) ffs
1048__mount_a_disk_2
1049	_md_fstype=`md_native_fstype`
1050	_md_fsopts=`md_native_fsopts`
1051	if [ ! -z "$_md_fstype" ]; then
1052		echo "	2) $_md_fstype"
1053	else
1054		_md_fstype="_undefined_"
1055	fi
1056	resp=""		# force one iteration
1057	while [ "X${resp}" = X"" ]; do
1058		echo -n "Which filesystem type? [ffs] "
1059		getresp "ffs"
1060		case "$resp" in
1061			ffs)
1062				_fstype=$resp
1063				_fsopts="ro"
1064				;;
1065			$_md_fstype)
1066				_fstype=$resp
1067				_fsopts=$_md_fsopts
1068				;;
1069			*)
1070				echo "Invalid response: $resp"
1071				resp=""		# force loop to repeat
1072				;;
1073		esac
1074	done
1075
1076	# Mount the disk
1077	if ! mount -t ${_fstype} -o $_fsopts \
1078	    /dev/${_drive}${_partition} /mnt2 ; then
1079		echo "Cannot mount disk.  Aborting."
1080		return 0
1081	fi
1082	return 1
1083}
1084
1085install_disk() {
1086	local _directory
1087
1088	cat << \__install_disk_1
1089
1090Ok, lets install from a disk.  The file-system the install sets on may
1091already mounted, or we might have to mount the filesystem to get to it.
1092
1093__install_disk_1
1094
1095	echo -n "Is the file-system with the install sets already mounted? [n] "
1096	getresp "n"
1097	case $resp in
1098	y*|Y*)
1099		echo "What mount point are the sets located in? [] "
1100		getresp ""
1101		if [ -d "$resp" ]; then
1102			install_from_mounted_fs $resp
1103		else
1104			echo "$resp: Not a directory, aborting..."
1105		fi
1106		return
1107		;;
1108	*)
1109		;;
1110	esac
1111
1112	cat << \__install_disk_2
1113
1114The following disk devices are installed on your system; please select
1115the disk device containing the partition with the installation sets:
1116
1117__install_disk_2
1118	_DKDEVS=`md_get_diskdevs`
1119	echo    "$_DKDEVS"
1120	echo	""
1121	echo -n	"Which is the disk with the installation sets? [abort] "
1122
1123	if mount_a_disk ; then
1124		return
1125	fi
1126
1127	install_from_mounted_fs /mnt2
1128	umount -f /mnt2 > /dev/null 2>&1
1129}
1130
1131install_nfs() {
1132	# Get the IP address of the server
1133	resp=""		# force one iteration
1134	while [ "X${resp}" = X"" ]; do
1135		echo -n "Server IP address? [${_nfs_server_ip}] "
1136		getresp "${_nfs_server_ip}"
1137	done
1138	_nfs_server_ip=$resp
1139
1140	# Get server path to mount
1141	resp=""		# force one iteration
1142	while [ "X${resp}" = X"" ]; do
1143		echo -n "Filesystem on server to mount? [${_nfs_server_path}] "
1144		getresp "${_nfs_server_path}"
1145	done
1146	_nfs_server_path=$resp
1147
1148	# Determine use of TCP
1149	echo -n "Use TCP transport (only works with capable NFS server)? [n] "
1150	getresp "n"
1151	case "$resp" in
1152		y*|Y*)
1153			_nfs_tcp="-T"
1154			;;
1155
1156		*)
1157			echo -n "Use small NFS transfers (needed when server "
1158			echo "or client"
1159			echo -n "has a slow network card)? [n] "
1160			getresp "n"
1161			case "$resp" in
1162			y*|Y*)
1163				_nfs_tcp="-r 1024 -w 1024"
1164				;;
1165
1166			*)
1167				_nfs_tcp=""
1168				;;
1169			esac
1170			;;
1171	esac
1172
1173	# Mount the server
1174	mkdir /mnt2 > /dev/null 2>&1
1175	if ! mount_nfs $_nfs_tcp ${_nfs_server_ip}:${_nfs_server_path} \
1176	    /mnt2 ; then
1177		echo "Cannot mount NFS server.  Aborting."
1178		return
1179	fi
1180
1181	install_from_mounted_fs /mnt2
1182	umount -f /mnt2 > /dev/null 2>&1
1183}
1184
1185install_tape() {
1186	local _xcmd
1187
1188	# Get the name of the tape from the user.
1189	cat << \__install_tape_1
1190
1191The installation program needs to know which tape device to use.  Make
1192sure you use a "no rewind on close" device.
1193
1194__install_tape_1
1195	_tape=`basename $TAPE`
1196	resp=""		# force one iteration
1197	while [ "X${resp}" = X"" ]; do
1198		echo -n "Name of tape device? [${_tape}]"
1199		getresp "${_tape}"
1200	done
1201	_tape=`basename $resp`
1202	TAPE="/dev/${_tape}"
1203	if [ ! -c $TAPE ]; then
1204		echo "$TAPE does not exist or is not a character special file."
1205		echo "Aborting."
1206		return
1207	fi
1208	export TAPE
1209
1210	# Rewind the tape device
1211	echo -n "Rewinding tape..."
1212	if ! mt rewind ; then
1213		echo "$TAPE may not be attached to the system or may not be"
1214		echo "a tape device.  Aborting."
1215		return
1216	fi
1217	echo "done."
1218
1219	# Get the file number
1220	resp=""		# force one iteration
1221	while [ "X${resp}" = X"" ]; do
1222		echo -n "File number? "
1223		getresp ""
1224		case "$resp" in
1225			[1-9]*)
1226				_nskip=`expr $resp - 1`
1227				;;
1228
1229			*)
1230				echo "Invalid file number ${resp}."
1231				resp=""		# fore loop to repeat
1232				;;
1233		esac
1234	done
1235
1236	# Skip to correct file.
1237	echo -n "Skipping to source file..."
1238	if [ "X${_nskip}" != X"0" ]; then
1239		if ! mt fsf $_nskip ; then
1240			echo "Could not skip $_nskip files.  Aborting."
1241			return
1242		fi
1243	fi
1244	echo "done."
1245
1246	cat << \__install_tape_2
1247
1248There are 2 different ways the file can be stored on tape:
1249
1250	1) an image of a gzipped tar file
1251	2) a standard tar image
1252
1253__install_tape_2
1254	resp=""		# force one iteration
1255	while [ "X${resp}" = X"" ]; do
1256		echo -n "Which way is it? [1] "
1257		getresp "1"
1258		case "$resp" in
1259		1)
1260			_xcmd="pax -zr${verbose_flag}pe"
1261			;;
1262
1263		2)
1264			_xcmd="pax -r${verbose_flag}pe"
1265			;;
1266
1267		*)
1268			echo "Invalid response: $resp."
1269			resp=""		# force loop to repeat
1270			;;
1271		esac
1272		( cd /mnt; dd if=$TAPE | $_xcmd )
1273	done
1274	echo "Extraction complete."
1275}
1276
1277get_timezone() {
1278	local _a
1279	local _zonepath
1280
1281	#
1282	# If the zoneinfo is not on the installation medium or on the
1283	# installed filesystem, set TZ to GMT and return immediatly.
1284	#
1285	if [ ! -e /usr/share/zoneinfo -a ! -e /mnt/usr/share/zoneinfo ]; then
1286		TZ=GMT
1287		return
1288	fi
1289	if [ ! -d /usr/share/zoneinfo ]; then
1290		_zonepath=/mnt
1291	else
1292		_zonepath=""
1293	fi
1294
1295cat << \__get_timezone_1
1296
1297Select a time zone for your location. Timezones are represented on the
1298system by a directory structure rooted in "/usr/share/zoneinfo". Most
1299timezones can be selected by entering a token like "MET" or "GMT-6".
1300Other zones are grouped by continent, with detailed zone information
1301separated by a slash ("/"), e.g. "US/Pacific".
1302
1303To get a listing of what's available in /usr/share/zoneinfo, enter "?"
1304at the prompts below.
1305
1306__get_timezone_1
1307	if [ X$TZ = X ]; then
1308		TZ=`ls -l /mnt/etc/localtime 2>/dev/null | cutlast`
1309		TZ=${TZ#/usr/share/zoneinfo/}
1310	fi
1311	while :; do
1312		echo -n	"What timezone are you in [\`?' for list] [$TZ]? "
1313		getresp "$TZ"
1314		case "$resp" in
1315		"")
1316			echo "Timezone defaults to GMT"
1317			TZ="GMT"
1318			break;
1319			;;
1320		"?")
1321			ls ${_zonepath}/usr/share/zoneinfo
1322			;;
1323		*)
1324			_a=$resp
1325			while [ -d ${_zonepath}/usr/share/zoneinfo/$_a ]; do
1326				echo -n "There are several timezones available"
1327				echo " within zone '$_a'"
1328				echo -n "Select a sub-timezone [\`?' for list]: "
1329				getresp ""
1330				case "$resp" in
1331				"?") ls ${_zonepath}/usr/share/zoneinfo/$_a ;;
1332				*)	_a=${_a}/${resp}
1333					if [ -f ${_zonepath}/usr/share/zoneinfo/$_a ]; then
1334						break;
1335					fi
1336					;;
1337				esac
1338			done
1339			if [ -f ${_zonepath}/usr/share/zoneinfo/$_a ]; then
1340				TZ="$_a"
1341				echo "You have selected timezone \"$_a\"".
1342				break 2
1343			fi
1344			echo "'/usr/share/zoneinfo/$_a' is not a valid timezone on this system."
1345			;;
1346		esac
1347	done
1348}
1349
1350install_sets()
1351{
1352	local _yup
1353	_yup="FALSE"
1354
1355	# Ask the user which media to load the distribution from.
1356	# Ask the user if they want verbose extraction.  They might not want
1357	# it on, eg, SPARC frame buffer console.
1358	cat << \__install_sets_1
1359
1360It is now time to extract the installation sets onto the hard disk.
1361Make sure the sets are either on a local device (i.e. tape, CD-ROM) or on a
1362network server.
1363
1364Would you like to see each file listed during extraction (verbose) mode?
1365On some console hardware, such as serial consoles and Sun frame buffers,
1366this can extend the total extraction time.
1367__install_sets_1
1368	echo -n "Use verbose listing for extractions? [y] "
1369	getresp "y"
1370	case "$resp" in
1371	y*|Y*)
1372		verbose_flag=v
1373		;;
1374	*)
1375		echo "Not using verbose listing."
1376		verbose_flag=""
1377		;;
1378	esac
1379
1380	if [ -d ${Default_sets_dir:-/dev/null} ]; then
1381		if dir_has_sets $Default_sets_dir $THESETS; then
1382			local_sets_dir=$Default_sets_dir
1383		fi
1384	fi
1385	if [ "X$local_sets_dir" != "X" ]; then
1386		install_from_mounted_fs ${local_sets_dir}
1387		if [ X"$_setsdone" != X ]; then
1388			_yup="TRUE"
1389		fi
1390	fi
1391
1392	# Go on prodding for alternate locations
1393	resp=""		# force at least one iteration
1394	while [ X"${resp}" = X ]; do
1395		# If _yup is not FALSE, it means that we extracted sets above.
1396		# If that's the case, bypass the menu the first time.
1397		if [ X"$_yup" = X"FALSE" ]; then
1398			echo -n	"Install from (f)tp, (t)ape, (C)D-ROM, (N)FS"
1399			echo -n " or local (d)isk? "
1400			getresp ""
1401			case "$resp" in
1402			d*|D*)
1403				install_disk
1404				;;
1405			f*|F*)
1406				install_ftp
1407				;;
1408			t*|T*)
1409				install_tape
1410				;;
1411			c*|C*)
1412				install_cdrom
1413				;;
1414			n*|N*)
1415				install_nfs
1416				;;
1417			*)
1418				echo "Invalid response: $resp"
1419				resp=""
1420				;;
1421			esac
1422		else
1423			_yup="FALSE"	# So we'll ask next time
1424		fi
1425
1426		# Give the user the opportunity to extract more sets. They
1427		# don't necessarily have to come from the same media.
1428		echo	""
1429		echo -n	"Extract more sets? [n] "
1430		getresp "n"
1431		case "$resp" in
1432		y*|Y*)
1433			# Force loop to repeat
1434			resp=""
1435			;;
1436
1437		*)
1438			;;
1439		esac
1440	done
1441}
1442
1443munge_fstab()
1444{
1445	local _fstab
1446	local _fstab_shadow
1447	local _dev
1448	local _mp
1449	local _fstype
1450	local _rest
1451
1452	# Now that the 'real' fstab is configured, we munge it into a 'shadow'
1453	# fstab which we'll use for mounting and unmounting all of the target
1454	# filesystems relative to /mnt.  Mount all filesystems.
1455	_fstab=$1
1456	_fstab_shadow=$2
1457	( while read _dev _mp _fstype _rest; do
1458		# Skip comment lines
1459		case "$_dev" in
1460			\#*)	continue;;
1461			*)	;;
1462		esac
1463		# and some filesystem types (like there are swap,kernfs,...)
1464		case "$_fstype" in
1465			ffs|ufs|nfs)	;;
1466			*)	continue;;
1467		esac
1468		if [ "$_mp" = "/" ]; then
1469			echo $_dev /mnt $_fstype $_rest
1470		else
1471			echo $_dev /mnt$_mp $_fstype $_rest
1472		fi
1473	    done ) < $_fstab > $_fstab_shadow
1474}
1475
1476mount_fs()
1477{
1478	# Must mount filesystems manually, one at a time, so we can make
1479	# sure the mount points exist.
1480	# $1 is a file in fstab format
1481	local _fstab
1482
1483	_fstab=$1
1484
1485	( while read line; do
1486		set -- $line
1487		_dev=$1
1488		_mp=$2
1489		_fstype=$3
1490		_opt=$4
1491
1492		# If not the root filesystem, make sure the mount
1493		# point is present.
1494		if [ "X{$_mp}" != X"/mnt" ]; then
1495			mkdir -p $_mp
1496		fi
1497
1498		# Mount the filesystem.  If the mount fails, exit
1499		# with an error condition to tell the outer
1500		# later to bail.
1501		if ! mount -v -t $_fstype -o async -o $_opt $_dev $_mp ; then
1502			# error message displated by mount
1503			exit 1
1504		fi
1505	done ) < $_fstab
1506
1507	if [ "X${?}" != X"0" ]; then
1508		cat << \__mount_filesystems_1
1509
1510FATAL ERROR:  Cannot mount filesystems.  Double-check your configuration
1511and restart the installation process.
1512__mount_filesystems_1
1513		exit
1514	fi
1515}
1516
1517unmount_fs()
1518{
1519	# Unmount all filesystems and check their integrity.
1520	# Usage: [-fast] <fstab file>
1521	local _fast
1522	local _fstab
1523	local _pid
1524
1525	if [ "$1" = "-fast" ]; then
1526		_fast=1
1527		_fstab=$2
1528	else
1529		_fast=0
1530		_fstab=$1
1531	fi
1532
1533	if [ ! \( -f $_fstab -a -s $_fstab \) ]; then
1534		echo "fstab empty" > /dev/tty
1535		return
1536	fi
1537
1538	if [ $_fast = 0 ]; then
1539		echo -n	"Syncing disks..."
1540		_pid=`twiddle`
1541		sync; sleep 4; sync; sleep 2; sync; sleep 2
1542		kill $_pid
1543		echo	"done."
1544	fi
1545
1546	(
1547		_devs=""
1548		_mps=""
1549		# maintain reverse order
1550		while read line; do
1551			set -- $line
1552			_devs="$1 ${_devs}"
1553			_mps="$2 ${_mps}"
1554		done
1555		echo -n "Umounting filesystems... "
1556		for _mp in ${_mps}; do
1557			echo -n "${_mp} "
1558			umount ${_mp}
1559		done
1560		echo "Done."
1561
1562		if [ $_fast = 0 ]; then
1563			exit
1564		fi
1565		echo "Checking filesystem integrity..."
1566		for _dev in ${_devs}; do
1567			echo  "${_dev}"
1568			fsck -f ${_dev}
1569		done
1570		echo "Done."
1571	) < $_fstab
1572}
1573
1574check_fs()
1575{
1576	# Check filesystem integrity.
1577	# $1 is a file in fstab format
1578	local _fstab
1579
1580	_fstab=$1
1581
1582	(
1583		_devs=""
1584		_mps=""
1585		while read line; do
1586			set -- $line
1587			_devs="$1 ${_devs}"
1588			_mps="$2 ${_mps}"
1589		done
1590
1591		echo "Checking filesystem integrity..."
1592		for _dev in ${_devs}; do
1593			echo  "${_dev}"
1594			fsck -f ${_dev}
1595		done
1596		echo "Done."
1597	) < $_fstab
1598}
1599