1#!/bin/sh
2#-
3# Copyright (c) 2011 Nathan Whitehorn
4# Copyright (c) 2013-2018 Devin Teske
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28# $FreeBSD$
29#
30############################################################ INCLUDES
31
32BSDCFG_SHARE="/usr/share/bsdconfig"
33. $BSDCFG_SHARE/common.subr || exit 1
34f_include $BSDCFG_SHARE/dialog.subr
35
36############################################################ GLOBALS
37
38#
39# Strings that should be moved to an i18n file and loaded with f_include_lang()
40#
41hline_arrows_tab_enter="Press arrows, TAB or ENTER"
42hline_arrows_tab_space_enter="Press arrows, TAB, SPACE or ENTER"
43msg_abort="Abort"
44msg_an_installation_step_has_been_aborted="An installation step has been aborted. Would you like\nto restart the installation or exit the installer?"
45msg_auto_ufs="Auto (UFS)"
46msg_auto_ufs_desc="Guided UFS Disk Setup"
47msg_auto_ufs_help="Menu options help choose which disk to setup using UFS and standard partitions"
48msg_auto_zfs="Auto (ZFS)"
49msg_auto_zfs_desc="Guided Root-on-ZFS"
50msg_auto_zfs_help="To use ZFS with less than 8GB RAM, see https://wiki.freebsd.org/ZFSTuningGuide"
51msg_exit="Exit"
52msg_freebsd_installer="FreeBSD Installer"
53msg_gpt_active_fix="Your hardware is known to have issues booting in CSM/Legacy/BIOS mode from GPT partitions that are not set active. Would you like the installer to apply this workaround for you?"
54msg_lenovo_fix="Your model of Lenovo is known to have a BIOS bug that prevents it booting from GPT partitions without UEFI. Would you like the installer to apply a workaround for you?"
55msg_manual="Manual"
56msg_manual_desc="Manual Disk Setup (experts)"
57msg_manual_help="Create customized partitions from menu options"
58msg_no="NO"
59msg_restart="Restart"
60msg_shell="Shell"
61msg_shell_desc="Open a shell and partition by hand"
62msg_shell_help="Create customized partitions using command-line utilities"
63msg_yes="YES"
64
65############################################################ FUNCTIONS
66
67# error [$msg]
68#
69# Display generic error message when a script fails. An optional message
70# argument can preceed the generic message. User is given the choice of
71# restarting the installer or exiting.
72#
73error()
74{
75	local title="$msg_abort"
76	local btitle="$msg_freebsd_installer"
77	local prompt="${1:+$1\n\n}$msg_an_installation_step_has_been_aborted"
78	local hline="$hline_arrows_tab_space_enter"
79
80	[ "$DISTDIR_IS_UNIONFS" ] && umount -f "$BSDINSTALL_DISTDIR"
81	[ -f "$PATH_FSTAB" ] && bsdinstall umount
82
83	local height width
84	f_dialog_buttonbox_size height width \
85		"$title" "$btitle" "$prompt" "$hline"
86
87	if $DIALOG \
88		--title "$title"           \
89		--backtitle "$btitle"      \
90		--hline "$hline"           \
91		--no-label "$msg_exit"     \
92		--yes-label "$msg_restart" \
93		--yesno "$prompt" $height $width
94	then
95		exec $0
96		# NOTREACHED
97	fi
98	exit 1
99}
100
101# dialog_workaround
102#
103# Ask the user if they wish to apply a workaround
104#
105dialog_workaround()
106{
107	local passed_msg="$1"
108	local title="$DIALOG_TITLE"
109	local btitle="$DIALOG_BACKTITLE"
110	local prompt # Calculated below
111	local hline="$hline_arrows_tab_enter"
112
113	local height=8 width=50 prefix="   "
114	local plen=${#prefix} list= line=
115	local max_width=$(( $width - 3 - $plen ))
116
117	local yes no defaultno extra_args format
118	if [ "$USE_XDIALOG" ]; then
119		yes=ok no=cancel defaultno=default-no
120		extra_args="--wrap --left"
121		format="$passed_msg"
122	else
123		yes=yes no=no defaultno=defaultno
124		extra_args="--cr-wrap"
125		format="$passed_msg"
126	fi
127
128	# Add height for Xdialog(1)
129	[ "$USE_XDIALOG" ] && height=$(( $height + $height / 5 + 3 ))
130
131	prompt=$( printf "$format" )
132	f_dprintf "%s: Workaround prompt" "$0"
133	$DIALOG \
134		--title "$title"        \
135		--backtitle "$btitle"   \
136		--hline "$hline"        \
137		--$yes-label "$msg_yes" \
138		--$no-label "$msg_no"   \
139		$extra_args             \
140		--yesno "$prompt" $height $width
141}
142
143############################################################ MAIN
144
145f_dprintf "Began Installation at %s" "$( date )"
146
147rm -rf $BSDINSTALL_TMPETC
148mkdir $BSDINSTALL_TMPETC
149
150trap true SIGINT	# This section is optional
151bsdinstall keymap
152
153trap error SIGINT	# Catch cntrl-C here
154bsdinstall hostname || error "Set hostname failed"
155
156export DISTRIBUTIONS="base.txz kernel.txz"
157if [ -f $BSDINSTALL_DISTDIR/MANIFEST ]; then
158	DISTMENU=`awk -F'\t' '!/^(kernel\.txz|base\.txz)/{print $1,$5,$6}' $BSDINSTALL_DISTDIR/MANIFEST`
159	DISTMENU="$(echo ${DISTMENU} | sed -E 's/\.txz//g')"
160
161	exec 3>&1
162	EXTRA_DISTS=$( eval dialog \
163	    --backtitle \"FreeBSD Installer\" \
164	    --title \"Distribution Select\" --nocancel --separate-output \
165	    --checklist \"Choose optional system components to install:\" \
166	    0 0 0 $DISTMENU \
167	2>&1 1>&3 )
168	for dist in $EXTRA_DISTS; do
169		export DISTRIBUTIONS="$DISTRIBUTIONS $dist.txz"
170	done
171fi
172
173LOCAL_DISTRIBUTIONS="MANIFEST"
174FETCH_DISTRIBUTIONS=""
175for dist in $DISTRIBUTIONS; do
176	if [ ! -f $BSDINSTALL_DISTDIR/$dist ]; then
177		FETCH_DISTRIBUTIONS="$FETCH_DISTRIBUTIONS $dist"
178	else
179		LOCAL_DISTRIBUTIONS="$LOCAL_DISTRIBUTIONS $dist"
180	fi
181done
182LOCAL_DISTRIBUTIONS=`echo $LOCAL_DISTRIBUTIONS`	# Trim white space
183FETCH_DISTRIBUTIONS=`echo $FETCH_DISTRIBUTIONS`	# Trim white space
184
185if [ -n "$FETCH_DISTRIBUTIONS" -a -n "$BSDINSTALL_CONFIGCURRENT" ]; then
186	dialog --backtitle "FreeBSD Installer" --title "Network Installation" --msgbox "Some installation files were not found on the boot volume. The next few screens will allow you to configure networking so that they can be downloaded from the Internet." 0 0
187	bsdinstall netconfig || error
188	NETCONFIG_DONE=yes
189fi
190
191if [ -n "$FETCH_DISTRIBUTIONS" ]; then
192	exec 3>&1
193	BSDINSTALL_DISTSITE=$(`dirname $0`/mirrorselect 2>&1 1>&3)
194	MIRROR_BUTTON=$?
195	exec 3>&-
196	test $MIRROR_BUTTON -eq 0 || error "No mirror selected"
197	export BSDINSTALL_DISTSITE
198fi
199
200rm -f $PATH_FSTAB
201touch $PATH_FSTAB
202
203#
204# Try to detect known broken platforms and apply their workarounds
205#
206
207if f_interactive; then
208	sys_maker=$( kenv -q smbios.system.maker )
209	f_dprintf "smbios.system.maker=[%s]" "$sys_maker"
210	sys_model=$( kenv -q smbios.system.product )
211	f_dprintf "smbios.system.product=[%s]" "$sys_model"
212	sys_version=$( kenv -q smbios.system.version )
213	f_dprintf "smbios.system.version=[%s]" "$sys_version"
214	sys_mb_maker=$( kenv -q smbios.planar.maker )
215	f_dprintf "smbios.planar.maker=[%s]" "$sys_mb_maker"
216	sys_mb_product=$( kenv -q smbios.planar.product )
217	f_dprintf "smbios.planar.product=[%s]" "$sys_mb_product"
218
219	#
220	# Laptop Models
221	#
222	case "$sys_maker" in
223	"LENOVO")
224		case "$sys_version" in
225		"ThinkPad X220"|"ThinkPad T420"|"ThinkPad T520"|"ThinkPad W520"|"ThinkPad X1")
226			dialog_workaround "$msg_lenovo_fix"
227			retval=$?
228			f_dprintf "lenovofix_prompt=[%s]" "$retval"
229			if [ $retval -eq $DIALOG_OK ]; then
230				export ZFSBOOT_PARTITION_SCHEME="GPT + Lenovo Fix"
231				export WORKAROUND_LENOVO=1
232			fi
233			;;
234		esac
235		;;
236	"Dell Inc.")
237		case "$sys_model" in
238		"Latitude E6330"|"Latitude E7440"|"Latitude E7240"|"Precision Tower 5810")
239			dialog_workaround "$msg_gpt_active_fix"
240			retval=$?
241			f_dprintf "gpt_active_fix_prompt=[%s]" "$retval"
242			if [ $retval -eq $DIALOG_OK ]; then
243				export ZFSBOOT_PARTITION_SCHEME="GPT + Active"
244				export WORKAROUND_GPTACTIVE=1
245			fi
246			;;
247		esac
248		;;
249	"Hewlett-Packard")
250		case "$sys_model" in
251		"HP ProBook 4330s")
252			dialog_workaround "$msg_gpt_active_fix"
253			retval=$?
254			f_dprintf "gpt_active_fix_prompt=[%s]" "$retval"
255			if [ $retval -eq $DIALOG_OK ]; then
256				export ZFSBOOT_PARTITION_SCHEME="GPT + Active"
257				export WORKAROUND_GPTACTIVE=1
258			fi
259			;;
260		esac
261		;;
262	esac
263	#
264	# Motherboard Models
265	#
266	case "$sys_mb_maker" in
267	"Intel Corporation")
268		case "$sys_mb_product" in
269		"DP965LT"|"D510MO")
270			dialog_workaround "$msg_gpt_active_fix"
271			retval=$?
272			f_dprintf "gpt_active_fix_prompt=[%s]" "$retval"
273			if [ $retval -eq $DIALOG_OK ]; then
274				export ZFSBOOT_PARTITION_SCHEME="GPT + Active"
275				export WORKAROUND_GPTACTIVE=1
276			fi
277			;;
278		esac
279		;;
280	"Acer")
281		case "$sys_mb_product" in
282		"Veriton M6630G")
283			dialog_workaround "$msg_gpt_active_fix"
284			retval=$?
285			f_dprintf "gpt_active_fix_prompt=[%s]" "$retval"
286			if [ $retval -eq $DIALOG_OK ]; then
287				export ZFSBOOT_PARTITION_SCHEME="GPT + Active"
288				export WORKAROUND_GPTACTIVE=1
289			fi
290			;;
291		esac
292		;;
293	esac
294fi
295
296PMODES="
297	'$msg_auto_ufs' '$msg_auto_ufs_desc' '$msg_auto_ufs_help'
298	'$msg_manual' '$msg_manual_desc' '$msg_manual_help'
299	'$msg_shell' '$msg_shell_desc' '$msg_shell_help'
300" # END-QUOTE
301
302CURARCH=$( uname -m )
303case $CURARCH in
304	amd64|arm64|i386)	# Booting ZFS Supported
305		PMODES="
306			'$msg_auto_zfs' '$msg_auto_zfs_desc' '$msg_auto_zfs_help'
307			$PMODES
308		" # END-QUOTE
309		;;
310	*)			# Booting ZFS Unsupported
311		;;
312esac
313
314exec 3>&1
315PARTMODE=`echo $PMODES | xargs dialog --backtitle "FreeBSD Installer" \
316	--title "Partitioning" \
317	--item-help \
318	--menu "How would you like to partition your disk?" \
319	0 0 0 2>&1 1>&3` || exit 1
320exec 3>&-
321
322case "$PARTMODE" in
323"$msg_auto_zfs")	# ZFS
324	bsdinstall zfsboot || error "ZFS setup failed"
325	bsdinstall mount || error "Failed to mount filesystem"
326	;;
327"$msg_auto_ufs")	# Guided UFS
328	bsdinstall autopart || error "Partitioning error"
329	bsdinstall mount || error "Failed to mount filesystem"
330	;;
331"$msg_shell")		# Shell
332	clear
333	echo "Use this shell to set up partitions for the new system. When finished, mount the system at $BSDINSTALL_CHROOT and place an fstab file for the new system at $PATH_FSTAB. Then type 'exit'. You can also enter the partition editor at any time by entering 'bsdinstall partedit'."
334	sh 2>&1
335	;;
336"$msg_manual")		# Manual
337	if f_isset debugFile; then
338		# Give partedit the path to our logfile so it can append
339		BSDINSTALL_LOG="${debugFile#+}" bsdinstall partedit || error "Partitioning error"
340	else
341		bsdinstall partedit || error "Partitioning error"
342	fi
343	bsdinstall mount || error "Failed to mount filesystem"
344	;;
345*)
346	error "Unknown partitioning mode"
347	;;
348esac
349
350if [ ! -z "$FETCH_DISTRIBUTIONS" ]; then
351	ALL_DISTRIBUTIONS="$DISTRIBUTIONS"
352	WANT_DEBUG=
353
354	# Download to a directory in the new system as scratch space
355	BSDINSTALL_FETCHDEST="$BSDINSTALL_CHROOT/usr/freebsd-dist"
356	mkdir -p "$BSDINSTALL_FETCHDEST" || error "Could not create directory $BSDINSTALL_FETCHDEST"
357
358	export DISTRIBUTIONS="$FETCH_DISTRIBUTIONS"
359	# Try to use any existing distfiles
360	if [ -d $BSDINSTALL_DISTDIR ]; then
361		DISTDIR_IS_UNIONFS=1
362		mount_nullfs -o union "$BSDINSTALL_FETCHDEST" "$BSDINSTALL_DISTDIR"
363	else
364		export DISTRIBUTIONS="$FETCH_DISTRIBUTIONS"
365		export BSDINSTALL_DISTDIR="$BSDINSTALL_FETCHDEST"
366	fi
367
368	export FTP_PASSIVE_MODE=YES
369	# Iterate through the distribution list and set a flag if debugging
370	# distributions have been selected.
371	for _DISTRIBUTION in $DISTRIBUTIONS; do
372		case $_DISTRIBUTION in
373			*-dbg.*)
374				[ -e $BSDINSTALL_DISTDIR/$_DISTRIBUTION ] \
375					&& continue
376				WANT_DEBUG=1
377				DEBUG_LIST="\n$DEBUG_LIST\n$_DISTRIBUTION"
378				;;
379			*)
380				;;
381		esac
382	done
383
384	# Fetch the distributions.
385	bsdinstall distfetch
386	rc=$?
387
388	if [ $rc -ne 0 ]; then
389		# If unable to fetch the remote distributions, recommend
390		# deselecting the debugging distributions, and retrying the
391		# installation, since failure to fetch *-dbg.txz should not
392		# be considered a fatal installation error.
393		msg="Failed to fetch remote distribution"
394		if [ ! -z "$WANT_DEBUG" ]; then
395			# Trim leading and trailing newlines.
396			DEBUG_LIST="${DEBUG_LIST%%\n}"
397			DEBUG_LIST="${DEBUG_LIST##\n}"
398			msg="$msg\n\nPlease deselect the following distributions"
399			msg="$msg and retry the installation:"
400			msg="$msg\n$DEBUG_LIST"
401		fi
402		error "$msg"
403	fi
404	export DISTRIBUTIONS="$ALL_DISTRIBUTIONS"
405fi
406
407if [ ! -z "$LOCAL_DISTRIBUTIONS" ]; then
408	# Download to a directory in the new system as scratch space
409	BSDINSTALL_FETCHDEST="$BSDINSTALL_CHROOT/usr/freebsd-dist"
410	mkdir -p "$BSDINSTALL_FETCHDEST" || error "Could not create directory $BSDINSTALL_FETCHDEST"
411	# Try to use any existing distfiles
412	if [ -d $BSDINSTALL_DISTDIR ]; then
413		DISTDIR_IS_UNIONFS=1
414		mount_nullfs -o union "$BSDINSTALL_FETCHDEST" "$BSDINSTALL_DISTDIR"
415		export BSDINSTALL_DISTDIR="$BSDINSTALL_FETCHDEST"
416	fi
417	env DISTRIBUTIONS="$LOCAL_DISTRIBUTIONS" \
418		BSDINSTALL_DISTSITE="file:///usr/freebsd-dist" \
419		bsdinstall distfetch || \
420		error "Failed to fetch distribution from local media"
421fi
422
423bsdinstall checksum || error "Distribution checksum failed"
424bsdinstall distextract || error "Distribution extract failed"
425
426# Set up boot loader
427bsdinstall bootconfig || error "Failed to configure bootloader"
428
429bsdinstall rootpass || error "Could not set root password"
430
431trap true SIGINT	# This section is optional
432if [ "$NETCONFIG_DONE" != yes ]; then
433	bsdinstall netconfig	# Don't check for errors -- the user may cancel
434fi
435bsdinstall time
436bsdinstall services
437bsdinstall hardening
438
439dialog --backtitle "FreeBSD Installer" --title "Add User Accounts" --yesno \
440    "Would you like to add users to the installed system now?" 0 0 && \
441    bsdinstall adduser
442
443finalconfig() {
444	exec 3>&1
445	REVISIT=$(dialog --backtitle "FreeBSD Installer" \
446	    --title "Final Configuration" --no-cancel --menu \
447	    "Setup of your FreeBSD system is nearly complete. You can now modify your configuration choices. After this screen, you will have an opportunity to make more complex changes using a shell." 0 0 0 \
448		"Exit" "Apply configuration and exit installer" \
449		"Add User" "Add a user to the system" \
450		"Root Password" "Change root password" \
451		"Hostname" "Set system hostname" \
452		"Network" "Networking configuration" \
453		"Services" "Set daemons to run on startup" \
454		"System Hardening" "Set security options" \
455		"Time Zone" "Set system timezone" \
456		"Handbook" "Install FreeBSD Handbook (requires network)" 2>&1 1>&3)
457	exec 3>&-
458
459	case "$REVISIT" in
460	"Add User")
461		bsdinstall adduser
462		finalconfig
463		;;
464	"Root Password")
465		bsdinstall rootpass
466		finalconfig
467		;;
468	"Hostname")
469		bsdinstall hostname
470		finalconfig
471		;;
472	"Network")
473		bsdinstall netconfig
474		finalconfig
475		;;
476	"Services")
477		bsdinstall services
478		finalconfig
479		;;
480	"System Hardening")
481		bsdinstall hardening
482		finalconfig
483		;;
484	"Time Zone")
485		bsdinstall time
486		finalconfig
487		;;
488	"Handbook")
489		bsdinstall docsinstall
490		finalconfig
491		;;
492	esac
493}
494
495# Allow user to change his mind
496finalconfig
497
498trap error SIGINT	# SIGINT is bad again
499bsdinstall config  || error "Failed to save config"
500
501if [ -n "$DISTDIR_IS_UNIONFS" ]; then
502	umount -f $BSDINSTALL_DISTDIR
503fi
504
505if [ ! -z "$BSDINSTALL_FETCHDEST" ]; then
506	rm -rf "$BSDINSTALL_FETCHDEST"
507fi
508
509dialog --backtitle "FreeBSD Installer" --title "Manual Configuration" \
510    --default-button no --yesno \
511   "The installation is now finished. Before exiting the installer, would you like to open a shell in the new system to make any final manual modifications?" 0 0
512if [ $? -eq 0 ]; then
513	clear
514	echo This shell is operating in a chroot in the new system. \
515	    When finished making configuration changes, type \"exit\".
516	chroot "$BSDINSTALL_CHROOT" /bin/sh 2>&1
517fi
518
519bsdinstall entropy
520bsdinstall umount
521
522f_dprintf "Installation Completed at %s" "$( date )"
523
524################################################################################
525# END
526################################################################################
527