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