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