1#!/bin/mksh
2# $MirOS: src/distrib/common/install.sh,v 1.28 2013/09/11 18:55:55 tg Exp $
3# $OpenBSD: install.sh,v 1.152 2005/04/21 21:41:33 krw Exp $
4# $NetBSD: install.sh,v 1.5.2.8 1996/08/27 18:15:05 gwr Exp $
5#
6# Copyright (c) 2007, 2008, 2009 Thorsten Glaser
7# Copyright (c) 1997-2004 Todd Miller, Theo de Raadt, Ken Westerback
8# All rights reserved.
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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
20# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29#
30# Copyright (c) 1996 The NetBSD Foundation, Inc.
31# All rights reserved.
32#
33# This code is derived from software contributed to The NetBSD Foundation
34# by Jason R. Thorpe.
35#
36# Redistribution and use in source and binary forms, with or without
37# modification, are permitted provided that the following conditions
38# are met:
39# 1. Redistributions of source code must retain the above copyright
40#    notice, this list of conditions and the following disclaimer.
41# 2. Redistributions in binary form must reproduce the above copyright
42#    notice, this list of conditions and the following disclaimer in the
43#    documentation and/or other materials provided with the distribution.
44# 3. All advertising materials mentioning features or use of this software
45#    must display the following acknowledgement:
46#        This product includes software developed by the NetBSD
47#        Foundation, Inc. and its contributors.
48# 4. Neither the name of The NetBSD Foundation nor the names of its
49#    contributors may be used to endorse or promote products derived
50#    from this software without specific prior written permission.
51#
52# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
53# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
54# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
55# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
56# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
59# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
60# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
61# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
62# POSSIBILITY OF SUCH DAMAGE.
63#
64
65#	OpenBSD installation script.
66#	In a perfect world, this would be a nice C program, with a reasonable
67#	user interface.
68
69# The name of the file holding the list of configured filesystems.
70FILESYSTEMS=/tmp/filesystems
71
72# The name of the file holding the list of non-default configured swap devices.
73SWAPLIST=/tmp/swaplist
74
75# install.sub needs to know the MODE
76MODE=install
77
78# include common subroutines and initialization code
79. install.sub
80
81# If /etc/fstab already exists, skip disk initialization.
82if [ ! -f /etc/fstab ]; then
83	# Install the shadowed disktab file; lets us write to it for temporary
84	# purposes without mounting the miniroot read-write.
85	[ -f /etc/disktab.shadow ] && cp /etc/disktab.shadow /tmp/disktab.shadow
86
87	DISK=
88	_DKDEVS=$DKDEVS
89
90	while :; do
91		_DKDEVS=$(rmel "$DISK" $_DKDEVS)
92
93		# Always do ROOTDISK first, and repeat until
94		# it is configured acceptably.
95		if isin $ROOTDISK $_DKDEVS; then
96			resp=$ROOTDISK
97			rm -f /tmp/fstab
98			# Make sure empty files exist so we don't have to
99			# keep checking for their existence before grep'ing.
100			: >$FILESYSTEMS
101			: >$SWAPLIST
102		else
103			# Force the user to think and type in a disk name by
104			# making 'done' the default choice.
105			ask_which "disk" "do you wish to initialise" "$_DKDEVS" done "No more disks to initialise"
106			[[ $resp = done ]] && break
107		fi
108
109		DISK=$resp
110		makedev $DISK || continue
111
112		# Deal with disklabels, including editing the root disklabel
113		# and labeling additional disks. This is machine-dependent since
114		# some platforms may not be able to provide this functionality.
115		# /tmp/fstab.$DISK is created here with 'disklabel -f'.
116		rm -f /tmp/*.$DISK
117		md_prep_disklabel $DISK
118
119		# Get the lists of BSD and swap partitions.
120		unset _partitions _psizes _mount_points
121		_i=0
122		disklabel $DISK 2>&1 | sed -ne '/^ *[a-p]: /p' >/tmp/disklabel.$DISK
123		while read _dev _size _offset _type _rest; do
124			_pp=${DISK}${_dev%:}
125			_ps=$_size
126
127			if [[ $_pp = $ROOTDEV ]]; then
128				echo "$ROOTDEV /" >$FILESYSTEMS
129				continue
130			elif [[ $_pp = $SWAPDEV || $_type = swap ]]; then
131				echo "$_pp" >>$SWAPLIST
132				continue
133			elif [[ $_type != *BSD ]]; then
134				continue
135			fi
136
137			_partitions[$_i]=$_pp
138			_psizes[$_i]=$_ps
139
140			# Set _mount_points[$_i].
141			if [[ -f /tmp/fstab.$DISK ]]; then
142				while read _pp _mp _rest; do
143					[[ $_pp = "/dev/${_partitions[$_i]}" ]] || continue
144					# Ignore mount points that have already been specified.
145					[[ -n $(grep " $_mp\$" $FILESYSTEMS) ]] && break
146					isin $_mp ${_mount_points[*]} && break
147					# Ignore '/' for any partition but ROOTDEV. Check just
148					# in case ROOTDEV isn't first partition processed.
149					[[ $_mp = '/' ]] && break
150					# Otherwise, record user specified mount point.
151					_mount_points[$_i]=$_mp
152				done </tmp/fstab.$DISK
153			fi
154			let _i++
155		done </tmp/disklabel.$DISK
156
157		if [[ $DISK = $ROOTDISK && -z $(grep "^$ROOTDEV /$" $FILESYSTEMS) ]]; then
158			echo "ERROR: No root partition ($ROOTDEV)."
159			DISK=
160			continue
161		fi
162
163		# If there are no BSD partitions go on to next disk.
164		(( ${#_partitions[*]} > 0 )) || continue
165
166		# Now prompt the user for the mount points.
167		_i=0
168		while :; do
169			_pp=${_partitions[$_i]}
170			_ps=$(( ${_psizes[$_i]} / 2 ))
171			_mp=${_mount_points[$_i]}
172
173			# Get the mount point from the user
174			ask "Mount point for ${_pp} (size=${_ps}k)? (or 'none' or 'done')" "$_mp"
175			case $resp in
176			"")	;;
177			none)	_mp=
178				;;
179			done)	break
180				;;
181			/*)	set -- $(grep " $resp\$" $FILESYSTEMS)
182				_pp=$1
183				if [[ -z $_pp ]]; then
184					# Mount point wasn't specified on a
185					# previous disk. Has it been specified
186					# on this one?
187					_j=0
188					for _pp in ${_partitions[*]} ""; do
189						if [[ $_i -ne $_j ]]; then
190							[[ $resp = ${_mount_points[$_j]} ]] && break
191						fi
192						let _j++
193					done
194				fi
195				if [[ -n $_pp ]]; then
196					echo "Invalid response: $_pp is already being mounted at $resp."
197					continue
198				fi
199				_mp=$resp
200				;;
201			*)	echo "Invalid response: mount point must be an absolute path!"
202				continue
203				;;
204			esac
205
206			_mount_points[$_i]=$_mp
207
208			(( ++_i < ${#_partitions[*]} )) || _i=0
209		done
210
211		# Append mount information to $FILESYSTEMS
212		_i=0
213		for _pp in ${_partitions[*]}; do
214			_mp=${_mount_points[$_i]}
215			[ "$_mp" ] && echo "$_pp $_mp" >>$FILESYSTEMS
216			let _i++
217		done
218	done
219
220	cat <<__EOT
221
222MirBSD filesystems:
223$(<$FILESYSTEMS)
224
225The next step *DESTROYS* all existing data on these partitions!
226__EOT
227
228	ask_yn "Are you really sure that you're ready to proceed?"
229	[[ $resp = n ]] && { echo "Ok, try again later." ; exit ; }
230
231	# Read $FILESYSTEMS, creating a new filesystem on each listed
232	# partition and saving the partition and mount point information
233	# for subsequent sorting by mount point.
234	_i=0
235	unset _partitions _mount_points
236	while read _pp _mp; do
237		_OPT=
238		[[ $_mp = / ]] && _OPT=$MDROOTFSOPT
239		newfs -q $_OPT /dev/r$_pp
240
241		_partitions[$_i]=$_pp
242		_mount_points[$_i]=$_mp
243		let _i++
244	done <$FILESYSTEMS
245
246	# Write fstab entries to /tmp/fstab in mount point alphabetic
247	# order to enforce a rational mount order.
248	for _mp in $(bsort ${_mount_points[*]}); do
249		_i=0
250		for _pp in ${_partitions[*]}; do
251			if [ "$_mp" = "${_mount_points[$_i]}" ]; then
252				echo -n "/dev/$_pp $_mp ffs rw,softdep"
253				# Only '/' is neither nodev nor nosuid. i.e.
254				# it can obviously *always* contain devices or
255				# setuid programs.
256				#
257				# Every other mounted filesystem is nodev. If
258				# the user chooses to mount /dev as a separate
259				# filesystem, then on the user's head be it.
260				#
261				# The only directories that install puts suid
262				# binaries into (as of 3.2) are:
263				#
264				# /sbin
265				# /usr/bin
266				# /usr/sbin
267				# /usr/libexec
268				# /usr/libexec/auth
269				# /usr/X11R6/bin
270				#
271				# and ports and users can do who knows what
272				# to /usr/local and sub directories thereof.
273				#
274				# So try to ensure that only filesystems that
275				# are mounted at or above these directories
276				# can contain suid programs. In the case of
277				# /usr/libexec, give blanket permission for
278				# subdirectories.
279				if [[ $_mp = / ]]; then
280					# / can hold devices and suid programs.
281					echo " 1 1"
282				else
283					# No devices anywhere but /.
284					echo -n ",nodev"
285					case $_mp in
286					# A few directories are allowed suid.
287					/sbin|/usr)			;;
288					/usr/bin|/usr/sbin)		;;
289					/usr/libexec|/usr/libexec/*)	;;
290					/usr/local|/usr/local/*)	;;
291					/usr/X11R6|/usr/X11R6/bin)	;;
292					# But all others are not.
293					*)	echo -n ",nosuid"	;;
294					esac
295					echo " 1 2"
296				fi
297			fi
298			let _i++
299		done
300	done >>/tmp/fstab
301
302	# Append all non-default swap devices to fstab.
303	while read _dev; do
304		[[ $_dev = $SWAPDEV ]] || \
305			echo "/dev/$_dev none swap sw 0 0" >>/tmp/fstab
306	done <$SWAPLIST
307
308	munge_fstab
309fi
310
311mount_fs "-o async"
312
313# Set hostname.
314#
315# Use existing hostname (short form) as the default value because we could
316# be restarting an install.
317#
318# Don't ask for, but don't discard, domain information provided by the user.
319#
320# Only apply the new value if the new short form name differs from the existing
321# one. This preserves any existing domain information in the hostname.
322ask_until "\nSystem hostname? (short form, e.g. 'foo')" "$(hostname -s)"
323[[ ${resp%%.*} != $(hostname -s) ]] && hostname $resp
324
325# If the network is already running, preserve resolv.conf
326if [[ -f /etc/ssh/ssh_host_rsa_key ]]; then
327	cat /etc/resolv.conf >/etc/resolv.conf.tmp 2>/dev/null
328	if [[ -s /etc/resolv.conf.tmp ]]; then
329		rm /etc/resolv.conf
330		mv /etc/resolv.conf.tmp /etc/resolv.conf
331	else
332		rm /etc/resolv.conf.tmp
333	fi
334fi
335
336# Remove existing network configuration files in /tmp to ensure they don't leak
337# onto the installed system in the case of a restarted install. Any information
338# contained within them should be accessible via ifconfig, hostname, route,
339# etc.
340( cd /tmp; rm -f host* my* resolv.* dhclient.* )
341
342# Always create new hosts file.
343cat >/tmp/hosts <<__EOT
344::1 localhost ip6-localhost
345127.0.0.1 localhost ip4-localhost
346::1 $(hostname -s)
347127.0.0.1 $(hostname -s)
348__EOT
349
350if [[ -f /etc/ssh/ssh_host_rsa_key ]]; then
351	echo "Since sshd(8) is running, I assume you already have" \
352	    "set up the network."
353	manual_net_cfg
354else
355	ask_yn "Configure the network?" yes
356	[[ $resp = y ]] && donetconfig
357fi
358
359install_sets
360
361# Remount all filesystems in /etc/fstab with the options from /etc/fstab, i.e.
362# without any options such as async which may have been used in the first
363# mount.
364while read _dev _mp _fstype _opt _rest; do
365	mount -u -o $_opt $_dev $_mp ||	exit
366done </etc/fstab
367
368# Handle questions...
369questions
370
371# Create initial user as root replacement
372cat <<EOF
373We will now create a user account on your system, which you can then
374use to log in and work with the system, as well as do administrative
375tasks using sudo(8). The newly created user account will be added to
376the class 'staff', and the group 'wheel' for being able to use sudo,
377as well as 'wsrc' and 'staff'. You might want to add yourself to the
378groups 'operator', 'audio', etc. manually later. eMail for root will
379be forwarded to this user as well.
380EOF
381_oifs=$IFS
382IFS=; _rootuser=; full=; _rootuid=3000
383while :; do
384	ask_until "User name?" $_rootuser
385	_rootuser=$resp
386	ask "Full name?" $full
387	full=$resp
388	ask "User ID?" $_rootuid
389	let _rootuid=$resp
390	askpass "Password? (will not echo)"
391	_password=$resp
392	askpass "Password? (again)"
393
394	if (( (_rootuid < 1000) || (_rootuid > 32765) )); then
395		print UID mismatch, must be between 1000 and 32765.
396	elif [[ $resp != $_password ]]; then
397		print Passwords do not match.
398	elif [[ $_rootuser != @([a-z])*([a-z0-9]) ]]; then
399		print Username is not alphanumeric.
400	elif [[ $full = *:* ]]; then
401		print Full name contains a colon.
402	else
403		ask_yn "Everything ok?"
404		[[ $resp = y ]] && break
405	fi
406done
407IFS=$_oifs
408_rootline=":$_rootuid:$_rootuid:staff:0:0:$full:/home/$_rootuser:/bin/mksh"
409
410set_timezone
411
412echo -n "Saving configuration files..."
413
414# Save any leases obtained during install.
415( cd /var/db
416[ -f dhclient.leases ] && mv dhclient.leases /mnt/var/db/. )
417
418# Move configuration files from /tmp to /mnt/etc.
419( cd /tmp
420hostname >myname
421
422# Add FQDN to /tmp/hosts entries, changing lines of the form '1.2.3.4 hostname'
423# to '1.2.3.4 hostname.$FQDN hostname'. Leave untouched any lines containing
424# domain information or aliases. The user added those manually.
425_dn=$(get_fqdn)
426while read _addr _hn _aliases; do
427	if [[ -n $_aliases || $_hn != ${_hn%%.*} || -z $_dn ]]; then
428		echo "$_addr $_hn $_aliases"
429	else
430		echo "$_addr $_hn.$_dn $_hn"
431	fi
432done <hosts >hosts.new
433mv hosts.new hosts
434
435# Prepend interesting comments from installed hosts and dhclient.conf files
436# to /tmp/hosts and /tmp/dhclient.conf.
437save_comments hosts
438save_comments dhclient.conf
439
440# Possible files: fstab, myname, sysctl.conf
441#                 dhclient.conf resolv.conf resolv.conf.tail
442#		  hostname.* hosts
443for _f in fstab my* *.conf *.tail host* ttys; do
444	[[ -f $_f ]] && mv $_f /mnt/etc/.
445done )
446
447[[ -s /tmp/kbdtype ]] && \
448    print keyboard.encoding=$(</tmp/kbdtype) >>/mnt/etc/wsconsctl.conf
449
450# calculate mfs size based on total hardware memory size, and set it
451# to 0 if <60 MB RAM, or if something is mounted on/below /tmp already
452integer avmem=$(sysctl -n hw.usermem)
453(( avmem = avmem > 250000000 ? 620000 : avmem > 120000000 ? 300000 : \
454    avmem > 60000000 ? 120000 : 0 ))
455while read type dir rest; do
456	[[ $dir = /tmp@(|/*) ]] || continue
457	avmem=0
458	break
459done </mnt/etc/fstab
460# amend target fstab by kernfs, mfs (BSD) / sysfs, tmpfs (Linux)
461# and procfs (both)
462cat >>/mnt/etc/fstab <<__EOF
463kern /kern kernfs rw,noauto 0 0
464proc /proc procfs rw,linux 0 0
465__EOF
466(( avmem )) && cat >>/mnt/etc/fstab <<__EOF
467swap /tmp mfs rw,-s$avmem 0 0
468__EOF
469# mount the mfs *now* in case we chroot /mnt after install
470(( avmem )) && mount_mfs -s $avmem swap /mnt/tmp
471
472# Generate initial user
473ed -s /mnt/etc/master.passwd <<EOF
474\$i
475$_rootuser:$(/mnt/usr/bin/encrypt -b 8 -- "$_password")$_rootline
476.
477wq
478EOF
479ed -s /mnt/etc/group <<EOF
480/^wheel:/s/\$/,$_rootuser/
481/^wsrc:/s/\$/$_rootuser/
482/^staff:/s/\$/,$_rootuser/
483\$i
484$_rootuser:*:$_rootuid:
485.
486wq
487EOF
488print '/^. root:$/'"s//root:\t\t$_rootuser/\nwq" | ed -s /mnt/etc/mail/aliases
489cp -r /mnt/etc/skel /mnt/home/$_rootuser
490chmod 711 /mnt/home/$_rootuser
491chown -R $_rootuid:$_rootuid /mnt/home/$_rootuser
492/mnt/usr/sbin/pwd_mkdb -pd /mnt/etc master.passwd
493
494if test -e /allow-vbox; then
495	echo WARNING! Allowing booting into the installed system with
496	echo WirrtualBox, which is not supported, buggy and often broken!
497	echo Continue doing so at your own risk and do not expect any support!
498	:>/mnt/etc/allow-vbox
499fi
500cat >/mnt/etc/rc.once <<-'EOF'
501	export TZ=UTC PATH=/bin:/usr/bin:/sbin:/usr/sbin
502	cd /
503	# lock to prevent double-runs
504	print -n postinstall run-once >/var/run/cron.maintenance
505	print starting postinstall script | logger -t rc.once
506	(date; exec ftp -mvo /dev/arandom \
507	    https://call.mirbsd.org/rn.cgi?runonce,whoami=$(uname -a | \
508	    tr ' ' '_'),seed=$(dd if=/dev/arandom bs=57 count=1 | \
509	    b64encode -r - | tr '+=/' '._-')) >/dev/wrandom 2>&1 &
510	[[ -x /usr/libexec/ekeyrng ]] && /usr/libexec/ekeyrng
511	newaliases 2>&1 | logger -t rc.once
512	# back up base configuration, dotfiles, etc.
513	(for f in .* var/anoncvs/.*; do
514		[[ -f $f ]] && print -r -- "$f"
515	done; find etc var/anoncvs/etc var/cron var/www/conf) | sort | \
516	    cpio -oC512 -Hustar -Mset | gzip -n9 >/var/backups/_basecfg.tgz
517	sync
518	sleep 1
519	( (
520		print running daily, weekly and monthly cronjobs
521		mksh /etc/cronrun -n daily
522		mksh /etc/cronrun -n weekly
523		mksh /etc/cronrun -n monthly
524		wait
525		print done, cleaning up
526		sync
527		rm -f /var/run/cron.maintenance /etc/rc.once
528	) 2>&1 | logger -t rc.once ) &
529	sleep 3
530EOF
531
532echo -n "done.\nGenerating initial host.random file..."
533( ( dd if=/dev/prandom bs=64 count=1; \
534    dd if=/dev/arandom bs=64 count=8; \
535    dd if=/dev/urandom bs=64 count=54; \
536  ) 2>/dev/wrandom | dd of=/mnt/var/db/host.random; \
537    chown 0:0 /mnt/var/db/host.random; \
538    chmod 600 /mnt/var/db/host.random) \
539    >/dev/wrandom 2>&1
540echo "done."
541
542# Perform final steps common to both an install and an upgrade.
543finish_up
544
545# Since the finishing takes some time, append one little block here.
546dd if=/dev/urandom bs=64 count=1 >>/mnt/var/db/host.random 2>/dev/wrandom
547