1#!/bin/ksh 2# $OpenBSD: install.sub,v 1.1154 2020/05/26 16:21:00 florian Exp $ 3# 4# Copyright (c) 1997-2015 Todd Miller, Theo de Raadt, Ken Westerback 5# Copyright (c) 2015, Robert Peichaer <rpe@openbsd.org> 6# 7# All rights reserved. 8# 9# Redistribution and use in source and binary forms, with or without 10# modification, are permitted provided that the following conditions 11# are met: 12# 1. Redistributions of source code must retain the above copyright 13# notice, this list of conditions and the following disclaimer. 14# 2. Redistributions in binary form must reproduce the above copyright 15# notice, this list of conditions and the following disclaimer in the 16# documentation and/or other materials provided with the distribution. 17# 18# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28# 29# Copyright (c) 1996 The NetBSD Foundation, Inc. 30# All rights reserved. 31# 32# This code is derived from software contributed to The NetBSD Foundation 33# by Jason R. Thorpe. 34# 35# Redistribution and use in source and binary forms, with or without 36# modification, are permitted provided that the following conditions 37# are met: 38# 1. Redistributions of source code must retain the above copyright 39# notice, this list of conditions and the following disclaimer. 40# 2. Redistributions in binary form must reproduce the above copyright 41# notice, this list of conditions and the following disclaimer in the 42# documentation and/or other materials provided with the distribution. 43# 44# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 45# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 46# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 47# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 48# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 49# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 50# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 51# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 52# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 53# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 54# POSSIBILITY OF SUCH DAMAGE. 55# 56 57# OpenBSD install/upgrade script common subroutines and initialization code 58 59# ------------------------------------------------------------------------------ 60# Misc functions 61# ------------------------------------------------------------------------------ 62 63# Print error message to stderr and exit the script. 64err_exit() { 65 print -u2 -- "$*" 66 exit 1 67} 68 69# Show usage of the installer script and exit. 70usage() { 71 err_exit "usage: ${0##*/} [-ax] [-f filename] [-m install | upgrade]" 72} 73 74# Wait for the ftp(1) process started in start_cgiinfo() to end and extract 75# various informations from the ftplist.cgi output. 76wait_cgiinfo() { 77 local _l _s _key _val 78 79 wait "$CGIPID" 2>/dev/null 80 81 # Ensure, there is actual data to extract info from. 82 [[ -s $CGI_INFO ]] || return 83 84 # Extract the list of mirror servers. 85 sed -En 's,^https?://([[A-Za-z0-9:_][]A-Za-z0-9:._-]*),\1,p' \ 86 $CGI_INFO >$HTTP_LIST 2>/dev/null 87 88 # Extract the previously selected mirror server (first entry in the 89 # ftplist.cgi output, if that has no location info). 90 read -r -- _s _l <$HTTP_LIST 91 [[ -z $_l ]] && : ${HTTP_SERVER:=${_s%%/*}} 92 93 # Extract the previously used install method, timezone information 94 # and a reference timestamp. 95 while IFS='=' read -r -- _key _val; do 96 case $_key=$_val in 97 method=+([a-z])*([0-9])) CGI_METHOD=$_val;; 98 TIME=+([0-9])) CGI_TIME=$_val;; 99 TZ=+([-_/+[:alnum:]])) CGI_TZ=$_val;; 100 esac 101 done <$CGI_INFO 102} 103 104 105# ------------------------------------------------------------------------------ 106# Utils functions 107# ------------------------------------------------------------------------------ 108 109# Sort and print unique list of provided arguments. 110bsort() { 111 local _a=$1 _b _l 112 113 (($#)) && shift || return 114 115 for _b; do 116 [[ $_a == "$_b" ]] && continue 117 if [[ $_a > $_b ]]; then 118 _l="$_a $_l" _a=$_b 119 else 120 _l="$_b $_l" 121 fi 122 done 123 124 # Output the smallest value found. 125 (($#)) && echo -n "$_a " || echo -n "$_a" 126 127 # Sort remaining values. 128 bsort $_l 129} 130 131# Test the first argument against the remaining ones, return success on a match. 132isin() { 133 local _a=$1 _b 134 135 shift 136 for _b; do 137 [[ $_a == "$_b" ]] && return 0 138 done 139 return 1 140} 141 142# Add first argument to list formed by the remaining arguments. 143# Adds to the tail if the element does not already exist. 144addel() { 145 local _a=$1 146 147 shift 148 isin "$_a" $* && echo -n "$*" || echo -n "${*:+$* }$_a" 149} 150 151# Remove all occurrences of first argument from list formed by the remaining 152# arguments. 153rmel() { 154 local _a=$1 _b _c 155 156 shift 157 for _b; do 158 [[ $_a != "$_b" ]] && _c="${_c:+$_c }$_b" 159 done 160 echo -n "$_c" 161} 162 163# If possible, print the timestamp received from the ftplist.cgi output, 164# adjusted with the time elapsed since it was received. 165http_time() { 166 local _sec=$(cat $HTTP_SEC 2>/dev/null) 167 168 [[ -n $_sec && -n $CGI_TIME ]] && 169 echo $((CGI_TIME + SECONDS - _sec)) 170} 171 172# Prints the supplied parameters properly escaped for future sh/ksh parsing. 173# Quotes are added if needed, so you should not do that yourself. 174quote() ( 175 # Since this is a subshell we won't pollute the calling namespace. 176 for _a; do 177 alias Q=$_a; _a=$(alias Q); print -rn -- " ${_a#Q=}" 178 done | sed '1s/ //' 179 echo 180) 181 182# Show a list of ordered arguments (read line by line from stdin) in column 183# output using ls. 184show_cols() { 185 local _l _cdir=/tmp/i/cdir _clist 186 187 mkdir -p $_cdir 188 rm -rf -- $_cdir/* 189 while read _l; do 190 [[ -n $_l ]] || continue 191 mkdir -p /tmp/i/cdir/"$_l" 192 _clist[${#_clist[*]}]="$_l" 193 done 194 (cd $_cdir; ls -Cdf "${_clist[@]}") 195 rm -rf -- $_cdir 196} 197 198# Echo file $1 to stdout. Skip comment lines. Strip leading and trailing 199# whitespace if IFS is set. 200stripcom() { 201 local _file=$1 _line 202 203 [[ -f $_file ]] || return 204 205 set -o noglob 206 while read _line; do 207 [[ -n ${_line%%#*} ]] && echo $_line 208 done <$_file 209 set +o noglob 210} 211 212# Create a temporary directory based on the supplied directory name prefix. 213tmpdir() { 214 local _i=1 _dir 215 216 until _dir="${1?}.$_i.$RANDOM" && mkdir -- "$_dir" 2>/dev/null; do 217 ((++_i < 10000)) || return 1 218 done 219 echo "$_dir" 220} 221 222# Generate unique filename based on the supplied filename $1. 223unique_filename() { 224 local _fn=$1 _ufn 225 226 while _ufn=${_fn}.$RANDOM && [[ -e $_ufn ]]; do done 227 print -- "$_ufn" 228} 229 230# Let rc.firsttime feed file $1 using $2 as subject to whatever mail system we 231# have at hand by then. 232prep_root_mail() { 233 local _fn=$1 _subject=$2 _ufn 234 235 [[ -s $_fn ]] || return 236 237 _ufn=$(unique_filename /mnt/var/log/${_fn##*/}) 238 cp $_fn $_ufn 239 chmod 600 $_ufn 240 _ufn=${_ufn#/mnt} 241 242 cat <<__EOT >>/mnt/etc/rc.firsttime 243( /usr/bin/mail -s '$_subject' root <$_ufn && rm $_ufn ) >/dev/null 2>&1 & 244__EOT 245} 246 247# Examine the contents of the DHCP lease file $1 for a line containing the 248# field provided as parameters and return the value of the first field found. 249# 250# Note that strings are unescaped but not unvis()'d. 251lease_value() { 252 local _lf=$1 _o 253 254 [[ -s $_lf ]] || return 255 shift 256 257 for _o; do 258 sed -E \ 259 -e '/^ *(option )?'"$_o"' (.*);$/!d;s//\2/' \ 260 -e '/^"(.*)"$/{s//\1/;s/\\(.)/\1/g;};q' "$_lf" \ 261 | grep ^ && return 262 done 263} 264 265# Extract one boot's worth of dmesg. 266dmesgtail() { 267 dmesg | sed -n 'H;/^OpenBSD/h;${g;p;}' 268} 269 270# ------------------------------------------------------------------------------ 271# Device related functions 272# ------------------------------------------------------------------------------ 273 274# Show device name, info, NAA and size for the provided list of disk devices. 275# Create device nodes as needed and cleanup afterwards. 276diskinfo() { 277 local _d _i _n _s 278 279 for _d; do 280 # Extract disk information enclosed in <> from dmesg. 281 _i=$(dmesg | sed -n '/^'$_d' at /h;${g;s/^.*<\(.*\)>.*$/\1/p;}') 282 _i=${_i##+([[:space:],])} 283 _i=${_i%%+([[:space:],])} 284 285 # Extract Network Address Authority information from dmesg. 286 _n=$(dmesg | sed -En '/^'$_d' at /h;${g;s/^.* ([a-z0-9]+\.[a-zA-Z0-9_]+)$/\1/p;}') 287 288 # Extract disk size from disklabel output. 289 make_dev $_d 290 _s=$(disklabel -dpg $_d 2>/dev/null | sed -n '/.*# total bytes: \(.*\)/{s//(\1)/p;}') 291 rm -f /dev/{r,}$_d? 292 293 echo "$_d: $_i $_n $_s" 294 done 295} 296 297# Create devices passed as arguments. 298make_dev() { 299 [[ -z $(cd /dev && sh MAKEDEV "$@" 2>&1) ]] 300} 301 302# Sort and print information from dmesg.boot using sed expression $1. 303scan_dmesg() { 304 bsort $(sed -n "$1" /var/run/dmesg.boot) 305} 306 307# Extract device names from hw.disknames matching sed expression $1. 308scan_disknames() { 309 local IFS=, _disks=$(sysctl -n hw.disknames) 310 311 bsort $(for _n in $_disks; do echo "${_n%%:*} "; done | sed -n "$1") 312} 313 314# Return disk devices found in hw.disknames. 315get_dkdevs() { 316 echo $(scan_disknames "${MDDKDEVS:-/^[sw]d[0-9][0-9]* /s/ .*//p}") 317} 318 319# Return CDROM devices found in hw.disknames. 320get_cddevs() { 321 echo $(scan_disknames "${MDCDDEVS:-/^cd[0-9][0-9]* /s/ .*//p}") 322} 323 324# Return sorted list of disks not in DISKS_DONE which contains disks already 325# initialized during installation. 326get_dkdevs_uninitialized() { 327 local _disks=$(get_dkdevs) _d 328 329 for _d in $DISKS_DONE; do 330 _disks=$(rmel "$_d" $_disks) 331 done 332 bsort $_disks 333} 334 335# Return list of valid root disks 336get_dkdevs_root() { 337 local _disks=$(get_dkdevs) _d 338 339 if [[ $MODE == upgrade ]]; then 340 for _d in $_disks; do 341 is_rootdisk "$_d" || _disks=$(rmel "$_d" $_disks) 342 done 343 fi 344 echo -n $_disks 345} 346 347# Return list of all network devices, optionally limited by parameters to 348# ifconfig. Filter out dynamically created network pseudo-devices except vlan. 349get_ifs() { 350 local _if _if_list=$(rmel vlan $(ifconfig -C)) 351 352 for _if in $(ifconfig "$@" 2>/dev/null | sed '/^[a-z]/!d;s/:.*//'); do 353 isin "${_if%%+([0-9])}" $_if_list || echo $_if 354 done 355} 356 357# Return the device name of the disk device $1, which may be a disklabel UID. 358get_dkdev_name() { 359 local _dev=${1#/dev/} _d 360 361 _dev=${_dev%.[a-p]} 362 ((${#_dev} < 16)) && _dev=${_dev%[a-p]} 363 local IFS=, 364 for _d in $(sysctl -n hw.disknames); do 365 [[ $_dev == @(${_d%:*}|${_d#*:}) ]] && echo ${_d%:*} && break 366 done 367} 368 369# Inspect disk $1 if it has a partition-table of type $2 and optionally 370# if it has a partition of type $3. 371disk_has() { 372 local _disk=$1 _pttype=$2 _part=$3 _cmd _p_pttype _p_part 373 374 [[ -n $_disk && -n $_pttype ]] || exit 375 376 # Commands to inspect disk. Default: "fdisk $_disk" 377 local _c_hfs="pdisk -l $_disk" 378 local _c_sr="bioctl -q $_disk" 379 380 # Patterns for partition-table-types and partition-types. 381 local _p_gpt='Usable LBA:' 382 local _p_gpt_openbsd='^[ *]...: OpenBSD ' 383 local _p_gpt_efisys='^[ *]...: EFI Sys ' 384 local _p_hfs='^Partition map ' 385 local _p_hfs_openbsd=' OpenBSD OpenBSD ' 386 local _p_mbr='Signature: 0xAA55' 387 local _p_mbr_openbsd='^..: A6 ' 388 local _p_mbr_dos='^..: 06 ' 389 local _p_mbr_dos_active='^\*.: 06 ' 390 local _p_mbr_linux='^..: 83 ' 391 local _p_sr='OPENBSD, SR' 392 local _p_sr_crypto='OPENBSD, SR CRYPTO' 393 394 # Compose command and patterns based on the parameters. 395 eval "_cmd=\"\$_c_${_pttype}\"" 396 eval "_p_pttype=\"\$_p_${_pttype}\"" 397 eval "_p_part=\"\$_p_${_pttype}_${_part}\"" 398 399 # Set the default command if none was defined before. 400 _cmd=${_cmd:-fdisk $_disk} 401 402 # Abort in case of undefined patterns. 403 [[ -z $_p_pttype ]] && exit 404 [[ -n $_part && -z $_p_part ]] && exit 405 406 if [[ -z $_p_part ]]; then 407 $_cmd 2>/dev/null | grep -Eq "$_p_pttype" 408 else 409 $_cmd 2>/dev/null | grep -Eq "$_p_pttype" && 410 $_cmd 2>/dev/null | grep -Eq "$_p_part" 411 fi 412} 413 414# Handle disklabel auto-layout for the root disk $1 during interactive install 415# and autopartitioning during unattended install by asking for and downloading 416# autopartitioning template. Write the resulting fstab to $2. Abort unattended 417# installation if autopartitioning fails. 418disklabel_autolayout() { 419 local _disk=$1 _f=$2 _dl=/tmp/i/disklabel.auto _op _qst 420 421 # Skip disklabel auto-layout for any disk except the root disk. 422 [[ $_disk != $ROOTDISK ]] && return 423 424 while $AI; do 425 ask "URL to autopartitioning template for disklabel?" none 426 [[ $resp == none ]] && break 427 if ! $FTP_TLS && [[ $resp == https://* ]]; then 428 err_exit "https not supported on this platform." 429 fi 430 echo "Fetching $resp" 431 if unpriv ftp -Vo - "$resp" >$_dl && [[ -s $_dl ]]; then 432 disklabel -T $_dl -F $_f -w -A $_disk && return 433 err_exit "Autopartitioning failed." 434 else 435 err_exit "No autopartitioning template found." 436 fi 437 done 438 439 _qst="Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout?" 440 while :; do 441 echo "The auto-allocated layout for $_disk is:" 442 disklabel -h -A $_disk | egrep "^# |^ [a-p]:" 443 ask "$_qst" a 444 case $resp in 445 [aA]*) _op=-w;; 446 [eE]*) _op=-E;; 447 [cC]*) return 0;; 448 *) continue;; 449 esac 450 disklabel -F $_f $_op -A $_disk 451 return 452 done 453} 454 455# Create a partition table and configure the partition layout for disk $1. 456configure_disk() { 457 local _disk=$1 _fstab=/tmp/i/fstab.$1 _opt 458 459 make_dev $_disk || return 460 461 # Deal with disklabels, including editing the root disklabel 462 # and labeling additional disks. This is machine-dependent since 463 # some platforms may not be able to provide this functionality. 464 # /tmp/i/fstab.$_disk is created here with 'disklabel -F'. 465 rm -f /tmp/i/*.$_disk 466 md_prep_disklabel $_disk || return 467 468 # Make sure a '/' mount point exists on the root disk. 469 if ! grep -qs ' / ffs ' /tmp/i/fstab.$ROOTDISK; then 470 echo "'/' must be configured!" 471 $AI && exit 1 || return 1 472 fi 473 474 if [[ -f $_fstab ]]; then 475 # Avoid duplicate mount points on different disks. 476 while read _pp _mp _rest; do 477 # Multiple swap partitions are ok. 478 if [[ $_mp == none ]]; then 479 echo "$_pp $_mp $_rest" >>/tmp/i/fstab 480 continue 481 fi 482 # Non-swap mountpoints must be in only one file. 483 if [[ $_fstab != $(grep -l " $_mp " /tmp/i/fstab.*) ]]; then 484 _rest=$_disk 485 _disk= 486 break 487 fi 488 done <$_fstab 489 490 # Duplicate mountpoint. 491 if [[ -z $_disk ]]; then 492 # Allow disklabel(8) to read back mountpoint info 493 # if it is immediately run against the same disk. 494 cat /tmp/i/fstab.$_rest >/etc/fstab 495 rm /tmp/i/fstab.$_rest 496 497 set -- $(grep -h " $_mp " /tmp/i/fstab.*[0-9]) 498 echo "$_pp and $1 can't both be mounted at $_mp." 499 $AI && exit 1 || return 1 500 fi 501 502 # Add ffs filesystems to list after newfs'ing them. Ignore 503 # other filesystems. 504 while read _pp _mp _fstype _rest; do 505 [[ $_fstype == ffs ]] || continue 506 507 # Default to FFS2 unless otherwise directed. 508 if [[ $_mp == / ]]; then 509 _opt=${MDROOTFSOPT:--O2} 510 else 511 _opt=${MDFSOPT:--O2} 512 fi 513 newfs -q $_opt ${_pp##/dev/} 514 515 # N.B.: '!' is lexically < '/'. 516 # That is required for correct sorting of mount points. 517 FSENT="$FSENT $_mp!$_pp" 518 done <$_fstab 519 fi 520 521 return 0 522} 523 524# ------------------------------------------------------------------------------ 525# Functions for the dmesg listener 526# ------------------------------------------------------------------------------ 527 528# Acquire lock. 529lock() { 530 while ! mkdir /tmp/i/lock 2>/dev/null && sleep .1; do done 531} 532 533# Release lock. 534unlock() { 535 rm -df /tmp/i/lock 2>/dev/null 536} 537 538# Add a trap to kill the dmesg listener co-process on exit of the installer. 539retrap() { 540 trap 'kill -KILL $CPPID 2>/dev/null; echo; stty echo; exit 0' \ 541 INT EXIT TERM 542} 543 544# Start a listener process looking for dmesg changes which indicates a possible 545# plug-in/-out of devices (e.g. usb disks, cdroms, etc.). This is used to abort 546# and redraw question prompts, especially in ask_which(). 547start_dmesg_listener() { 548 local _update=/tmp/i/update 549 550 # Ensure the lock is initially released and that no update files exists. 551 unlock 552 rm -f $_update 553 554 # Do not start the listener if in non-interactive mode. 555 $AI && return 556 557 # To ensure that only one dmesg listener instance can be active, run it 558 # in a co-process subshell of which there can always only be one active. 559 ( 560 while :; do 561 lock 562 # The dmesg listener will continuously check for the existence of 563 # the update file and sends a signal to the parent process (that 564 # is the installer script) if the dmesg output differs from the 565 # contents of that file. 566 if [[ -e $_update && "$(dmesgtail)" != "$(<$_update)" ]]; then 567 dmesgtail >$_update 568 kill -TERM 2>/dev/null $$ || exit 1 569 fi 570 unlock 571 sleep .5 572 done 573 ) |& 574 575 # Save the co-process PID in a global variable so it can be used in 576 # the retrap() function which adds a trap to kill the co-process on 577 # exit of the installer script. 578 CPPID=$! 579 retrap 580} 581 582# ------------------------------------------------------------------------------ 583# Functions to ask (or auto-answer) questions 584# ------------------------------------------------------------------------------ 585 586# Log installer questions and answers so that the resulting file can be used as 587# response file for an unattended install/upgrade. 588log_answers() { 589 if [[ -n $1 && -n $2 ]]; then 590 print -r -- "${1%%'?'*} = $2" >>/tmp/i/$MODE.resp 591 fi 592} 593 594# Fetch response file for autoinstall. 595get_responsefile() { 596 local _rf _if _lf _path _aifile 597 export AI_HOSTNAME= AI_MAC= AI_MODE= AI_SERVER= 598 599 [[ -f /auto_upgrade.conf ]] && _rf=/auto_upgrade.conf AI_MODE=upgrade 600 [[ -f /auto_install.conf ]] && _rf=/auto_install.conf AI_MODE=install 601 [[ -f $_rf ]] && cp $_rf /tmp/ai/ai.$AI_MODE.conf && return 602 603 for _if in ''; do 604 [[ -x /sbin/dhclient ]] || break 605 606 # Select a network interface for initial dhcp request. 607 # Prefer the interface the system netbooted from. 608 set -- $(get_ifs netboot) 609 (($# == 0)) && set -- $(get_ifs) 610 (($# == 1)) && _if=$1 611 612 # Ask if multiple were found and system was not netbooted. 613 while (($# > 1)); do 614 ask_which "network interface" \ 615 "should be used for the initial DHCP request" \ 616 "$*" 617 isin "$resp" $* && _if=$resp && break 618 done 619 620 # Issue initial dhcp request via the found interface. 621 [[ -n $_if ]] && dhclient $_if || break 622 _lf=/var/db/dhclient.leases.$_if 623 624 # Extract installer mode and response file path from lease file. 625 _aifile=$(lease_value $_lf filename bootfile-name) 626 [[ $_aifile == ?(*/)auto_@(install|upgrade) ]] || _aifile= 627 _path=${_aifile%auto_@(install|upgrade)} 628 AI_MODE=${_aifile##*?(/)auto_} 629 630 # Extract install server ip address from lease file. 631 AI_SERVER=$(lease_value $_lf \ 632 server-name tftp-server-name next-server) 633 634 # Prime hostname with host-name option from lease file. 635 AI_HOSTNAME=$(lease_value $_lf host-name) 636 hostname "$AI_HOSTNAME" 637 done 638 639 # Try to fetch mac-mode.conf, then hostname-mode.conf, and finally 640 # mode.conf if install server and mode are known, otherwise tell which 641 # one was missing. 642 if [[ -n $AI_SERVER && -n $AI_MODE ]]; then 643 AI_MAC=$(ifconfig $_if | sed 's/.*lladdr \(.*\)/\1/p;d') 644 for _rf in {$AI_MAC-,${AI_HOSTNAME:+$AI_HOSTNAME-,}}$AI_MODE; do 645 # Append HTTP_SETDIR as parameter to _url which can be 646 # used by the webserver to return dynamically created 647 # response files. 648 _url="http://$AI_SERVER/$_path$_rf.conf?path=$HTTP_SETDIR" 649 echo "Fetching $_url" 650 if unpriv ftp -Vo - "$_url" \ 651 >"/tmp/ai/ai.$AI_MODE.conf" 2>/dev/null; then 652 ifconfig $_if delete down 2>/dev/null 653 return 0 654 fi 655 done 656 else 657 [[ -z $AI_SERVER ]] && echo "Could not determine auto server." 658 [[ -z $AI_MODE ]] && echo "Could not determine auto mode." 659 fi 660 661 # Ask for url or local path to response file. Provide a default url if 662 # server was found in lease file. 663 while :; do 664 ask "Response file location?" \ 665 "${AI_SERVER:+http://$AI_SERVER/install.conf}" 666 [[ -n $resp ]] && _rf=$resp && break 667 done 668 669 # Ask for the installer mode only if auto-detection failed. 670 AI_MODE=$(echo "$_rf" | sed -En 's/^.*(install|upgrade).conf$/\1/p') 671 while [[ -z $AI_MODE ]]; do 672 ask "(I)nstall or (U)pgrade?" 673 [[ $resp == [iI]* ]] && AI_MODE=install 674 [[ $resp == [uU]* ]] && AI_MODE=upgrade 675 done 676 677 echo "Fetching $_rf" 678 [[ -f $_rf ]] && _rf="file://$_rf" 679 if unpriv ftp -Vo - "$_rf" >"/tmp/ai/ai.$AI_MODE.conf" 2>/dev/null; then 680 ifconfig $_if delete down 2>/dev/null 681 return 0 682 fi 683 return 1 684} 685 686# Find a response to question $1 in $AI_RESPFILE and return it via $resp. 687# Return default answer $2 if provided and none is found in the file. 688# 689# Move the existing ai.conf file to a tmp file, read from it line by line 690# and write a new ai.conf file skipping the line containing the response. 691# 692# 1) skip empty and comment lines and lines without = 693# 2) split question (_key) and answer (_val) at leftmost = 694# 3) strip leading/trailing blanks 695# 4) compare questions case insensitive (typeset -l) 696# 697_autorespond() { 698 typeset -l _q=$1 _key 699 local _def=$2 _l _val 700 701 [[ -f $AI_RESPFILE && -n $_q ]] || return 702 703 mv /tmp/ai/ai.conf /tmp/ai/ai.conf.tmp 704 while IFS=' ' read -r _l; do 705 [[ $_l == [!#=]*=?* ]] || continue 706 _key=${_l%%*([[:blank:]])=*} 707 _val=${_l##*([!=])=*([[:blank:]])} 708 [[ $_q == @(|*[[:blank:]])"$_key"@([[:blank:]?]*|) ]] && 709 resp=$_val && cat && return 710 print -r " $_l" 711 done </tmp/ai/ai.conf.tmp >/tmp/ai/ai.conf 712 [[ -n $_def ]] && resp=$_def && return 713 err_exit "\nQuestion has no answer in response file: \"$_q\"" 714} 715 716# Capture user response either by issuing an interactive read or by searching 717# the response file and store the response in the global variable $resp. 718# 719# Optionally present a question $1 and a default answer $2 shown in []. 720# 721# If the dmesg output is changed while waiting for the interactive response, 722# the current read will be aborted and the function will return a non-zero 723# value. Normally, the caller function will then reprint any prompt and call 724# the function again. 725_ask() { 726 local _q=$1 _def=$2 _int _redo=0 _pid 727 728 lock; dmesgtail >/tmp/i/update; unlock 729 echo -n "${_q:+$_q }${_def:+[$_def] }" 730 _autorespond "$_q" "$_def" && echo "$resp" && return 731 trap "_int=1" INT 732 trap "_redo=1" TERM 733 read resp 734 lock; rm /tmp/i/update; unlock 735 if ((_redo)); then 736 stty raw 737 stty -raw 738 else 739 case $resp in 740 !) echo "Type 'exit' to return to install." 741 sh 742 _redo=1 743 ;; 744 !*) eval "${resp#?}" 745 _redo=1 746 ;; 747 esac 748 fi 749 retrap 750 ((_int)) && kill -INT $$ 751 : ${resp:=$_def} 752 return $_redo 753} 754 755# Ask for user response to question $1 with an optional default answer $2. 756# Write the question and the answer to a logfile. 757ask() { 758 759 # Prompt again in case the dmesg listener detected a change. 760 while ! _ask "$1" "$2"; do done 761 log_answers "$1" "$resp" 762} 763 764# Ask the user a yes/no question $1 with 'no' as default answer unless $2 is 765# set to 'yes' and insist on 'y', 'yes', 'n' or 'no' as response. 766# Return response via $resp as 'y' with exit code 0 or 'n' with exit code 1. 767ask_yn() { 768 local _q=$1 _a=${2:-no} 769 typeset -l _resp 770 771 while :; do 772 ask "$_q" "$_a" 773 _resp=$resp 774 case $_resp in 775 y|yes) resp=y; return 0;; 776 n|no) resp=n; return 1;; 777 esac 778 echo "'$resp' is not a valid choice." 779 $AI && exit 1 780 done 781} 782 783# Ask for the user to select one value from a list, or 'done'. 784# At exit $resp holds selected item, or 'done'. 785# 786# Parameters: 787# 788# $1 = name of the list items (disk, cd, etc.) 789# $2 = question to ask 790# $3 = list of valid choices 791# $4 = default choice, if it is not specified use the first item in $3 792# 793# N.B.! $3 and $4 will be "expanded" using eval, so be sure to escape them 794# if they contain spooky stuff 795ask_which() { 796 local _name=$1 _query=$2 _list=$3 _def=$4 _dynlist _dyndef _key _q 797 _key=$(echo "$_name" | sed 's/[^[:alnum:]]/_/g') 798 799 while :; do 800 eval "_dynlist=\"$_list\"" 801 eval "_dyndef=\"$_def\"" 802 803 # Clean away whitespace and determine the default. 804 set -o noglob 805 set -- $_dyndef; _dyndef="$1" 806 set -- $_dynlist; _dynlist="$*" 807 set +o noglob 808 (($# < 1)) && resp=done && return 809 : ${_dyndef:=$1} 810 811 echo "Available ${_name}s are: $_dynlist." 812 _q="Which $_name $_query?" 813 echo -n "$_q (or 'done') ${_dyndef:+[$_dyndef] }" 814 _autorespond "$_q" "${_dyndef-done}" && echo "$resp" \ 815 || _ask || continue 816 [[ -z $resp ]] && resp="$_dyndef" 817 818 # Quote $resp to prevent user from confusing isin() by 819 # entering something like 'a a'. 820 if isin "$resp" $_dynlist done; then 821 log_answers "$_q" "$resp" 822 break 823 fi 824 echo "'$resp' is not a valid choice." 825 $AI && [[ -n $AI_RESPFILE ]] && exit 1 826 done 827} 828 829# Ask for user response to question $1 with an optional default answer $2 830# until a non-empty reply is entered. 831ask_until() { 832 resp= 833 834 while :; do 835 ask "$1" "$2" 836 [[ -n $resp ]] && break 837 echo "A response is required." 838 $AI && exit 1 839 done 840} 841 842# Capture a user password and save it in $resp, optionally showing prompt $1. 843# 844# 1) *Don't* allow the '!' options that ask does. 845# 2) *Don't* echo input. 846# 3) *Don't* interpret "\" as escape character. 847# 4) Preserve whitespace in input 848# 849ask_pass() { 850 stty -echo 851 IFS= read -r resp?"$1 " 852 stty echo 853 echo 854} 855 856# Ask for a password twice showing prompt $1. Ensure both inputs are identical 857# and save it in $_password. 858ask_password() { 859 local _q=$1 860 861 if $AI; then 862 echo -n "$_q " 863 _autorespond "$_q" 864 echo '<provided>' 865 _password=$resp 866 return 867 fi 868 869 while :; do 870 ask_pass "$_q (will not echo)" 871 _password=$resp 872 873 ask_pass "$_q (again)" 874 [[ $resp == "$_password" ]] && break 875 876 echo "Passwords do not match, try again." 877 done 878} 879 880 881# ------------------------------------------------------------------------------ 882# Support functions for donetconfig() 883# ------------------------------------------------------------------------------ 884 885# Issue a DHCP request to configure interface $1 and add it to group 'dhcp' to 886# later be able to identify DHCP configured interfaces. 887dhcp_request() { 888 local _if=$1 889 890 echo "lookup file bind" >/etc/resolv.conf.tail 891 892 ifconfig $_if group dhcp >/dev/null 2>&1 893 894 if [[ -x /sbin/dhclient ]]; then 895 /sbin/dhclient $_if 896 else 897 echo "DHCP leases not available during install - no /sbin/dhclient." 898 fi 899 900 # Move resolv.conf to where it will be copied to the installed system. 901 mv /etc/resolv.conf.tail /tmp/i/resolv.conf.tail 902} 903 904# Obtain and output the inet information related to interface $1. 905# Outputs: 906# <flags>\n<addr> <netmask> <rest of inet line>[\n<more inet lines>] 907v4_info() { 908 ifconfig $1 inet | sed -n ' 909 1s/.*flags=.*<\(.*\)>.*/\1/p 910 /inet/s/netmask // 911 /.inet /s///p' 912} 913 914# Convert a netmask in hex format ($1) to dotted decimal format. 915hextodec() { 916 set -- $(echo ${1#0x} | sed 's/\(..\)/0x\1 /g') 917 echo $(($1)).$(($2)).$(($3)).$(($4)) 918} 919 920# Create an entry in the hosts file using IP address $1 and symbolic name $2. 921# Treat $1 as IPv6 address if it contains ':', otherwise as IPv4. If an entry 922# with the same name and address family already exists, delete it first. 923add_hostent() { 924 local _addr=$1 _name=$2 _delim="." 925 926 [[ -z $_addr || -z $_name ]] && return 927 928 [[ $_addr == *:* ]] && _delim=":" 929 sed -i "/^[0-9a-fA-F]*[$_delim].*[ ]$_name\$/d" \ 930 /tmp/i/hosts 2>/dev/null 931 932 echo "$_addr $_name" >>/tmp/i/hosts 933} 934 935# Configure VLAN interface $1 and create the corresponding hostname.if(5) file. 936# Ask the user what parent network interface and vnetid to use. 937vlan_config() { 938 local _if=$1 _hn=/tmp/i/hostname.$1 _hn_vd _vd _vdvi _vdvi_used _vi 939 local _sed_vdvi='s/.encap: vnetid ([[:alnum:]]+) parent ([[:alnum:]]+)/\2:\1/p' 940 941 # Use existing parent device and vnetid for this interface as default in 942 # case of a restart. 943 _vdvi=$(ifconfig $_if 2>/dev/null | sed -En "$_sed_vdvi") 944 _vd=${_vdvi%%:*} 945 _vi=${_vdvi##*:} 946 947 # Use the vlan interface minor as the default vnetid. If it's 0, set it 948 # to 'none' which equals to the default vlan. 949 if [[ $_vi == @(|none) ]]; then 950 ((${_if##vlan} == 0)) && _vi=none || _vi=${_if##vlan} 951 fi 952 953 # Use the first non vlan interface as the default parent. 954 if [[ $_vd == @(|none) ]]; then 955 _vd=$(get_ifs | sed '/^vlan/d' | sed q) 956 fi 957 958 ask "Which interface:tag should $_if be on?" "$_vd:$_vi" 959 _vd=${resp%%:*} 960 _vi=${resp##*:} 961 962 # Ensure that the given parent is an existing (non vlan) interface. 963 if ! isin "$_vd" $(get_ifs | sed '/^vlan/d'); then 964 echo "Invalid parent interface choice '$_vd'." 965 return 1 966 fi 967 968 # Get a list of parent:vnetid tuples of all configured vlan interfaces. 969 _vdvi_used=$(ifconfig vlan 2>/dev/null | sed -En "$_sed_vdvi") 970 971 # Ensure that the given vnetid is not already configured on the given 972 # parent interface. 973 for _vdvi in $_vdvi_used; do 974 if [[ $_vdvi == $_vd:* && ${_vdvi##*:} == $_vi ]]; then 975 echo "vlan tag '$_vi' already used on parent '$_vd'" 976 return 1 977 fi 978 done 979 980 # Further ensure that the given vnetid is 'none', or within 1-4095. 981 if [[ $_vi == none ]]; then 982 _vi="-vnetid" 983 elif (($_vi > 0 && $_vi < 4096)); then 984 _vi="vnetid $_vi" 985 else 986 echo "Invalid vlan tag '$_vi'." 987 return 1 988 fi 989 990 # Write the config to the hostname.if files and set proper permissions. 991 _hn_vd=/tmp/i/hostname.$_vd 992 grep -qs "^up" $_hn_vd || echo up >>$_hn_vd 993 echo "$_vi parent $_vd" >>$_hn 994 chmod 640 $_hn_vd $_hn 995 996 # Bring up the parent interface and configure the vlan interface. 997 ifconfig $_vd up 998 ifconfig $_if destroy >/dev/null 2>&1 999 ifconfig $_if create >/dev/null 2>&1 1000 ifconfig $_if $_vi parent $_vd 1001} 1002 1003# Configure IPv4 on interface $1. 1004v4_config() { 1005 local _if=$1 _name=$2 _hn=$3 _addr _mask _newaddr 1006 1007 # Set default answers based on any existing configuration. 1008 set -- $(v4_info $_if) 1009 if [[ -n $2 ]] && ! isin $_if $(get_ifs dhcp); then 1010 _addr=$2; 1011 _mask=$(hextodec $3) 1012 fi 1013 1014 # Nuke existing inet configuration. 1015 ifconfig $_if -inet 1016 ifconfig $_if -group dhcp >/dev/null 2>&1 1017 1018 while :; do 1019 ask_until "IPv4 address for $_if? (or 'dhcp' or 'none')" \ 1020 "${_addr:-dhcp}" 1021 case $resp in 1022 none) return 1023 ;; 1024 dhcp) dhcp_request $_if 1025 echo "dhcp" >>$_hn 1026 return 1027 ;; 1028 esac 1029 1030 _newaddr=$resp 1031 1032 # Ask for the netmask if the user did not use CIDR notation. 1033 if [[ $_newaddr == */* ]]; then 1034 ifconfig $_if $_newaddr up 1035 else 1036 ask_until "Netmask for $_if?" "${_mask:-255.255.255.0}" 1037 ifconfig $_if $_newaddr netmask $resp 1038 fi 1039 1040 set -- $(v4_info $_if) 1041 if [[ -n $2 ]]; then 1042 echo "inet $2 $3" >>$_hn 1043 add_hostent "$2" "$_name" 1044 return 1045 fi 1046 1047 $AI && exit 1 1048 done 1049} 1050 1051# Obtain and output the inet6 information related to interface $1. 1052# <flags>\n<addr> <prefixlen> <rest of inet6 line>[\n<more inet6 lines>] 1053v6_info() { 1054 ifconfig $1 inet6 | sed -n ' 1055 1s/.*flags=.*<\(.*\)>.*/\1/p 1056 /scopeid/d 1057 /inet6/s/prefixlen // 1058 /.inet6 /s///p' 1059} 1060 1061# Configure an IPv6 default route on interface $1 and preserve that information 1062# in the /etc/mygate file. Ask the user to either select from a list of default 1063# router candidates or to enter a router IPv6 address. 1064v6_defroute() { 1065 local _if _v6ifs _prompt _resp _routers _dr PS3 1066 1067 # Only configure a default route if an IPv6 address was manually configured. 1068 for _if in $(get_ifs); do 1069 set -- $(v6_info $_if) 1070 [[ -z $2 || $1 == *AUTOCONF6* ]] || _v6ifs="$_v6ifs $_if" 1071 done 1072 [[ -n $_v6ifs ]] || return 1073 1074 # Start with any existing default routes. 1075 _routers=$(route -n show -inet6 | 1076 sed -En 's/^default[[:space:]]+([^[:space:]]+).*/\1 /p') 1077 1078 # Add more default router canditates by ping6'ing 1079 # the All-Routers multicast address. 1080 for _if in $_v6ifs; do 1081 _resp=$(ping6 -n -c 2 ff02::2%$_if 2>/dev/null | 1082 sed -En '/^[0-9]+ bytes from /{s///;s/: .*$//p;}') 1083 for _dr in $_resp; do 1084 _routers=$(addel $_dr $_routers) 1085 done 1086 done 1087 1088 [[ -n $_routers ]] && _routers=$(bsort $_routers) 1089 _prompt="IPv6 default router?" 1090 1091 if $AI; then 1092 _autorespond "$_prompt (IPv6 address or 'none')" none && 1093 echo "$_prompt $resp" 1094 [[ $resp != none ]] && 1095 route -n add -inet6 -host default $resp && 1096 echo $resp >>/tmp/i/mygate 1097 else 1098 PS3="$_prompt (list #, IPv6 address or 'none') " 1099 select _resp in none $_routers; do 1100 [[ $REPLY == none || $_resp == none ]] && break 1101 [[ -z $_resp ]] && _resp=$REPLY 1102 # Avoid possible "file exists" errors 1103 route -n -q delete -inet6 -host default $_resp 1104 if route -n add -inet6 -host default $_resp; then 1105 echo $_resp >>/tmp/i/mygate 1106 break 1107 fi 1108 done 1109 fi 1110} 1111 1112# Configure IPv6 interface $1, add hostname $2 to the hosts file, 1113# create the hostname.if file $3. Ask the user for the IPv6 address 1114# and prefix length if the address was not specified in CIDR notation, 1115# unless he chooses 'autoconf'. 1116v6_config() { 1117 local _if=$1 _name=$2 _hn=$3 _addr _newaddr _prefixlen 1118 1119 ifconfig lo0 inet6 >/dev/null 2>&1 || return 1120 1121 # Preset the default answers by preserving possibly existing 1122 # configuration from previous runs. 1123 set -- $(v6_info $_if) 1124 if [[ $1 == *AUTOCONF6* ]]; then 1125 _addr=autoconf 1126 elif [[ -n $2 ]]; then 1127 _addr=$2 1128 _prefixlen=$3 1129 fi 1130 1131 # Nuke existing inet6 configuration. 1132 ifconfig $_if -inet6 1133 1134 while :; do 1135 ask_until "IPv6 address for $_if? (or 'autoconf' or 'none')" \ 1136 "${_addr:-none}" 1137 case $resp in 1138 none) return 1139 ;; 1140 autoconf) 1141 ifconfig $_if inet6 autoconf up 1142 echo "inet6 autoconf" >>$_hn 1143 return 1144 ;; 1145 esac 1146 1147 _newaddr=$resp 1148 if [[ $_newaddr == */* ]]; then 1149 ifconfig $_if inet6 $_newaddr up 1150 else 1151 ask_until "IPv6 prefix length for $_if?" \ 1152 "${_prefixlen:-64}" 1153 ifconfig $_if inet6 $_newaddr/$resp up 1154 fi 1155 1156 set -- $(v6_info $_if) 1157 if [[ -n $2 ]]; then 1158 echo "inet6 $2 $3" >>$_hn 1159 add_hostent "$2" "$_name" 1160 return 1161 fi 1162 1163 $AI && exit 1 1164 done 1165} 1166 1167# Perform an 802.11 network scan on interface $1 and cache the result a file. 1168ieee80211_scan() { 1169 [[ -f $WLANLIST ]] || 1170 ifconfig $1 scan | 1171 sed -n 's/^[[:space:]]*nwid \(.*\) chan [0-9]* bssid \([[:xdigit:]:]*\).*/\1 (\2)/p' >$WLANLIST 1172 cat $WLANLIST 1173} 1174 1175# Configure 802.11 interface $1 and append ifconfig options to hostname.if $2. 1176# Ask the user for the access point ESSID, the security protocol and a secret. 1177ieee80211_config() { 1178 local _if=$1 _hn=$2 _prompt _nwid _haswpa=0 _err 1179 1180 # Reset 802.11 settings and determine wpa capability. 1181 ifconfig $_if -nwid -nwkey 1182 ifconfig $_if -wpa 2>/dev/null && _haswpa=1 1183 1184 # Empty scan cache. 1185 rm -f $WLANLIST 1186 1187 while [[ -z $_nwid ]]; do 1188 ask_until "Access point? (ESSID, 'any', list# or '?')" "any" 1189 case "$resp" in 1190 +([0-9])) 1191 _nwid=$(ieee80211_scan $_if | 1192 sed -n ${resp}'{s/ ([[:xdigit:]:]*)$//p;q;}') 1193 [[ -z $_nwid ]] && echo "There is no line $resp." 1194 [[ $_nwid = \"*\" ]] && _nwid=${_nwid#\"} _nwid=${_nwid%\"} 1195 ;; 1196 \?) ieee80211_scan $_if | cat -n | more -c 1197 ;; 1198 *) _nwid=$resp 1199 ;; 1200 esac 1201 done 1202 1203 # 'any' implies that only open access points are considered. 1204 if [[ $_nwid != any ]]; then 1205 1206 _prompt="Security protocol? (O)pen, (W)EP" 1207 ((_haswpa == 1)) && _prompt="$_prompt, WPA-(P)SK" 1208 while :; do 1209 ask_until "$_prompt" "O" 1210 case "$_haswpa-$resp" in 1211 ?-[Oo]) # No further questions 1212 ifconfig $_if nwid "$_nwid" 1213 quote nwid "$_nwid" >>$_hn 1214 break 1215 ;; 1216 ?-[Ww]) ask_until "WEP key? (will echo)" 1217 # Make sure ifconfig accepts the key. 1218 if _err=$(ifconfig $_if nwid "$_nwid" nwkey "$resp" 2>&1) && 1219 [[ -z $_err ]]; then 1220 quote nwid "$_nwid" nwkey "$resp" >>$_hn 1221 break 1222 fi 1223 echo "$_err" 1224 ;; 1225 1-[Pp]) ask_until "WPA passphrase? (will echo)" 1226 # Make sure ifconfig accepts the key. 1227 if ifconfig $_if nwid "$_nwid" wpakey "$resp"; then 1228 quote nwid "$_nwid" wpakey "$resp" >>$_hn 1229 break 1230 fi 1231 ;; 1232 *) echo "'$resp' is not a valid choice." 1233 ;; 1234 esac 1235 done 1236 fi 1237} 1238 1239# Set up IPv4 and IPv6 interface configuration. 1240configure_ifs() { 1241 local _first _hn _if _name _p _vi 1242 1243 # Always need lo0 configured. 1244 ifconfig lo0 inet 127.0.0.1/8 1245 1246 # In case of restart, delete previous default gateway config. 1247 rm -f /tmp/i/mygate 1248 1249 while :; do 1250 # Discover last configured vlan interface and increment its 1251 # minor for the next offered vlan interface. 1252 _vi=$(get_ifs vlan | sed '$!d;s/^vlan//') 1253 [[ -n $_vi ]] && ((_vi++)) 1254 1255 ask_which "network interface" "do you wish to configure" \ 1256 "\$(get_ifs) vlan${_vi:-0}" \ 1257 ${_p:-'$( (get_ifs netboot; get_ifs) | sed q )'} 1258 [[ $resp == done ]] && break 1259 1260 _if=$resp 1261 _hn=/tmp/i/hostname.$_if 1262 rm -f $_hn 1263 1264 # If the offered vlan is chosen, ask the relevant 1265 # questions and bring it up. 1266 if [[ $_if == vlan+([0-9]) ]]; then 1267 vlan_config $_if || continue 1268 fi 1269 1270 # Test if it is an 802.11 interface. 1271 ifconfig $_if 2>/dev/null | grep -q "^[[:space:]]*ieee80211:" && 1272 ieee80211_config $_if $_hn 1273 1274 # First interface configured will use the hostname without 1275 # asking the user. 1276 resp=$(hostname -s) 1277 [[ -n $_first && $_first != $_if ]] && 1278 ask "Symbolic (host) name for $_if?" "$resp" 1279 _name=$resp 1280 1281 v4_config $_if $_name $_hn 1282 v6_config $_if $_name $_hn 1283 1284 if [[ -f $_hn ]]; then 1285 chmod 640 $_hn 1286 : ${_first:=$_if} 1287 fi 1288 1289 NIFS=$(ls -1 /tmp/i/hostname.* 2>/dev/null | grep -c ^) 1290 _p=done 1291 done 1292} 1293 1294# Set up IPv4 default route by asking the user for an IPv4 address and preserve 1295# that information in /etc/mygate. If setting the default route fails, try to 1296# revert to a possibly existing previous one. 1297v4_defroute() { 1298 local _dr _dr_if 1299 1300 # Only configure a default route if an IPv4 address was configured. 1301 [[ -n $(ifconfig | sed -n '/[ ]inet .* broadcast /p') ]] || return 1302 1303 # Check routing table to see if a default route ($1) already exists 1304 # and what interface it is connected to ($2). 1305 set -- $(route -n show -inet | 1306 sed -En 's/^default +([0-9.]+) .* ([a-z0-9]+) *$/\1 \2/p') 1307 [[ -n $1 ]] && _dr=$1 _dr_if=$2 1308 1309 # Don't ask if a default route exits and is handled by dhclient. 1310 [[ -n $_dr ]] && isin "$_dr_if" $(get_ifs dhcp) && return 1311 1312 while :; do 1313 ask_until "Default IPv4 route? (IPv4 address or none)" "$_dr" 1314 [[ $resp == none ]] && break 1315 route delete -inet default >/dev/null 2>&1 1316 if route -n add -inet -host default "$resp"; then 1317 echo "$resp" >>/tmp/i/mygate 1318 break 1319 else 1320 route -n add -inet -host default $_dr >/dev/null 2>&1 1321 fi 1322 done 1323} 1324 1325# Extract the domain part from currently configured fully qualified domain name. 1326# If none is set, use 'my.domain'. 1327get_fqdn() { 1328 local _dn 1329 1330 _dn=$(hostname) 1331 _dn=${_dn#$(hostname -s)} 1332 _dn=${_dn#.} 1333 1334 echo "${_dn:=my.domain}" 1335} 1336 1337 1338# ------------------------------------------------------------------------------ 1339# Support functions for install_sets() 1340# ------------------------------------------------------------------------------ 1341 1342# SANESETS defines the required list of set files for a sane install or upgrade. 1343# During install_files(), each successfully installed set file is removed from 1344# DEFAULTSETS. Check if there are SANESETS still in DEFAULTSETS and if they were 1345# deliberately skipped. If $1 is not defined, ask the user about each skipped 1346# set file. Care is taken to make sure the return value is correct. 1347sane_install() { 1348 local _q=$1 _s 1349 1350 for _s in $SANESETS; do 1351 isin "$_s" $DEFAULTSETS || continue 1352 [[ -n $_q ]] && return 1 1353 if ! ask_yn "Are you *SURE* your $MODE is complete without '$_s'?"; then 1354 $AI && exit 1 || return 1 1355 fi 1356 done 1357} 1358 1359# Show list of available sets $1 and let the user select which sets to install. 1360# Preselect sets listed in $2 and store the list of selected sets in $resp. 1361# 1362# If the list of available sets only contains kernels during an upgrade, assume 1363# that the user booted into the installer using the currently installed bsd.rd 1364# and specified a set location pointing to a new release. In this case, only 1365# show and preselect bsd.rd. By setting UPGRADE_BSDRD the signify key for the 1366# next release is used to verify the downloaded bsd.rd, the current bsd.rd is 1367# preserved and no questions about missing sets are asked. 1368select_sets() { 1369 local _avail=$1 _selected=$2 _f _action _col=$COLUMNS 1370 local _bsd_rd _no_sets=true 1371 1372 if [[ $MODE == upgrade ]]; then 1373 for _f in $_avail; do 1374 [[ $_f != bsd* ]] && _no_sets=false 1375 [[ $_f == bsd.rd* ]] && _bsd_rd=$_f 1376 done 1377 $_no_sets && UPGRADE_BSDRD=true _avail=$_bsd_rd _selected=$_bsd_rd 1378 fi 1379 1380 # account for 4 spaces added to the sets list 1381 let COLUMNS=_col-8 1382 1383 cat <<__EOT 1384 1385Select sets by entering a set name, a file name pattern or 'all'. De-select 1386sets by prepending a '-', e.g.: '-game*'. Selected sets are labelled '[X]'. 1387__EOT 1388 while :; do 1389 for _f in $_avail; do 1390 isin "$_f" $_selected && echo "[X] $_f" || echo "[ ] $_f" 1391 done | show_cols | sed 's/^/ /' 1392 ask "Set name(s)? (or 'abort' or 'done')" done 1393 1394 set -o noglob 1395 for resp in $resp; do 1396 case $resp in 1397 abort) _selected=; break 2;; 1398 done) break 2;; 1399 -*) _action=rmel;; 1400 *) _action=addel;; 1401 esac 1402 resp=${resp#[+-]} 1403 [[ $resp == all ]] && resp=* 1404 1405 for _f in $_avail; do 1406 [[ $_f == $resp ]] && 1407 _selected=$($_action $_f $_selected) 1408 done 1409 done 1410 done 1411 1412 set +o noglob 1413 COLUMNS=$_col 1414 1415 resp=$_selected 1416} 1417 1418# Run a command ($2+) as unprivileged user ($1). 1419# Take extra care that after "cmd" no "user" processes exist. 1420# 1421# Optionally: 1422# - create "file" and chown it to "user" 1423# - after "cmd", chown "file" back to root 1424# 1425# Usage: do_as user [-f file] cmd 1426do_as() { 1427 (( $# >= 2 )) || return 1428 1429 local _file _rc _user=$1 1430 shift 1431 1432 if [[ $1 == -f ]]; then 1433 _file=$2 1434 shift 2 1435 fi 1436 1437 if [[ -n $_file ]]; then 1438 >$_file 1439 chown "$_user" "$_file" 1440 fi 1441 1442 doas -u "$_user" "$@" 1443 _rc=$? 1444 1445 while doas -u "$_user" kill -9 -1 2>/dev/null; do 1446 echo "Processes still running for user $_user after: $@" 1447 sleep 1 1448 done 1449 1450 [[ -n $_file ]] && chown root "$_file" 1451 1452 return $_rc 1453} 1454 1455unpriv() { 1456 do_as _sndio "$@" 1457} 1458 1459unpriv2() { 1460 do_as _file "$@" 1461} 1462 1463# Find and list filesystems to store the prefetched sets. Prefer filesystems 1464# which are not used during extraction with 512M free space. Otherwise search 1465# any other filesystem that has 2 GB free space to prevent overflow during 1466# extraction. 1467prefetcharea_fs_list() { 1468 local _fs_list 1469 1470 _fs_list=$( ( 1471 for fs in /mnt/{tmp,home,usr{/local,}}; do 1472 df -k $fs 2>/dev/null | grep " $fs\$" 1473 done 1474 df -k 1475 ) | ( 1476 while read a a a a m m; do 1477 [[ $m == /mnt/@(tmp|home|usr/@(src,obj,xobj))@(|/*) ]] && 1478 ((a > 524288)) && echo $m && continue 1479 [[ $m == /mnt@(|/*) ]] && 1480 ((a > 524288 * 4)) && echo $m 1481 done 1482 ) | ( 1483 while read fs; do 1484 isin "$fs" $list || list="$list${list:+ }$fs" 1485 done 1486 echo $list 1487 ) ) 1488 1489 [[ -n $_fs_list ]] && echo $_fs_list || return 1 1490} 1491 1492# Install a user-selected subset of the files listed in $2 from the source $1. 1493# Display an error message for each failed install and ask the user whether to 1494# continue or not. 1495install_files() { 1496 local _src=$1 _files=$2 _f _sets _get_sets _n _col=$COLUMNS _tmpfs \ 1497 _tmpfs_list _tmpsrc _cfile=/tmp/SHA256 _fsrc _unver _t _issue 1498 local _srclocal=false _unpriv=unpriv 1499 1500 # Fetch sets from local sources (disk, cdrom, nfs) as root. 1501 [[ $_src == file://* ]] && _srclocal=true _unpriv= 1502 1503 # Based on the file list in $_files, create two lists for select_sets(). 1504 # _sets: the list of files the user can select from 1505 # _get_sets: the list of files that are shown as pre-selected 1506 # 1507 # Sets will be installed in the order given in ALLSETS to ensure proper 1508 # installation. So, to minimize user confusion display the sets in the 1509 # order in which they will be installed. 1510 for _f in $ALLSETS; do 1511 isin "$_f" $_files || continue 1512 _sets=$(addel $_f $_sets) 1513 isin "$_f" $DEFAULTSETS "site$VERSION-$(hostname -s).tgz" && 1514 _get_sets=$(addel $_f $_get_sets) 1515 done 1516 1517 if [[ -z $_sets ]]; then 1518 echo -n "Looked at $_src " 1519 echo "and found no $OBSD sets. The set names looked for were:" 1520 1521 let COLUMNS=_col-8 1522 for _n in $ALLSETS; do echo $_n; done | show_cols | sed 's/^/ /' 1523 COLUMNS=$_col 1524 1525 $AI && exit 1 1526 echo 1527 return 1528 fi 1529 1530 isin "INSTALL.$ARCH" $_files || 1531 ask_yn "INSTALL.$ARCH not found. Use sets found here anyway?" || 1532 return 1533 1534 select_sets "$_sets" "$_get_sets" 1535 1536 [[ -n $resp ]] || return 1537 _get_sets=$resp 1538 1539 # Reorder $_get_sets. 1540 _get_sets=$(for s in $ALLSETS; do isin "$s" $_get_sets && echo $s; done) 1541 1542 # Note which sets didn't verify ok. 1543 _unver=$_get_sets 1544 1545 # Try to prefetch and control checksum of the set files. 1546 # Use dummy for loop as combined assignment and do { ... } while(0). 1547 for _issue in ''; do 1548 ! isin SHA256.sig $_files && 1549 _issue="Directory does not contain SHA256.sig" && break 1550 1551 if ! $_srclocal; then 1552 ! _tmpfs_list=$(prefetcharea_fs_list) && 1553 _issue="Cannot determine prefetch area" && break 1554 1555 for _tmpfs in $_tmpfs_list; do 1556 # Try to clean up from previous runs, assuming 1557 # the _tmpfs selection yields the same mount 1558 # point. 1559 for _tmpsrc in $_tmpfs/sets.+([0-9]).+([0-9]); do 1560 [[ -d $_tmpsrc ]] && rm -r $_tmpsrc 1561 done 1562 1563 # Create a download directory for the sets and 1564 # check that the _sndio user can read files from 1565 # it. Otherwise cleanup and skip the filesystem. 1566 if _tmpsrc=$(tmpdir "$_tmpfs/sets"); then 1567 ( 1568 >$_tmpsrc/t && 1569 $_unpriv cat $_tmpsrc/t 1570 ) >/dev/null 2>&1 && break || 1571 rm -r $_tmpsrc 1572 fi 1573 done 1574 1575 [[ ! -d $_tmpsrc ]] && 1576 _issue="Cannot create prefetch area" && break 1577 fi 1578 1579 # Cleanup from previous runs. 1580 rm -f $_cfile $_cfile.sig 1581 1582 _t=Get/Verify 1583 $_srclocal && _t='Verifying ' 1584 1585 # Fetch signature file. 1586 ! $_unpriv ftp -D "$_t" -Vmo - "$_src/SHA256.sig" >"$_cfile.sig" && 1587 _issue="Cannot fetch SHA256.sig" && break 1588 1589 # The bsd.rd only download/verify/install assumes the sets 1590 # location of the next release. So use the right signature file. 1591 $UPGRADE_BSDRD && 1592 PUB_KEY=/mnt/etc/signify/openbsd-$((VERSION + 1))-base.pub 1593 1594 # Verify signature file with public keys. 1595 ! unpriv -f "$_cfile" \ 1596 signify -Vep $PUB_KEY -x "$_cfile.sig" -m "$_cfile" && 1597 _issue="Signature check of SHA256.sig failed" && break 1598 1599 # Fetch and verify the set files. 1600 for _f in $_get_sets; do 1601 $UU && reset_watchdog 1602 1603 rm -f /tmp/h /tmp/fail 1604 1605 # Fetch set file and create a checksum by piping through 1606 # sha256. Create a flag file in case ftp failed. Sets 1607 # from net are written to the prefetch area, the output 1608 # of local sets is discarded. 1609 ( $_unpriv ftp -D "$_t" -Vmo - "$_src/$_f" || >/tmp/fail ) | 1610 ( $_srclocal && unpriv2 sha256 >/tmp/h || 1611 unpriv2 -f /tmp/h sha256 -ph /tmp/h >"$_tmpsrc/$_f" ) 1612 1613 # Handle failed transfer. 1614 if [[ -f /tmp/fail ]]; then 1615 rm -f "$_tmpsrc/$_f" 1616 if ! ask_yn "Fetching of $_f failed. Continue anyway?"; then 1617 [[ -d $_tmpsrc ]] && rm -rf "$_tmpsrc" 1618 $AI && exit 1 1619 return 1620 fi 1621 _unver=$(rmel $_f $_unver) 1622 _get_sets=$(rmel $_f $_get_sets) 1623 continue 1624 fi 1625 1626 # Verify sets by comparing its checksum with SHA256. 1627 if fgrep -qx "SHA256 ($_f) = $(</tmp/h)" "$_cfile"; then 1628 _unver=$(rmel $_f $_unver) 1629 else 1630 if ! ask_yn "Checksum test for $_f failed. Continue anyway?"; then 1631 [[ -d $_tmpsrc ]] && rm -rf "$_tmpsrc" 1632 $AI && exit 1 1633 return 1634 fi 1635 fi 1636 done 1637 done 1638 1639 [[ -n $_unver ]] && : ${_issue:="Unverified sets:" ${_unver% }} 1640 if [[ -n $_issue ]] && 1641 ! ask_yn "$_issue. Continue without verification?"; then 1642 [[ -d $_tmpsrc ]] && rm -rf "$_tmpsrc" 1643 $AI && exit 1 1644 return 1645 fi 1646 1647 # We are committed to installing new files. Attempt to cope with 1648 # potential space shortage in /usr by deleting a few versioned 1649 # areas which will be replaced from the new sets 1650 if [[ $MODE == upgrade ]]; then 1651 if isin base$VERSION.tgz $_get_sets; then 1652 rm -f /mnt/usr/share/relink/usr/lib/* 1653 rm -rf /mnt/usr/lib/libLLVM.so.[01].0 1654 rm -rf /mnt/usr/libdata/perl5 1655 fi 1656 if isin comp$VERSION.tgz $_get_sets; then 1657 rm -rf /mnt/usr/lib/{gcc-lib,clang} 1658 rm -rf /mnt/usr/include/g++ 1659 fi 1660 rm -rf /mnt/var/syspatch/* 1661 fi 1662 1663 # Install the set files. 1664 for _f in $_get_sets; do 1665 $UU && reset_watchdog 1666 _fsrc="$_src/$_f" 1667 1668 # Take the set file from the prefetch area if possible. 1669 [[ -f $_tmpsrc/$_f ]] && _fsrc="file://$_tmpsrc/$_f" 1670 1671 # Extract the set files and put the kernel files in place. 1672 case $_fsrc in 1673 *.tgz) $_unpriv ftp -D Installing -Vmo - "$_fsrc" | 1674 tar -zxphf - -C /mnt && 1675 if [[ $_f == ?(x)base*.tgz && $MODE == install ]]; then 1676 ftp -D Extracting -Vmo - \ 1677 file:///mnt/var/sysmerge/${_f%%base*}etc.tgz | 1678 tar -zxphf - -C /mnt 1679 fi 1680 ;; 1681 *) # Make a backup of the existing ramdisk kernel in the 1682 # bsd.rd only download/verify/install case. 1683 $UPGRADE_BSDRD && [[ $_f == bsd.rd* ]] && 1684 cp /mnt/$_f /mnt/$_f.old.$VERSION 1685 $_unpriv ftp -D Installing -Vmo - "$_fsrc" >"/mnt/$_f" 1686 ;; 1687 esac 1688 if (($?)); then 1689 if ! ask_yn "Installation of $_f failed. Continue anyway?"; then 1690 [[ -d $_tmpsrc ]] && rm -rf "$_tmpsrc" 1691 $AI && exit 1 1692 return 1693 fi 1694 else 1695 # Remove each successfully installed set file from 1696 # DEFAULTSETS which is checked by sane_sets(). 1697 DEFAULTSETS=$(rmel $_f $DEFAULTSETS) 1698 # Reset DEFAULTSETS to make sure that sane_sets() does 1699 # not complain about missing set files in the bsd.rd 1700 # only download/verify/install case, 1701 $UPGRADE_BSDRD && DEFAULTSETS= 1702 fi 1703 [[ -d $_tmpsrc ]] && rm -f "$_tmpsrc/$_f" 1704 done 1705 [[ -d $_tmpsrc ]] && rm -rf "$_tmpsrc" || true 1706 1707 # Keep SHA256 from installed sets for sysupgrade(8). 1708 if [[ -f $_cfile ]]; then 1709 cp $_cfile /mnt/var/db/installed.SHA256 1710 elif $_srclocal && [[ -f ${_src#file://}/SHA256 ]]; then 1711 cp ${_src#file://}/SHA256 /mnt/var/db/installed.SHA256 1712 fi 1713 1714 $UU && reset_watchdog 1715} 1716 1717# Fetch install sets from an HTTP server possibly using a proxy. 1718install_http() { 1719 local _d _f _flist _file_list _prompt _tls _http_proto _url_base 1720 local _idx=/tmp/i/index.txt _sha=/tmp/i/SHA256 _sig=/tmp/i/SHA256.sig 1721 local _iu_url _iu_srv _iu_dir _mirror_url _mirror_srv _mirror_dir 1722 local _ftp_stdout=/tmp/i/ftpstdout _rurl_base 1723 1724 # N.B.: Don't make INSTALL_MIRROR a local variable! It preserves the 1725 # mirror information if install_http() is called multiple times with 1726 # mirror and local servers. That ensures that the mirror server ends 1727 # up in /etc/installurl file if one of the servers is not a mirror. 1728 1729 # N.B.: 'http_proxy' is an environment variable used by ftp(1). 1730 # DON'T change the name or case! 1731 ask "HTTP proxy URL? (e.g. 'http://proxy:8080', or 'none')" \ 1732 "${http_proxy:-none}" 1733 unset http_proxy 1734 [[ $resp == none ]] || export http_proxy=$resp 1735 1736 # If the mirror server listfile download failed, inform the user and 1737 # show a reduced prompt. 1738 if [[ -s $HTTP_LIST ]]; then 1739 _prompt="HTTP Server? (hostname, list#, 'done' or '?')" 1740 else 1741 echo "(Unable to get list from ftp.openbsd.org, but that is OK)" 1742 _prompt="HTTP Server? (hostname or 'done')" 1743 fi 1744 1745 # Use information from /etc/installurl as defaults for upgrades. 1746 # Format of installurl: _http_proto://_iu_srv/_iu_dir 1747 # ^--------- _iu_url ---------^ 1748 if [[ $MODE == upgrade ]] && 1749 _iu_url=$(stripcom /mnt/etc/installurl); then 1750 _iu_srv=${_iu_url#*://} 1751 _iu_srv=${_iu_srv%%/*} 1752 _iu_dir=${_iu_url##*$_iu_srv*(/)} 1753 [[ -n $_iu_srv ]] && HTTP_SERVER=$_iu_srv 1754 fi 1755 1756 # Get server IP address or hostname and optionally the http protocol. 1757 while :; do 1758 ask_until "$_prompt" "$HTTP_SERVER" 1759 case $resp in 1760 done) return 1761 ;; 1762 "?") [[ -s $HTTP_LIST ]] || continue 1763 # Show a numbered list of mirror servers. 1764 cat -n < $HTTP_LIST | more -c 1765 ;; 1766 +([0-9])) 1767 # A number is only used as a line number in $HTTP_LIST. 1768 [[ -s $HTTP_LIST ]] || continue 1769 # Extract the URL from the mirror server listfile. 1770 set -- $(sed -n "${resp}p" $HTTP_LIST) 1771 if (($# < 1)); then 1772 echo "There is no line $resp." 1773 continue 1774 fi 1775 HTTP_SERVER=${1%%/*} 1776 # Repeat loop to get user to confirm server address. 1777 ;; 1778 ?(http?(s)://)+([A-Za-z0-9:.\[\]_-])) 1779 case $resp in 1780 https://*) _tls=force _http_proto=https;; 1781 http://*) _tls=no _http_proto=http;; 1782 *) _tls=try _http_proto=$HTTP_PROTO;; 1783 esac 1784 if ! $FTP_TLS && [[ $_tls == force ]]; then 1785 echo "https not supported on this platform." 1786 $AI && exit 1 || continue 1787 fi 1788 HTTP_SERVER=${resp#*://} 1789 break 1790 ;; 1791 *) echo "'$resp' is not a valid hostname." 1792 ;; 1793 esac 1794 done 1795 1796 # Get directory info from *last* line starting with the server 1797 # name. This means the last install from a mirror will not keep 1798 # the specific directory info. But an install from a local 1799 # server *will* remember the specific directory info. 1800 # Format: _mirror_srv/_mirror_dir location_info 1801 # ^---- _mirror_url ----^ 1802 set -- $(grep -i "^$HTTP_SERVER" $HTTP_LIST 2>/dev/null | sed '$!d') 1803 _mirror_url=${1%%*(/)} 1804 _mirror_srv=${_mirror_url%%/*} 1805 _mirror_dir=${_mirror_url##*$_mirror_srv*(/)} 1806 1807 # Decide on the default for the "Server directory" question. 1808 if [[ -n $_mirror_url ]]; then 1809 # Use directory information from cgi server if HTTP_SERVER was 1810 # found in HTTP_LIST. That is either an official mirror or the 1811 # server used in a previous installation or upgrade. 1812 _d=$_mirror_dir/$HTTP_SETDIR 1813 1814 # Preserve the information that it is an official mirror if 1815 # location is present in $2. 1816 (($# > 1)) && INSTALL_MIRROR=$_mirror_url 1817 elif [[ -n $_iu_url ]]; then 1818 # Otherwise, if it exists, use directory information from 1819 # installurl(5) during upgrade. 1820 _d=$_iu_dir/$HTTP_SETDIR 1821 else 1822 _d=pub/OpenBSD/$HTTP_SETDIR 1823 fi 1824 1825 ask_until "Server directory?" "$_d" 1826 HTTP_DIR=${resp##+(/)} 1827 _url_base="$_http_proto://$HTTP_SERVER/$HTTP_DIR" 1828 1829 # Fetch SHA256.sig to create the list of files to select from. 1830 rm -f $_idx $_sha $_sig $_ftp_stdout 1831 if ! unpriv -f $_sig \ 1832 ftp -w 15 -vMo $_sig "$_url_base/SHA256.sig" \ 1833 >$_ftp_stdout 2>/dev/null; then 1834 case $_tls in 1835 force) $AI && exit 1 || return 1836 ;; 1837 try) ask_yn "Unable to connect using https. Use http instead?" || 1838 return 1839 _http_proto=http 1840 _url_base="http://$HTTP_SERVER/$HTTP_DIR" 1841 unpriv -f $_sig ftp -vMo $_sig "$_url_base/SHA256.sig" \ 1842 >$_ftp_stdout 2>/dev/null 1843 ;; 1844 esac 1845 fi 1846 1847 # In case of URL redirection, use the final location to retrieve the 1848 # rest of the files from. Redirection does not change INSTALL_MIRROR. 1849 _rurl_base=$(sed -n 's/^Requesting //p' $_ftp_stdout | sed '$!d') 1850 _rurl_base=${_rurl_base%/SHA256.sig*} 1851 1852 # Verify SHA256.sig, write SHA256 and extract the list of files. 1853 if unpriv -f $_sha \ 1854 signify -Vep $PUB_KEY -x $_sig -m $_sha >/dev/null 2>&1; then 1855 _file_list="$(sed -n 's/^SHA256 (\(.*\)).*$/\1/p' $_sha)" 1856 _file_list="SHA256.sig $_file_list" 1857 else 1858 echo "Unable to get a verified list of distribution sets." 1859 # Deny this server, if it's a mirror without a valid SHA256.sig. 1860 if [[ ${_rurl_base%/$HTTP_SETDIR} == "$_http_proto://$INSTALL_MIRROR" ]]; then 1861 $AI && exit 1 || return 1862 fi 1863 fi 1864 1865 # Fetch index.txt, extract file list but add only entries that are not 1866 # already in _file_list. This allows for a verified list of distribution 1867 # sets from SHA256.sig, siteXX sets or the whole set list from index.txt 1868 # if SHA256.sig was not found (e.g. self compiled sets). 1869 if unpriv -f $_idx \ 1870 ftp -VMo $_idx "$_rurl_base/index.txt" 2>/dev/null; then 1871 _flist=$(sed -En 's/^.* ([a-zA-Z][a-zA-Z0-9._-]+)$/\1/p' $_idx) 1872 for _f in $_flist; do 1873 ! isin "$_f" $_file_list && _file_list="$_file_list $_f" 1874 done 1875 fi 1876 rm -f $_idx $_sha $_sig $_ftp_stdout 1877 1878 install_files "$_rurl_base" "$_file_list" 1879 1880 # Remember the sets location which is used later for creating the 1881 # installurl(5) file and to tell the cgi server. 1882 if [[ -n $INSTALL_MIRROR ]]; then 1883 INSTALL_URL=$_http_proto://$INSTALL_MIRROR 1884 else 1885 # Remove the architecture and snaphots or version part. 1886 INSTALL_URL=${_url_base%/$ARCH} 1887 INSTALL_URL=${INSTALL_URL%@(/$VNAME|/snapshots)} 1888 fi 1889} 1890 1891# Ask for the path to the set files on an already mounted filesystem and start 1892# the set installation. 1893install_mounted_fs() { 1894 local _dir 1895 1896 while :; do 1897 ask_until "Pathname to the sets? (or 'done')" "$SETDIR" 1898 [[ $resp == done ]] && return 1899 # Accept a valid /mnt2 or /mnt relative path. 1900 [[ -d /mnt2/$resp ]] && { _dir=/mnt2/$resp; break; } 1901 [[ -d /mnt/$resp ]] && { _dir=/mnt/$resp; break; } 1902 # Accept a valid absolute path. 1903 [[ -d /$resp ]] && { _dir=/$resp; break; } 1904 echo "The directory '$resp' does not exist." 1905 $AI && exit 1 1906 done 1907 1908 install_files "file://$_dir" "$(ls $_dir/)" 1909} 1910 1911# Install sets from CD-ROM drive $1. 1912install_cdrom() { 1913 local _drive=$1 1914 1915 make_dev $_drive && mount_mnt2 $_drive || return 1916 1917 install_mounted_fs 1918} 1919 1920# Install sets from disk. 1921# Ask for the disk device containing the set files. 1922install_disk() { 1923 if ! ask_yn "Is the disk partition already mounted?" yes; then 1924 ask_which "disk" "contains the $MODE media" \ 1925 '$(bsort $(get_dkdevs))' \ 1926 '$(bsort $(rmel $ROOTDISK $(get_dkdevs)))' 1927 [[ $resp == done ]] && return 1 1928 1929 # Ensure the device file exists and mount the fs on /mnt2. 1930 make_dev $resp && mount_mnt2 $resp || return 1931 fi 1932 1933 install_mounted_fs 1934} 1935 1936# Ask for the nfs share details, mount it and start the set installation. 1937install_nfs() { 1938 local _tcp 1939 1940 # Get the IP address of the server. 1941 ask_until "Server IP address or hostname?" "$NFS_ADDR" 1942 NFS_ADDR=$resp 1943 1944 # Get the server path to mount. 1945 ask_until "Filesystem on server to mount?" "$NFS_PATH" 1946 NFS_PATH=$resp 1947 1948 # Determine use of TCP. 1949 ask_yn "Use TCP transport? (requires TCP-capable NFS server)" && _tcp=-T 1950 1951 # Mount the server. 1952 mount_nfs $_tcp -o ro -R 5 $NFS_ADDR:$NFS_PATH /mnt2 || return 1953 1954 install_mounted_fs 1955} 1956 1957# Mount filesystem containing the set files on device $1, optionally ask the 1958# user for the device name. 1959mount_mnt2() { 1960 local _dev=$1 _opts _file=/tmp/i/parts.$1 _parts 1961 1962 disklabel $_dev 2>/dev/null | 1963 sed -En '/swap|unused/d;/^ [a-p]: /p' >$_file 1964 _parts=$(sed 's/^ \(.\): .*/\1/' $_file) 1965 set -- $_parts 1966 (($# == 0)) && { echo "No filesystems found on $_dev."; return 1; } 1967 1968 if isin "c" $_parts; then 1969 # Don't ask questions if 'c' contains a filesystem. 1970 resp=c 1971 elif (($# == 1)); then 1972 # Don't ask questions if there's only one choice. 1973 resp=$1 1974 else 1975 # Display partitions with filesystems and ask which to use. 1976 cat $_file 1977 ask_which "$_dev partition" "has the $MODE sets" \ 1978 '$(disklabel '$_dev' 2>/dev/null | 1979 sed -En '\''/swap|unused/d;/^ ([a-p]): .*/s//\1/p'\'')' 1980 [[ $resp == done ]] && return 1 1981 fi 1982 1983 # Always mount msdos partitions with -s to get lower case names. 1984 grep -q "^ $resp: .*MSDOS" $_file && _opts="-s" 1985 mount -o ro,$_opts /dev/$_dev$resp /mnt2 1986} 1987 1988 1989# ------------------------------------------------------------------------------ 1990# Functions used in install.sh/upgrade.sh and its associates 1991# ------------------------------------------------------------------------------ 1992 1993# Ask for terminal type if on console, otherwise ask for/set keyboard layout. 1994set_term() { 1995 local _layouts 1996 export TERM=${TERM:-${MDTERM:-vt220}} 1997 1998 if [[ -n $CONSOLE ]]; then 1999 ask "Terminal type?" "$TERM" 2000 TERM=$resp 2001 else 2002 [[ -x /sbin/kbd ]] || return 2003 _layouts=$(bsort $(kbd -l | egrep -v "^(user|tables|encoding)")) 2004 while :; do 2005 ask "Choose your keyboard layout ('?' or 'L' for list)" default 2006 case $resp in 2007 [lL\?]) echo "Available layouts: $_layouts" 2008 ;; 2009 default) break 2010 ;; 2011 *) if kbd -q "$resp"; then 2012 echo $resp >/tmp/i/kbdtype 2013 break 2014 fi 2015 ;; 2016 esac 2017 done 2018 fi 2019} 2020 2021# Configure the network. 2022donetconfig() { 2023 local _dn _ns _f1 _f2 _f3 2024 2025 configure_ifs 2026 v4_defroute 2027 v6_defroute 2028 2029 # As dhclient will populate /etc/resolv.conf, a symbolic link to 2030 # /tmp/i/resolv.conf.shadow, mv any such file to /tmp/i/resolv.conf 2031 # so it will eventually be copied to /mnt/etc/resolv.conf and will 2032 # not in the meantime remove the user's ability to choose to use it 2033 # or not, during the rest of the install. 2034 if [[ -f /tmp/i/resolv.conf.shadow ]]; then 2035 mv /tmp/i/resolv.conf.shadow /tmp/i/resolv.conf 2036 # Get/store nameserver address(es) as a blank separated list 2037 # and the default fully qualified domain name from *first* 2038 # domain given on *last* search or domain statement. 2039 while read -r -- _f1 _f2 _f3; do 2040 [[ $_f1 == nameserver ]] && _ns="${_ns:+$_ns }$_f2" 2041 [[ $_f1 == @(domain|search) ]] && _dn=$_f2 2042 done </tmp/i/resolv.conf 2043 fi 2044 2045 # Get & apply fqdn to hostname. Don't ask if there's only one configured 2046 # interface and if it's managed by dhclient and if the domain name is 2047 # configured via dhclient too. 2048 resp="${_dn:-$(get_fqdn)}" 2049 if ifconfig dhcp >/dev/null 2>&1 && [[ $NIFS == 1 && -n $_dn ]]; then 2050 # If we have a 'domain-name' option in the lease file use that. 2051 # It might *NOT* not be the same as the first domain in any 2052 # 'domain-search' option. 2053 set -- $(get_ifs dhcp) 2054 set -- $(lease_value /var/db/dhclient.leases.$1 domain-name) 2055 [[ -n $1 ]] && resp=$1 2056 echo "Using DNS domainname $resp" 2057 else 2058 ask "DNS domain name? (e.g. 'example.com')" "$resp" 2059 fi 2060 hostname "$(hostname -s).$resp" 2061 2062 # Get & add nameservers to /tmp/i/resolv.conf. Don't ask if there's only 2063 # one configured interface and if it's managed by dhclient and if the 2064 # nameserver is configured via dhclient too. 2065 resp="${_ns:-none}" 2066 if ifconfig dhcp >/dev/null 2>&1 && [[ $NIFS == 1 && -n $_ns ]]; then 2067 echo "Using DNS nameservers at $resp" 2068 else 2069 ask "DNS nameservers? (IP address list or 'none')" "$resp" 2070 fi 2071 2072 # Construct appropriate resolv.conf. 2073 if [[ $resp != none ]]; then 2074 echo "lookup file bind" >/tmp/i/resolv.conf 2075 for _ns in $resp; do 2076 echo "nameserver $_ns" >>/tmp/i/resolv.conf 2077 done 2078 cp /tmp/i/resolv.conf /tmp/i/resolv.conf.shadow 2079 fi 2080} 2081 2082# Ask user about daemon startup on boot, X Window usage and console setup. 2083# The actual configuration is done later in apply(). 2084questions() { 2085 local _d _cdef=no 2086 2087 ask_yn "Start sshd(8) by default?" yes 2088 START_SSHD=$resp 2089 2090 APERTURE= 2091 resp= 2092 START_XDM= 2093 if [[ -n $(scan_dmesg '/^wsdisplay[0-9]* /s/ .*//p') ]]; then 2094 if [[ -n $(scan_dmesg '/^[a-z]*[01]: aperture needed/p') ]]; then 2095 ask_yn "Do you expect to run the X Window System?" yes && 2096 APERTURE=$MDXAPERTURE 2097 fi 2098 if [[ -n $MDXDM && $resp != n ]]; then 2099 ask_yn "Do you want the X Window System to be started by xenodm(1)?" 2100 START_XDM=$resp 2101 fi 2102 fi 2103 2104 if [[ -n $CDEV ]]; then 2105 _d=${CPROM:-$CDEV} 2106 [[ -n $CONSOLE ]] && _cdef=yes 2107 ask_yn "Change the default console to $_d?" $_cdef 2108 DEFCONS=$resp 2109 if [[ $resp == y ]]; then 2110 ask_which "speed" "should $_d use" \ 2111 "9600 19200 38400 57600 115200" $CSPEED 2112 case $resp in 2113 done) DEFCONS=n;; 2114 *) CSPEED=$resp;; 2115 esac 2116 fi 2117 fi 2118} 2119 2120# Gather information for setting up the user later in do_install(). 2121user_setup() { 2122 local _q="Setup a user? (enter a lower-case loginname, or 'no')" 2123 2124 while :; do 2125 ask "$_q" no 2126 case $resp in 2127 n|no) return 2128 ;; 2129 y|yes) _q="No really, what is the lower-case loginname, or 'no'?" 2130 continue 2131 ;; 2132 root|daemon|operator|bin|build|sshd|www|nobody|ftp) 2133 ;; 2134 [a-z]*([-a-z0-9_])) 2135 ((${#resp} <= 31)) && break 2136 ;; 2137 esac 2138 echo "$resp is not a usable loginname." 2139 done 2140 ADMIN=$resp 2141 while :; do 2142 ask "Full name for user $ADMIN?" "$ADMIN" 2143 case $resp in 2144 *[:\&,]*) 2145 echo "':', '&' or ',' are not allowed." 2146 ;; 2147 *) 2148 ((${#resp} <= 100)) && break 2149 echo "Too long." 2150 ;; 2151 esac 2152 done 2153 ADMIN_NAME=$resp 2154 2155 ask_password "Password for user $ADMIN?" 2156 ADMIN_PASS=$_password 2157 2158 ADMIN_KEY= 2159 $AI && ask "Public ssh key for user $ADMIN" none && 2160 [[ $resp != none ]] && ADMIN_KEY=$resp 2161} 2162 2163# Ask user whether or not to allow logins to root in case sshd(8) is enabled. 2164# If no user is setup, show a hint to enable root logins, but warn about risks 2165# of doing so. 2166ask_root_sshd() { 2167 typeset -l _resp 2168 2169 [[ $START_SSHD == y ]] || return 2170 2171 if [[ -z $ADMIN ]]; then 2172 echo "Since no user was setup, root logins via sshd(8) might be useful." 2173 fi 2174 echo "WARNING: root is targeted by password guessing attacks, pubkeys are safer." 2175 while :; do 2176 ask "Allow root ssh login? (yes, no, prohibit-password)" no 2177 _resp=$resp 2178 case $_resp in 2179 y|yes) SSHD_ENABLEROOT=yes 2180 ;; 2181 n|no) SSHD_ENABLEROOT=no 2182 ;; 2183 w|p|without-password|prohibit-password) 2184 SSHD_ENABLEROOT=prohibit-password 2185 ;; 2186 *) echo "'$resp' is not a valid choice." 2187 $AI && exit 1 2188 continue 2189 ;; 2190 esac 2191 break 2192 done 2193} 2194 2195# Set TZ variable based on zonefile $1 and user selection. 2196set_timezone() { 2197 local _zonefile=$1 _zonepath _zsed _zoneroot=/usr/share/zoneinfo 2198 2199 # If the timezone file is not available, 2200 # return immediately. 2201 [[ ! -f $_zonefile ]] && return 2202 2203 # If configured in a previous call, return immediately. 2204 [[ -n $TZ ]] && return 2205 2206 if [[ -h /mnt/etc/localtime ]]; then 2207 TZ=$(ls -l /mnt/etc/localtime 2>/dev/null) 2208 TZ=${TZ#*${_zoneroot#/mnt}/} 2209 fi 2210 2211 wait_cgiinfo 2212 isin "$CGI_TZ" $(<$_zonefile) && TZ=$CGI_TZ 2213 2214 # If neither the base or HTTP_LIST gave a hint, and this is the 2215 # early question, give up, and ask after the sets are installed. 2216 [[ $_zonefile == /var/tzlist && -z $TZ ]] && return 2217 2218 while :; do 2219 ask "What timezone are you in? ('?' for list)" "$TZ" 2220 _zonepath=${resp%%*(/)} 2221 case $_zonepath in 2222 "") continue 2223 ;; 2224 "?") grep -v /. $_zonefile | show_cols 2225 continue 2226 ;; 2227 esac 2228 2229 while isin "$_zonepath/" $(<$_zonefile); do 2230 ask "What sub-timezone of '$_zonepath' are you in? ('?' for list)" 2231 _zsed=$(echo $_zonepath/ | sed 's,/,\\/,g') 2232 resp=${resp%%*(/)} 2233 case $resp in 2234 "") ;; 2235 "?") sed -n "/^$_zsed/{s/$_zsed//;/\/./!p;}" $_zonefile | show_cols;; 2236 *) _zonepath=$_zonepath/$resp;; 2237 esac 2238 done 2239 2240 if isin "$_zonepath" $(<$_zonefile); then 2241 TZ=${_zonepath#$_zoneroot} 2242 return 2243 fi 2244 2245 echo -n "'${_zonepath}'" 2246 echo " is not a valid timezone on this system." 2247 done 2248} 2249 2250# Determine if the supplied disk is a potential root disk, by: 2251# - Check the disklabel if there is an 'a' partition of type 4.2BSD 2252# - Mount the partition (read-only) and look for typical root filesystem layout 2253is_rootdisk() { 2254 local _d=$1 _rc=1 2255 2256 ( 2257 make_dev $_d 2258 if disklabel $_d | grep -q '^ a: .*4\.2BSD ' && 2259 mount -t ffs -r /dev/${_d}a /mnt; then 2260 ls -d /mnt/{bin,dev,etc,home,mnt,root,sbin,tmp,usr,var} 2261 _rc=$? 2262 umount -f /mnt 2263 fi 2264 rm -f /dev/{r,}$_d? 2265 return $_rc 2266 ) >/dev/null 2>&1 2267} 2268 2269# Get global root information. ie. ROOTDISK, ROOTDEV and SWAPDEV. 2270get_rootinfo() { 2271 local _default=$(get_dkdevs_root) _dkdev 2272 local _q="Which disk is the root disk? ('?' for details)" 2273 2274 while :; do 2275 echo "Available disks are: $(get_dkdevs_root | sed 's/^$/none/')." 2276 _ask "$_q" $_default || continue 2277 case $resp in 2278 "?") diskinfo $(get_dkdevs);; 2279 '') ;; 2280 *) # Translate $resp to disk dev name in case it is a DUID. 2281 # get_dkdev_name bounces back the disk dev name if not. 2282 _dkdev=$(get_dkdev_name "$resp") 2283 if isin "$_dkdev" $(get_dkdevs); then 2284 [[ $MODE == install ]] && break 2285 is_rootdisk "$_dkdev" && break 2286 echo "$resp is not a valid root disk." 2287 _default="$(rmel "$_dkdev" $_default) $_dkdev" 2288 else 2289 echo "no such disk" 2290 fi 2291 ;; 2292 esac 2293 $AI && exit 1 2294 done 2295 log_answers "$_q" "$resp" 2296 2297 make_dev $_dkdev || exit 2298 2299 ROOTDISK=$_dkdev 2300 ROOTDEV=${ROOTDISK}a 2301 SWAPDEV=${ROOTDISK}b 2302} 2303 2304# Parse and "unpack" a hostname.if(5) line given as positional parameters. 2305# Fill the _cmds array with the resulting interface configuration commands. 2306parse_hn_line() { 2307 local _af=0 _name=1 _mask=2 _bc=3 _prefix=2 _c _cmd _prev _daddr 2308 local _has_dhclient=false _has_inet6=false 2309 set -A _c -- "$@" 2310 set -o noglob 2311 2312 ifconfig $_if inet6 >/dev/null 2>&1 && _has_inet6=true 2313 [[ -x /sbin/dhclient ]] && _has_dhclient=true 2314 2315 case ${_c[_af]} in 2316 ''|*([[:blank:]])'#'*) 2317 return 2318 ;; 2319 inet) ((${#_c[*]} > 1)) || return 2320 [[ ${_c[_name]} == alias ]] && _mask=3 _bc=4 2321 [[ -n ${_c[_mask]} ]] && _c[_mask]="netmask ${_c[_mask]}" 2322 if [[ -n ${_c[_bc]} ]]; then 2323 _c[_bc]="broadcast ${_c[_bc]}" 2324 [[ ${_c[_bc]} == *NONE ]] && _c[_bc]= 2325 fi 2326 _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}" 2327 ;; 2328 inet6) ! $_has_inet6 && return 2329 ((${#_c[*]} > 1)) || return 2330 if [[ ${_c[_name]} == autoconf ]]; then 2331 _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}" 2332 V6_AUTOCONF=true 2333 return 2334 fi 2335 [[ ${_c[_name]} == alias ]] && _prefix=3 2336 [[ -n ${_c[_prefix]} ]] && _c[_prefix]="prefixlen ${_c[_prefix]}" 2337 _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}" 2338 ;; 2339 dest) ((${#_c[*]} == 2)) && _daddr=${_c[1]} || return 2340 ! $_has_inet6 && [[ $_daddr == @(*:*) ]] && return 2341 _prev=$((${#_cmds[*]} - 1)) 2342 ((_prev >= 0)) || return 2343 set -A _c -- ${_cmds[_prev]} 2344 _name=3 2345 [[ ${_c[_name]} == alias ]] && _name=4 2346 _c[_name]="${_c[_name]} $_daddr" 2347 _cmds[$_prev]="${_c[@]}" 2348 ;; 2349 dhcp) ! $_has_dhclient && return 2350 _c[0]= 2351 _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]} up;dhclient $_if" 2352 V4_DHCPCONF=true 2353 ;; 2354 '!'*|bridge) 2355 # Skip shell commands and bridge in the installer. 2356 return 2357 ;; 2358 *) _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}" 2359 ;; 2360 esac 2361 unset _c 2362 set +o noglob 2363} 2364 2365# Start interface using the on-disk hostname.if file passed as argument $1. 2366# Much of this is gratuitously stolen from /etc/netstart. 2367ifstart() { 2368 local _if=$1 _hn=/mnt/etc/hostname.$1 _cmds _i=0 _line 2369 set -A _cmds 2370 2371 # Create interface if it does not yet exist. 2372 { ifconfig $_if || ifconfig $_if create; } >/dev/null 2>&1 || return 2373 2374 ((NIFS++)) 2375 2376 # Parse the hostname.if(5) file and fill _cmds array with interface 2377 # configuration commands. 2378 set -o noglob 2379 while IFS= read -- _line; do 2380 parse_hn_line $_line 2381 done <$_hn 2382 2383 # Apply the interface configuration commands stored in _cmds array. 2384 while ((_i < ${#_cmds[*]})); do 2385 eval "${_cmds[_i]}" 2386 ((_i++)) 2387 done 2388 unset _cmds 2389 set +o noglob 2390} 2391 2392# Configure the network during upgrade based on the on-disk configuration. 2393enable_network() { 2394 local _f _gw _hn _if _trunks _svlans _vlans 2395 2396 # Use installed network configuration files during upgrade. 2397 for _f in resolv.conf resolv.conf.tail; do 2398 if [[ -f /mnt/etc/$_f ]]; then 2399 cp /mnt/etc/$_f /etc/$_f 2400 fi 2401 done 2402 2403 # Create a minimal hosts file. 2404 echo "127.0.0.1\tlocalhost" >/tmp/i/hosts 2405 echo "::1\t\tlocalhost" >>/tmp/i/hosts 2406 2407 _f=/mnt/etc/soii.key 2408 [[ -f $_f ]] && sysctl "net.inet6.ip6.soiikey=$(<$_f)" 2409 2410 # Set the address for the loopback interface. Bringing the 2411 # interface up, automatically invokes the IPv6 address ::1. 2412 ifconfig lo0 inet 127.0.0.1/8 2413 2414 # Configure all of the non-loopback interfaces which we know about. 2415 # Refer to hostname.if(5) 2416 for _hn in /mnt/etc/hostname.*; do 2417 # Strip off prefix to get interface name. 2418 _if=${_hn#/mnt/etc/hostname.} 2419 if isin "${_if%%+([0-9])}" $(ifconfig -C); then 2420 # Dynamic interfaces must be done later. 2421 case ${_if%%+([0-9])} in 2422 trunk) _trunks="$_trunks $_if";; 2423 svlan) _svlans="$_svlans $_if";; 2424 vlan) _vlans="$_vlans $_if";; 2425 esac 2426 else 2427 # 'Real' interfaces (if available) are done now. 2428 ifconfig $_if >/dev/null 2>&1 && ifstart $_if 2429 fi 2430 done 2431 # Configure any dynamic interfaces now that 'real' ones are up. 2432 # ORDER IS IMPORTANT! (see /etc/netstart). 2433 for _if in $_trunks $_svlans $_vlans; do 2434 ifstart $_if 2435 done 2436 2437 # /mnt/etc/mygate, if it exists, contains the address(es) of my 2438 # default gateway(s). Use for ipv4 if no interfaces configured via 2439 # dhcp. Use for ipv6 if no interfaces configured via autoconf. 2440 ! $V4_DHCPCONF && stripcom /mnt/etc/mygate | 2441 while read _gw; do 2442 [[ $_gw == @(*:*) ]] && continue 2443 route -qn add -host default $_gw && break 2444 done 2445 ! $V6_AUTOCONF && stripcom /mnt/etc/mygate | 2446 while read _gw; do 2447 [[ $_gw == !(*:*) ]] && continue 2448 route -qn add -host -inet6 default $_gw && break 2449 done 2450 2451 route -qn add -net 127 127.0.0.1 -reject >/dev/null 2452} 2453 2454# Fetch the list of mirror servers and installer choices from previous runs if 2455# available from ftplist.cgi. Start the ftp process in the background, but kill 2456# it if it takes longer than 12 seconds. 2457start_cgiinfo() { 2458 # If no networks are configured, we do not need the httplist file. 2459 ((NIFS < 1)) && return 2460 2461 # Ensure proper name resolution in case there's no dns yet. 2462 add_hostent 129.128.5.191 ftp.openbsd.org 2463 2464 # Make sure the ftp subshell gets its own process group. 2465 set -m 2466 ( 2467 unpriv2 ftp -w 15 -Vao - \ 2468 "$HTTP_PROTO://ftp.openbsd.org/cgi-bin/ftplist.cgi?dbversion=1" \ 2469 2>/dev/null >$CGI_INFO 2470 2471 # Remember finish time for adjusting the received timestamp. 2472 echo -n $SECONDS >$HTTP_SEC 2473 feed_random 2474 ) & CGIPID=$! 2475 set +m 2476 2477 # If the ftp process takes more than 12 seconds, kill it. 2478 # XXX We are relying on the pid space not randomly biting us. 2479 # XXX ftp could terminate early, and the pid could be reused. 2480 (sleep 12; kill -INT -$CGIPID >/dev/null 2>&1) & 2481} 2482 2483# Create a skeletal but useful /etc/fstab from /tmp/i/fstab by stripping all 2484# comment lines and dropping all filesystems which 2485# 2486# 1) can't be mounted (no mount_* command is found), 2487# 2) have 'xx' in the option field (usually /altroot), 2488# 3) have 'noauto' in the option field, 2489# 4) are nfs (since name resolution may not be present), 2490# 5) are on a vnd device. 2491# 2492# In addition, 2493# 2494# 1) delete 'softdep' options (no soft updates in ramdisk kernels), 2495# 2) mount non-ffs filesystems read only, 2496# 3) prepend '/mnt' to all mount points, 2497# 4) delete any trailing '/' from the mount point (e.g. root), 2498# 2499# If no /etc/fstab is created, do not proceed with install/upgrade. 2500munge_fstab() { 2501 local _dev _mp _fstype _opt _rest 2502 2503 while read _dev _mp _fstype _opt _rest; do 2504 # Drop irrelevant lines and filesystems. 2505 [[ $_dev == @(/dev/vnd*|\#*) || 2506 $_fstype == nfs || 2507 ! -f /sbin/mount_$_fstype || 2508 $_opt == *noauto* || 2509 $_opt == *xx* ]] && continue 2510 2511 # Remove any softdep options, as soft updates are not 2512 # available in the ramdisk kernels. 2513 _opt=$(echo $_opt | sed 's/softdep//') 2514 2515 # Change read-only ffs to read-write since we'll potentially 2516 # write to these filesystems. 2517 # Mount non-ffs filesystems read only. 2518 if [[ $_fstype == ffs ]]; then 2519 _opt=$(echo $_opt | sed 's/[[:<:]]ro[[:>:]]/rw/') 2520 else 2521 _opt=$(echo $_opt | sed 's/[[:<:]]rw[[:>:]]/ro/') 2522 fi 2523 2524 # Write fs entry in fstab. 2525 # 1) prepend '/mnt' to the mount point. 2526 # 2) remove a trailing '/' from the mount point (e.g. root). 2527 echo $_dev /mnt${_mp%/} $_fstype $_opt $_rest 2528 2529 done </tmp/i/fstab >/etc/fstab 2530 2531 # If no /etc/fstab was created, we have nowhere to $MODE to. 2532 if [[ ! -s /etc/fstab ]]; then 2533 echo "Unable to create valid /etc/fstab." 2534 exit 2535 fi 2536} 2537 2538# Preen all filesystems in /etc/fstab that have a /sbin/fsck_XXX and a 2539# fs_passno > 0, showing individual results, but skipping $ROOTDEV. This was 2540# already fsck'ed successfully. 2541# 2542# Exit if any fsck's fail (but do them all before exiting!). 2543check_fs() { 2544 local _dev _dn _mp _fstype _rest _fail _f _passno 2545 2546 ask_yn "Force checking of clean non-root filesystems?" && _f=f 2547 2548 while read _dev _mp _fstype _rest _rest _passno _rest; do 2549 _dn=$(get_dkdev_name "$_dev") 2550 [[ $ROOTDEV == @(${_dev#/dev/}|$_dn${_dev##*.}) ]] && continue 2551 [[ -f /sbin/fsck_$_fstype ]] || continue 2552 # Make sure device exists before fsck'ing it. 2553 make_dev "$_dn" || continue 2554 ((_passno > 0)) || continue 2555 echo -n "fsck -${_f}p $_dev..." 2556 if ! fsck -${_f}p $_dev >/dev/null 2>&1; then 2557 echo " FAILED. You must fsck $_dev manually." 2558 _fail=y 2559 else 2560 echo " OK." 2561 fi 2562 done </etc/fstab 2563 2564 [[ -n $_fail ]] && exit 2565} 2566 2567# Must mount filesystems manually, one at a time, so we can make sure the mount 2568# points exist. 2569mount_fs() { 2570 local _async=$1 _dev _mp _fstype _opt _rest _msg _fail 2571 2572 while read _dev _mp _fstype _opt _rest; do 2573 # If not the root filesystem, make sure the mount 2574 # point is present. 2575 [[ $_mp == /mnt ]] || mkdir -p $_mp 2576 2577 # Mount the filesystem. Remember any failure. 2578 _msg=$(mount -v -t $_fstype $_async -o $_opt $_dev $_mp) || 2579 _fail="$_fail\n$_mp ($_dev)" 2580 echo $_msg | sed 's/, ctime=[^,)]*//' 2581 done </etc/fstab 2582 2583 if [[ -n $_fail ]]; then 2584 # One or more mounts failed. Continue or abort? 2585 echo "\nWARNING! The following filesystems were not properly mounted:$_fail" 2586 ask_yn "Continue anyway?" || exit 2587 fi 2588} 2589 2590# Feed the random pool some entropy before we read from it. 2591feed_random() { 2592 (dmesg; cat $CGI_INFO /*.conf; sysctl; route -n show; df; 2593 ifconfig -A; hostname) >/dev/random 2>&1 2594 if [[ -e /mnt/var/db/host.random ]]; then 2595 dd if=/mnt/var/db/host.random of=/dev/random bs=65536 count=1 \ 2596 status=none 2597 fi 2598} 2599 2600# Ask the user for locations of sets, and then install whatever sets the user 2601# selects from that location. Repeat as many times as the user needs to get all 2602# desired sets. 2603install_sets() { 2604 local _cddevs=$(get_cddevs) _d _im _locs="disk http" _src 2605 2606 echo 2607 2608 # Set default location to method recorded last time. 2609 _d=$CGI_METHOD 2610 2611 # Set default location to HTTP in case we netbooted. 2612 ifconfig netboot >/dev/null 2>&1 && : ${_d:=http} 2613 2614 # Set default location to HTTP if installurl(5) exists. 2615 [[ -s /mnt/etc/installurl ]] && _d=http 2616 2617 # Set default location to the first cdrom device if any are found. 2618 [[ -n $_cddevs ]] && : ${_d:=cd0} 2619 2620 # Add NFS to set locations if the boot kernel supports it. 2621 [[ -x /sbin/mount_nfs ]] && _locs="$_locs nfs" 2622 2623 # In case none of the above applied, set HTTP as default location. 2624 : ${_d:=http} 2625 2626 # If the default location set so far is not one of the cdrom devices or 2627 # is not in the list of valid locations, set a sane default. 2628 if ! isin "$_d" $_cddevs $_locs; then 2629 for _src in http $_cddevs nfs disk; do 2630 isin "$_src" $_cddevs $_locs && _d=$_src && break 2631 done 2632 fi 2633 2634 echo "Let's $MODE the sets!" 2635 while :; do 2636 # Get list of cdroms again in case one just got plugged in. 2637 _cddevs=$(get_cddevs) 2638 umount -f /mnt2 >/dev/null 2>&1 2639 2640 ask "Location of sets? (${_cddevs:+$_cddevs }$_locs or 'done')" "$_d" 2641 case $resp in 2642 done) sane_install && return 2643 ;; 2644 [cC]*) if [[ -n $_cddevs ]]; then 2645 set -- $_cddevs 2646 [[ $resp == [cC]?([dD]) ]] && resp=$1 2647 _im=$resp 2648 install_cdrom $resp && INSTALL_METHOD=$_im 2649 fi 2650 ;; 2651 [dD]*) install_disk && INSTALL_METHOD=disk 2652 ;; 2653 [hH]*) isin http $_locs && install_http && INSTALL_METHOD=http 2654 ;; 2655 [nN]*) isin nfs $_locs && install_nfs && INSTALL_METHOD=nfs 2656 ;; 2657 *) $AI && err_exit "'$resp' is not a valid choice." 2658 ;; 2659 esac 2660 2661 # Preserve the selected install source selection. 2662 [[ -n $INSTALL_METHOD ]] && _d=$INSTALL_METHOD 2663 2664 # Set default to 'done' to leave the while-loop. 2665 sane_install quiet || $AI && _d=done 2666 done 2667} 2668 2669# Apply configuration settings based on the previously gathered information. 2670apply() { 2671 if [[ $START_SSHD == n ]]; then 2672 echo "sshd_flags=NO" >>/mnt/etc/rc.conf.local 2673 elif [[ -n $SSHD_ENABLEROOT ]]; then 2674 # Only change sshd_config if the user choice is not the default. 2675 if ! grep -q "^#PermitRootLogin $SSHD_ENABLEROOT\$" \ 2676 /mnt/etc/ssh/sshd_config; then 2677 sed -i "s/^#\(PermitRootLogin\) .*/\1 $SSHD_ENABLEROOT/" \ 2678 /mnt/etc/ssh/sshd_config 2679 fi 2680 fi 2681 2682 [[ -n $APERTURE ]] && 2683 echo "machdep.allowaperture=$APERTURE # See xf86(4)" \ 2684 >>/mnt/etc/sysctl.conf 2685 2686 [[ $START_XDM == y && -x /mnt/usr/X11R6/bin/xenodm ]] && 2687 echo "xenodm_flags=" >>/mnt/etc/rc.conf.local 2688 2689 if [[ $DEFCONS == y ]]; then 2690 cp /mnt/etc/ttys /tmp/i/ttys 2691 sed -e "/^$CTTY/s/std.9600/std.${CSPEED}/" \ 2692 -e "/^$CTTY/s/std.115200/std.${CSPEED}/" \ 2693 -e "/^$CTTY/s/unknown/vt220 /" \ 2694 -e "/$CTTY/s/off.*/on secure/" /tmp/i/ttys >/mnt/etc/ttys 2695 [[ -n $CPROM ]] && 2696 echo "stty $CPROM $CSPEED\nset tty $CPROM" \ 2697 >>/mnt/etc/boot.conf 2698 fi 2699 2700 ln -sf /usr/share/zoneinfo/$TZ /mnt/etc/localtime 2701} 2702 2703# Return string suitable for the encrypted password field in master.passwd. 2704# 2705# 1) Without argument, return a single '*'. 2706# 2) Return argument unchanged if it looks like a encrypted password string 2707# or if it consists of just 13 asterisks. 2708# 3) Otherwise return encrypted password string. 2709# 2710encr_pwd() { 2711 local _p=$1 2712 2713 if [[ -z $_p ]]; then 2714 echo '*' 2715 elif [[ $_p == \$2?\$[0-9][0-9]\$* && ${#_p} > 40 || 2716 $_p == '*************' ]]; then 2717 echo "$_p" 2718 else 2719 encrypt -b a -- "$_p" 2720 fi 2721} 2722 2723# Store entropy for the next boot. 2724store_random() { 2725 dd if=/dev/random of=/mnt/var/db/host.random bs=65536 count=1 \ 2726 status=none 2727 dd if=/dev/random of=/mnt/etc/random.seed bs=512 count=1 status=none 2728 chmod 600 /mnt/var/db/host.random /mnt/etc/random.seed 2729} 2730 2731# Final steps common for installs and upgrades. 2732finish_up() { 2733 local _dev _mp _fstype _rest _d 2734 local _kernel_dir=/mnt/usr/share/relink/kernel 2735 local _kernel=${MDKERNEL:-GENERIC} _syspatch_archs="amd64 arm64 i386" 2736 2737 # Mount all known swap partitions. This gives systems with little 2738 # memory a better chance at running 'MAKEDEV all'. 2739 if [[ -x /mnt/sbin/swapctl ]]; then 2740 /mnt/sbin/swapctl -a /dev/$SWAPDEV >/dev/null 2>&1 2741 # Can't do chmod && swapctl -A because devices are not yet 2742 # created on install'ed systems. On upgrade'ed system there 2743 # is a small chance the device does not exist on the ramdisk 2744 # and will thus not get mounted. 2745 while read _dev _mp _fstype _rest; do 2746 [[ $_fstype == swap ]] && 2747 /mnt/sbin/swapctl -a $_dev >/dev/null 2>&1 2748 done </mnt/etc/fstab 2749 fi 2750 2751 # Create /etc/installurl if it does not yet exist. 2752 if [[ ! -f /mnt/etc/installurl ]]; then 2753 echo "${INSTALL_URL:-https://cdn.openbsd.org/pub/OpenBSD}" \ 2754 >/mnt/etc/installurl 2755 fi 2756 2757 echo -n "Making all device nodes..." 2758 (cd /mnt/dev; sh MAKEDEV all 2759 # Make sure any devices we found during probe are created in the 2760 # installed system. 2761 for _dev in $(get_dkdevs) $(get_cddevs); do 2762 sh MAKEDEV $_dev 2763 done 2764 ) 2765 echo " done." 2766 2767 # We may run some programs in chroot, and some of them might be 2768 # dynamic. That is highly discouraged, but let us play it safe. 2769 rm -f /mnt/var/run/ld.so.hints 2770 2771 # Conditionally create /usr/{src,obj,xobj} directories and set 2772 # proper ownership and permissions during install. 2773 if [[ $MODE == install ]]; then 2774 mkdir -p /mnt/usr/{src,{,x}obj} && ( 2775 cd /mnt/usr 2776 chmod 770 {,x}obj 2777 chown build:wobj {,x}obj 2778 chmod 775 src 2779 chown root:wsrc src 2780 ) 2781 fi 2782 2783 # In case this is a softraid device, make sure all underlying 2784 # device nodes exist before installing boot-blocks on disk. 2785 make_dev $(bioctl $ROOTDISK 2>/dev/null | sed -n 's/.*<\(.*\)>$/\1/p') 2786 md_installboot $ROOTDISK 2787 2788 chmod og-rwx /mnt/bsd{,.mp,.rd} 2>/dev/null 2789 if [[ -f /mnt/bsd.mp ]] && ((NCPU > 1)); then 2790 _kernel=$_kernel.MP 2791 echo "Multiprocessor machine; using bsd.mp instead of bsd." 2792 mv /mnt/bsd /mnt/bsd.sp 2>/dev/null 2793 mv /mnt/bsd.mp /mnt/bsd 2794 fi 2795 2796 # Write kernel.SHA256 matching the just installed kernel and fix path to 2797 # ensure it references the kernel as /bsd. 2798 sha256 /mnt/bsd | (umask 077; sed 's,/mnt,,' >/mnt/var/db/kernel.SHA256) 2799 2800 if [[ -f $_kernel_dir.tgz ]]; then 2801 echo -n "Relinking to create unique kernel..." 2802 ( 2803 set -e 2804 rm -rf $_kernel_dir 2805 mkdir -m 700 -p $_kernel_dir 2806 tar -C $_kernel_dir -xzf $_kernel_dir.tgz $_kernel 2807 rm -f $_kernel_dir.tgz 2808 chroot /mnt /bin/ksh -e -c "cd ${_kernel_dir#/mnt}/$_kernel; \ 2809 make newbsd; make newinstall" 2810 ) >/dev/null 2>&1 && echo " done." || echo " failed." 2811 fi 2812 2813 # Ensure that sysmerge in batch mode is run on reboot. 2814 [[ $MODE == upgrade ]] && 2815 echo "/usr/sbin/sysmerge -b" >>/mnt/etc/rc.sysmerge 2816 2817 # If a proxy was needed to fetch the sets, use it for fw_update and syspatch 2818 [[ -n $http_proxy ]] && 2819 quote export "http_proxy=$http_proxy" >>/mnt/etc/rc.firsttime 2820 2821 # Ensure that fw_update is run on reboot. 2822 echo "/usr/sbin/fw_update -v" >>/mnt/etc/rc.firsttime 2823 2824 # Run syspatch -c on reboot if the arch is supported and if it is a 2825 # release system (not -stable or -current). List uninstalled syspatches 2826 # on the console and in the rc.firsttime output mail. 2827 isin "$ARCH" $_syspatch_archs && cat <<__EOT >>/mnt/etc/rc.firsttime 2828set -A _KERNV -- \$(sysctl -n kern.version | 2829 sed 's/^OpenBSD \([1-9][0-9]*\.[0-9]\)\([^ ]*\).*/\1 \2/;q') 2830if ((\${#_KERNV[*]} == 1)) && [[ -s /etc/installurl ]] && 2831 _CKPATCH=\$(mktemp /tmp/_ckpatch.XXXXXXXXXX); then 2832 echo "Checking for available binary patches..." 2833 syspatch -c > \$_CKPATCH 2834 if [[ -s \$_CKPATCH ]]; then 2835 echo "Run syspatch(8) to install:" 2836 column -xc 80 \$_CKPATCH 2837 fi 2838 rm -f \$_CKPATCH 2839fi 2840__EOT 2841 2842 # Email installer questions and their answers to root on next boot. 2843 prep_root_mail /tmp/i/$MODE.resp "$(hostname) $MODE response file" 2844 2845 if [[ -x /mnt/$MODE.site ]]; then 2846 if ! chroot /mnt /$MODE.site; then 2847 store_random 2848 err_exit "$MODE.site failed" 2849 fi 2850 fi 2851 2852 # Store entropy for the next boot. 2853 store_random 2854 2855 # Pat on the back. 2856 cat <<__EOT 2857 2858CONGRATULATIONS! Your OpenBSD $MODE has been successfully completed! 2859 2860__EOT 2861 [[ $MODE == install ]] && cat <<__EOT 2862When you login to your new system the first time, please read your mail 2863using the 'mail' command. 2864 2865__EOT 2866 2867 md_congrats 2868 $AI && >/tmp/ai/ai.done 2869} 2870 2871do_autoinstall() { 2872 rm -f /tmp/ai/ai.done 2873 2874 echo "Performing non-interactive $AI_MODE..." 2875 /$AI_MODE -af /tmp/ai/ai.$AI_MODE.conf 2>&1 </dev/null | 2876 tee /dev/stderr | sed "s/^.*$(echo '\r')//" >/tmp/ai/ai.log 2877 2878 $UU || [[ -f /tmp/ai/ai.done ]] || 2879 err_exit "failed; check /tmp/ai/ai.log" 2880 2881 # Email autoinstall protocol to root on next boot. 2882 prep_root_mail /tmp/ai/ai.log "$(hostname) $AI_MODE log" 2883 2884 exec reboot 2885} 2886 2887do_install() { 2888 local _rootkey _rootpass 2889 2890 # Ask for and set the system hostname and add the hostname specific 2891 # siteXX set. 2892 while :; do 2893 ask_until "System hostname? (short form, e.g. 'foo')" \ 2894 "$(hostname -s)" 2895 [[ $resp != *+([[:cntrl:]]|[[:space:]])* ]] && break 2896 echo "Invalid hostname." 2897 $AI && exit 1 2898 done 2899 [[ ${resp%%.*} != $(hostname -s) ]] && hostname "$resp" 2900 ALLSETS="$ALLSETS site$VERSION-$(hostname -s).tgz" 2901 export PS1='\h# ' 2902 2903 echo 2904 2905 # Configure the network. 2906 donetconfig 2907 2908 # Fetch list of mirror servers and installer choices from previous runs. 2909 start_cgiinfo 2910 2911 echo 2912 2913 while :; do 2914 ask_password "Password for root account?" 2915 _rootpass="$_password" 2916 [[ -n "$_password" ]] && break 2917 echo "The root password must be set." 2918 done 2919 2920 # Ask for the root user public ssh key during autoinstall. 2921 _rootkey= 2922 if $AI; then 2923 ask "Public ssh key for root account?" none 2924 [[ $resp != none ]] && _rootkey=$resp 2925 fi 2926 2927 # Ask user about daemon startup on boot, X Window usage and console 2928 # setup. 2929 questions 2930 2931 # Gather information for setting up the initial user account. 2932 user_setup 2933 ask_root_sshd 2934 2935 # Set TZ variable based on zonefile and user selection. 2936 set_timezone /var/tzlist 2937 2938 echo 2939 2940 # Get information about ROOTDISK, etc. 2941 get_rootinfo 2942 2943 DISKS_DONE= 2944 FSENT= 2945 2946 # Remove traces of previous install attempt. 2947 rm -f /tmp/i/fstab* 2948 2949 # Configure the disk(s). 2950 while :; do 2951 # Always do ROOTDISK first, and repeat until it is configured. 2952 if ! isin "$ROOTDISK" $DISKS_DONE; then 2953 resp=$ROOTDISK 2954 rm -f /tmp/i/fstab 2955 else 2956 # Force the user to think and type in a disk name by 2957 # making 'done' the default choice. 2958 ask_which "disk" "do you wish to initialize" \ 2959 '$(get_dkdevs_uninitialized)' done 2960 [[ $resp == done ]] && break 2961 fi 2962 _disk=$resp 2963 configure_disk $_disk || continue 2964 DISKS_DONE=$(addel $_disk $DISKS_DONE) 2965 done 2966 2967 # Write fstab entries to fstab in mount point alphabetic order 2968 # to enforce a rational mount order. 2969 for _mp in $(bsort $FSENT); do 2970 _pp=${_mp##*!} 2971 _mp=${_mp%!*} 2972 echo -n "$_pp $_mp ffs rw" 2973 2974 # Only '/' is neither nodev nor nosuid. i.e. it can obviously 2975 # *always* contain devices or setuid programs. 2976 [[ $_mp == / ]] && { echo " 1 1"; continue; } 2977 2978 # Every other mounted filesystem is nodev. If the user chooses 2979 # to mount /dev as a separate filesystem, then on the user's 2980 # head be it. 2981 echo -n ",nodev" 2982 2983 # The only directories that the install puts suid binaries into 2984 # (as of 3.2) are: 2985 # 2986 # /sbin 2987 # /usr/bin 2988 # /usr/sbin 2989 # /usr/libexec 2990 # /usr/libexec/auth 2991 # /usr/X11R6/bin 2992 # 2993 # and ports and users can do who knows what to /usr/local and 2994 # sub directories thereof. 2995 # 2996 # So try to ensure that only filesystems that are mounted at 2997 # or above these directories can contain suid programs. In the 2998 # case of /usr/libexec, give blanket permission for 2999 # subdirectories. 3000 case $_mp in 3001 /sbin|/usr) ;; 3002 /usr/bin|/usr/sbin) ;; 3003 /usr/libexec|/usr/libexec/*) ;; 3004 /usr/local|/usr/local/*) ;; 3005 /usr/X11R6|/usr/X11R6/bin) ;; 3006 *) echo -n ",nosuid" ;; 3007 esac 3008 echo " 1 2" 3009 done >>/tmp/i/fstab 3010 3011 # Create a skeletal /etc/fstab which is usable for the installation 3012 # process. 3013 munge_fstab 3014 3015 # Use async options for faster mounts of the filesystems. 3016 mount_fs "-o async" 3017 3018 # Feed the random pool some entropy before we read from it. 3019 feed_random 3020 3021 # Ask the user for locations, and install whatever sets the user 3022 # selected. 3023 install_sets 3024 3025 # Set 'wxallowed' mount option for the filesystem /usr/local resides on. 3026 _mp=$(df /mnt/usr/local | sed '$!d') 3027 _mp=${_mp##*/mnt} 3028 sed -i "s#\(${_mp:-/} ffs rw\)#\1,wxallowed#" /tmp/i/fstab 3029 3030 # If we did not succeed at setting TZ yet, we try again 3031 # using the timezone names extracted from the base set. 3032 if [[ -z $TZ ]]; then 3033 (cd /mnt/usr/share/zoneinfo 3034 ls -1dF $(tar cvf /dev/null [A-Za-y]*) >/mnt/tmp/tzlist ) 3035 echo 3036 set_timezone /mnt/tmp/tzlist 3037 rm -f /mnt/tmp/tzlist 3038 fi 3039 3040 # If we got a timestamp from the cgi server, and that time diffs by more 3041 # than 120 seconds, ask if the user wants to adjust the time. 3042 if _time=$(http_time) && _now=$(date +%s) && 3043 (( _now - _time > 120 || _time - _now > 120 )); then 3044 _tz=/mnt/usr/share/zoneinfo/$TZ 3045 if ask_yn "Time appears wrong. Set to '$(TZ=$_tz date -r "$(http_time)")'?" yes; then 3046 # We do not need to specify TZ below since both date 3047 # invocations use the same one. 3048 date $(date -r "$(http_time)" "+%Y%m%d%H%M.%S") >/dev/null 3049 # N.B. This will screw up SECONDS. 3050 fi 3051 fi 3052 3053 # If we managed to talk to the cgi server before, tell it what 3054 # location we used... so it can perform magic next time. 3055 if [[ -s $HTTP_LIST ]]; then 3056 _i=${INSTALL_URL:+install=$INSTALL_URL&} 3057 _i=$_i${TZ:+TZ=$TZ&} 3058 _i=$_i${INSTALL_METHOD:+method=$INSTALL_METHOD} 3059 _i=${_i%&} 3060 [[ -n $_i ]] && unpriv2 ftp -w 15 -Vao - \ 3061 "$HTTP_PROTO://ftp.openbsd.org/cgi-bin/ftpinstall.cgi?dbversion=1&$_i" \ 3062 >/dev/null 2>&1 & 3063 fi 3064 3065 # Ensure an enabled console has the correct speed in /etc/ttys. 3066 sed "/^console.*on.*secure.*$/s/std\.[0-9]*/std.$(stty speed </dev/console)/" \ 3067 /mnt/etc/ttys >/tmp/i/ttys 3068 mv /tmp/i/ttys /mnt/etc/ttys 3069 3070 echo -n "Saving configuration files..." 3071 3072 # Save any leases obtained during install. 3073 (cd /var/db; for _f in dhclient.leases.*; do 3074 [[ -f $_f ]] && mv $_f /mnt/var/db/. 3075 done) 3076 3077 # Move configuration files from /tmp/i/ to /mnt/etc. 3078 hostname >/tmp/i/myname 3079 3080 # Append entries to installed hosts file, changing '1.2.3.4 hostname' 3081 # to '1.2.3.4 hostname.$FQDN hostname'. Leave untouched lines containing 3082 # domain information or aliases. These are lines the user added/changed 3083 # manually. 3084 3085 # Add common entries. 3086 echo "127.0.0.1\tlocalhost" >/mnt/etc/hosts 3087 echo "::1\t\tlocalhost" >>/mnt/etc/hosts 3088 3089 # Note we may have no hosts file if no interfaces were configured. 3090 if [[ -f /tmp/i/hosts ]]; then 3091 # Remove the entry for ftp.openbsd.org 3092 sed -i '/^129\.128\.5\.191 /d' /tmp/i/hosts 3093 _dn=$(get_fqdn) 3094 while read _addr _hn _aliases; do 3095 if [[ -n $_aliases || $_hn != ${_hn%%.*} || -z $_dn ]]; then 3096 echo "$_addr\t$_hn $_aliases" 3097 else 3098 echo "$_addr\t$_hn.$_dn $_hn" 3099 fi 3100 done </tmp/i/hosts >>/mnt/etc/hosts 3101 rm /tmp/i/hosts 3102 fi 3103 3104 # Possible files to copy from /tmp/i/: fstab hostname.* kbdtype mygate 3105 # myname ttys boot.conf resolv.conf sysctl.conf resolv.conf.tail 3106 # Save only non-empty (-s) regular (-f) files. 3107 (cd /tmp/i; for _f in fstab hostname* kbdtype my* ttys *.conf *.tail; do 3108 [[ -f $_f && -s $_f ]] && mv $_f /mnt/etc/. 3109 done) 3110 3111 echo " done." 3112 3113 # Apply configuration settings based on information from questions(). 3114 apply 3115 3116 # Create user account based on information from user_setup(). 3117 if [[ -n $ADMIN ]]; then 3118 _encr=$(encr_pwd "$ADMIN_PASS") 3119 _home=/home/$ADMIN 3120 uline="${ADMIN}:${_encr}:1000:1000:staff:0:0:${ADMIN_NAME}:$_home:/bin/ksh" 3121 echo "$uline" >>/mnt/etc/master.passwd 3122 echo "${ADMIN}:*:1000:" >>/mnt/etc/group 3123 echo $ADMIN >/mnt/root/.forward 3124 3125 _home=/mnt$_home 3126 mkdir -p $_home 3127 (cd /mnt/etc/skel; pax -rw -k -pe . $_home) 3128 (umask 077 && sed "s,^To: root\$,To: ${ADMIN_NAME} <${ADMIN}>," \ 3129 /mnt/var/mail/root >/mnt/var/mail/$ADMIN ) 3130 chown -R 1000:1000 $_home /mnt/var/mail/$ADMIN 3131 sed -i -e "s@^wheel:.:0:root\$@wheel:\*:0:root,${ADMIN}@" \ 3132 /mnt/etc/group 2>/dev/null 3133 3134 # During autoinstall, add public ssh key to authorized_keys. 3135 [[ -n "$ADMIN_KEY" ]] && 3136 print -r -- "$ADMIN_KEY" >>$_home/.ssh/authorized_keys 3137 fi 3138 3139 # Store root password and rebuild password database. 3140 if [[ -n "$_rootpass" ]]; then 3141 _encr=$(encr_pwd "$_rootpass") 3142 sed -i -e "s@^root::@root:${_encr}:@" /mnt/etc/master.passwd \ 3143 2>/dev/null 3144 fi 3145 pwd_mkdb -p -d /mnt/etc /etc/master.passwd 3146 3147 # During autoinstall, add root user's public ssh key to authorized_keys. 3148 [[ -n "$_rootkey" ]] && ( 3149 umask 077 3150 print -r -- "$_rootkey" >>/mnt/root/.ssh/authorized_keys 3151 ) 3152 3153 # Perform final steps common to both an install and an upgrade. 3154 finish_up 3155} 3156 3157do_upgrade() { 3158 local _f 3159 3160 # Get $ROOTDISK and $ROOTDEV 3161 get_rootinfo 3162 3163 echo -n "Checking root filesystem (fsck -fp /dev/$ROOTDEV)..." 3164 fsck -fp /dev/$ROOTDEV >/dev/null 2>&1 || { echo "FAILED."; exit; } 3165 echo " OK." 3166 3167 echo -n "Mounting root filesystem (mount -o ro /dev/$ROOTDEV /mnt)..." 3168 mount -o ro /dev/$ROOTDEV /mnt || { echo "FAILED."; exit; } 3169 echo " OK." 3170 3171 # The fstab and myname files are required. 3172 for _f in /mnt/etc/{fstab,myname}; do 3173 [[ -f $_f ]] || { echo "No $_f!"; exit; } 3174 cp $_f /tmp/i/${_f##*/} 3175 done 3176 3177 # Set system hostname and register hostname specific site set. 3178 hostname $(stripcom /tmp/i/myname) 3179 ALLSETS="$ALLSETS site$VERSION-$(hostname -s).tgz" 3180 export PS1='\h# ' 3181 3182 # Configure the network. 3183 enable_network 3184 3185 # Fetch list of mirror servers and installer choices from previous runs. 3186 start_cgiinfo 3187 3188 # Create a skeletal /etc/fstab which is usable for the upgrade process. 3189 munge_fstab 3190 3191 # fsck -p non-root filesystems in /etc/fstab. 3192 check_fs 3193 3194 # Mount filesystems in /etc/fstab. 3195 umount /mnt || { echo "Can't umount $ROOTDEV!"; exit; } 3196 mount_fs 3197 3198 rm -f /mnt/bsd.upgrade /mnt/auto_upgrade.conf 3199 3200 # Feed the random pool some entropy before we read from it. 3201 feed_random 3202 3203 # Ensure that previous installer choices (e.g. method) are available. 3204 wait_cgiinfo 3205 3206 # Ask the user for locations, and install whatever sets the user 3207 # selected. 3208 install_sets 3209 3210 # Perform final steps common to both an install and an upgrade. 3211 finish_up 3212} 3213 3214check_unattendedupgrade() { 3215 local _d=$(get_dkdevs_root) _rc=1 3216 3217 _d=${_d%% *} 3218 ( 3219 if [[ -n $_d ]]; then 3220 make_dev $_d 3221 if mount -t ffs -r /dev/${_d}a /mnt; then 3222 [[ -f /mnt/bsd.upgrade && -f /mnt/auto_upgrade.conf ]] 3223 _rc=$? 3224 ((_rc == 0)) && cp /mnt/auto_upgrade.conf / 3225 umount -f /mnt 3226 fi 3227 rm -f /dev/{r,}$_d? 3228 fi 3229 return $_rc 3230 ) > /dev/null 2>&1 3231} 3232 3233WATCHDOG_PERIOD_SEC=$((30 * 60)) 3234 3235# Restart the background timer. 3236reset_watchdog() { 3237 kill -KILL $WDPID 2>/dev/null 3238 start_watchdog 3239} 3240 3241# Start a co-process to reboot a stalled sysupgrade. 3242# This mechanism is only used during non-interactive sysupgrade. 3243start_watchdog() { 3244 ( 3245 sleep $WATCHDOG_PERIOD_SEC && reboot 3246 ) |& 3247 WDPID=$! 3248 3249 # Close standard input of the co-process. 3250 exec 3>&p; exec 3>&- 3251} 3252 3253# ------------------------------------------------------------------------------ 3254# Initial actions common to both installs and upgrades. 3255# 3256# Some may require machine dependent routines, which may call functions defined 3257# above, so it's safest to put this code here rather than at the top. 3258# ------------------------------------------------------------------------------ 3259 3260# Parse parameters. 3261AI=false 3262UU=false 3263MODE= 3264PROGNAME=${0##*/} 3265AI_RESPFILE= 3266while getopts "af:m:x" opt; do 3267 case $opt in 3268 a) AI=true;; 3269 f) AI_RESPFILE=$OPTARG;; 3270 m) MODE=$OPTARG;; 3271 x) UU=true;; 3272 *) usage;; 3273 esac 3274done 3275shift $((OPTIND-1)) 3276(($# == 0)) || usage 3277 3278# The installer can be started by using the symbolic links 'install', 'upgrade' 3279# and 'autoinstall' pointing to this script. Set MODE and AI based on that. 3280if [[ -z $MODE ]]; then 3281 case $PROGNAME in 3282 autoinstall) AI=true;; 3283 install|upgrade) MODE=$PROGNAME;; 3284 *) exit 1;; 3285 esac 3286fi 3287 3288# Do not limit ourselves during installs or upgrades. 3289for _opt in d f l m n p s; do 3290 ulimit -$_opt unlimited 3291done 3292 3293# umount all filesystems, just in case we are re-running install or upgrade. 3294cd / 3295umount -af >/dev/null 2>&1 3296 3297# Include machine-dependent functions and definitions. 3298# 3299# The following functions must be provided: 3300# md_congrats() - display friendly message 3301# md_installboot() - install boot-blocks on disk 3302# md_prep_disklabel() - put an OpenBSD disklabel on the disk 3303# md_consoleinfo() - set CDEV, CTTY, CSPEED, CPROM 3304# 3305# The following variables can be provided if required: 3306# MDEFI - set to 'y' on archs that support GPT partitioning 3307# MDFSOPT - newfs options for non-root partitions, '-O2' assumed if not provided 3308# MDROOTFSOPT - newfs options for the root partition, '-O2' assumed if not provided 3309# MDSETS - list of files to add to DEFAULT and ALLSETS 3310# MDSANESETS - list of files to add to SANESETS 3311# MDTERM - 'vt220' assumed if not provided 3312# MDDKDEVS - '/^[sw]d[0-9][0-9]* /s/ .*//p' assumed if not provided 3313# MDCDDEVS - '/^cd[0-9][0-9]* /s/ .*//p' assumed if not provided 3314# MDXAPERTURE - set machdep.allowaperture=value in sysctl.conf 3315# MDXDM - ask if xdm should be started if set to 'y' 3316# NCPU - the number of cpus for mp capable arches 3317# MDKERNEL - the name of the boot kernel 3318# MDHALT - default to 'halt' at the end of installs if set to 'y' 3319. install.md 3320 3321# Start listener process looking for dmesg changes. 3322start_dmesg_listener 3323 3324CGI_INFO=/tmp/i/cgiinfo 3325CGI_METHOD= 3326CGI_TIME= 3327CGI_TZ= 3328export EDITOR=ed 3329HTTP_DIR= 3330HTTP_LIST=/tmp/i/httplist 3331HTTP_SEC=/tmp/i/httpsec 3332INSTALL_METHOD= 3333NIFS=0 3334export PS1="$MODE# " 3335PUB_KEY=/etc/signify/openbsd-${VERSION}-base.pub 3336ROOTDEV= 3337ROOTDISK= 3338SETDIR="$VNAME/$ARCH" 3339UPGRADE_BSDRD=false 3340V4_DHCPCONF=false 3341V6_AUTOCONF=false 3342WLANLIST=/tmp/i/wlanlist 3343 3344# Save one boot's worth of dmesg. 3345dmesgtail >/var/run/dmesg.boot 3346 3347# Are we in a real release, or a snapshot? If this is a snapshot 3348# install media, default us to a snapshot directory. 3349HTTP_SETDIR=$SETDIR 3350set -- $(scan_dmesg "/^OpenBSD $VNAME\([^ ]*\).*$/s//\1/p") 3351[[ $1 == -!(stable) ]] && HTTP_SETDIR=snapshots/$ARCH 3352 3353# Detect if ftp(1) has tls support and set defaults based on that. 3354if [[ -e /etc/ssl/cert.pem ]]; then 3355 FTP_TLS=true 3356 HTTP_PROTO=https 3357else 3358 FTP_TLS=false 3359 HTTP_PROTO=http 3360fi 3361 3362# Scan /var/run/dmesg.boot for console device. 3363CONSOLE=$(scan_dmesg '/^\([^ ]*\).*: console$/s//\1/p') 3364[[ -n $CONSOLE ]] && CSPEED=$(stty speed </dev/console) 3365 3366# Look for the serial device matching the console. If we are not installing 3367# from a serial console, just find the first serial device that could be used 3368# as a console. If a suitable device is found, set CDEV, CTTY, CSPEED, CPROM. 3369md_consoleinfo 3370 3371# Selected sets will be installed in the order they are listed in $ALLSETS. 3372# Ensure that siteXX.tgz is the *last* set listed so its contents overwrite 3373# the contents of the other sets, not the other way around. 3374SETS=$(echo {base,comp,man,game,xbase,xshare,xfont,xserv}$VERSION.tgz) 3375DEFAULTSETS="${MDSETS:-bsd bsd.rd} $SETS" 3376ALLSETS="${MDSETS:-bsd bsd.rd} $SETS site$VERSION.tgz" 3377SANESETS="${MDSANESETS:-bsd} base${VERSION}.tgz" 3378if ((NCPU > 1)); then 3379 DEFAULTSETS="${MDSETS:-bsd bsd.mp bsd.rd} $SETS" 3380 ALLSETS="${MDSETS:-bsd bsd.mp bsd.rd} $SETS site$VERSION.tgz" 3381 SANESETS="${MDSANESETS:-bsd bsd.mp} base${VERSION}.tgz" 3382fi 3383 3384# Prepare COLUMNS sanely. 3385export COLUMNS=$(stty -a </dev/console | 3386 sed -n '/columns/{s/^.* \([0-9]*\) columns.*$/\1/;p;}') 3387((COLUMNS == 0)) && COLUMNS=80 3388 3389# Interactive or automatic installation? 3390if ! $AI; then 3391 cat <<__EOT 3392At any prompt except password prompts you can escape to a shell by 3393typing '!'. Default answers are shown in []'s and are selected by 3394pressing RETURN. You can exit this program at any time by pressing 3395Control-C, but this can leave your system in an inconsistent state. 3396 3397__EOT 3398elif $UU; then 3399 MODE=upgrade 3400 check_unattendedupgrade || exit 1 3401 3402 start_watchdog 3403 3404 get_responsefile 3405 do_autoinstall 3406elif [[ -z $AI_RESPFILE ]]; then 3407 get_responsefile || 3408 err_exit "No response file found; non-interactive mode aborted." 3409 3410 do_autoinstall 3411else 3412 cp $AI_RESPFILE /tmp/ai/ai.conf || exit 3413fi 3414 3415# Configure the terminal and keyboard. 3416set_term 3417 3418# In case of restart, delete previously logged answers. 3419rm -f /tmp/i/$MODE.resp 3420 3421case $MODE in 3422install) do_install;; 3423upgrade) do_upgrade;; 3424esac 3425 3426# In case of autoinstall, this is a second process of install.sub. 3427# Exiting here returns to the original process, which handles the 3428# automatic reboot in do_autoinstall(). 3429$AI && exit 3430 3431_d=reboot 3432[[ $MODE == install && $MDHALT == y ]] && _d=halt 3433 3434while :; do 3435 ask "Exit to (S)hell, (H)alt or (R)eboot?" "$_d" 3436 case $resp in 3437 [hH]*) exec halt;; 3438 [rR]*) exec reboot;; 3439 [sS]*) break;; 3440 esac 3441done 3442 3443# Fall through to .profile which leaves us at the command prompt. 3444echo "To boot the new system, enter 'reboot' at the command prompt." 3445