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