# $OpenBSD: install.sub,v 1.653 2011/10/18 19:02:44 matthieu Exp $ # $NetBSD: install.sub,v 1.5.2.8 1996/09/02 23:25:02 pk Exp $ # # Copyright (c) 1997-2009 Todd Miller, Theo de Raadt, Ken Westerback # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Copyright (c) 1996 The NetBSD Foundation, Inc. # All rights reserved. # # This code is derived from software contributed to The NetBSD Foundation # by Jason R. Thorpe. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # OpenBSD install/upgrade script common subroutines and initialization code # Include machine-dependent functions and definitions. # # The following functions must be provided: # md_congrats() - display friendly message # md_installboot() - install boot-blocks on disk # md_prep_disklabel() - put an OpenBSD disklabel on the disk # md_consoleinfo() - set CDEV, CTTY, CSPEED, CPROM # # The following variables can be provided if required: # MDSETS - list of files to add to THESETS # MDTERM - 'vt220' assumed if not provided # MDDKDEVS - '/^[sw]d[0-9][0-9]* /s/ .*//p' assumed if not provided # MDCDDEVS - '/^cd[0-9][0-9]* /s/ .*//p' assumed if not provided # MDMTDEVS - '/^[cms]t[0-9][0-9]* /s/ .*//p' # MDXAPERTURE - set machdep.allowaperture=value in sysctl.conf # NCPU - the number of cpus for mp capable arches . install.md set_term() { local _layouts export TERM=${TERM:-${MDTERM:-vt220}} if [[ -n $CONSOLE ]]; then ask "Terminal type?" $TERM export TERM=$resp else [[ -x /sbin/kbd ]] || return _layouts=$(bsort $(kbd -l | egrep -v "^(user|tables|encoding)")) while :; do ask "Choose your keyboard layout ('?' or 'L' for list)" "default" case $resp in "?"|L|l) echo "Available layouts: $_layouts" ;; default) return ;; *) kbd $resp && { echo $resp >/tmp/kbdtype ; return ; } ;; esac done fi } # Echo the file $1 to standard output, skipping any lines that begin with a # '#'. i.e. strip comment lines from the file. stripcom () { local _l [[ -f $1 ]] || return while read _l; do [[ -n ${_l%%#*} ]] && echo $_l done <$1 } # Prints the supplied parameters properly escaped for future sh/ksh parsing. # Quotes are added if needed, so you should not do that yourself. quote() ( # Since this is a subshell we won't pollute the calling namespace for a; do alias Q=$a; a=$(alias Q); print -rn -- " ${a#Q=}" done | sed '1s/ //' echo ) scan_dmesg() { bsort $(sed -ne "$1" /var/run/dmesg.boot) } scan_disknames() { local _n _oifs=$IFS IFS="," bsort $(for _n in $(sysctl -n hw.disknames); do echo "${_n%%:*} "; done | sed -ne "$1") IFS=$_oifs } get_dkdevs () { echo $(scan_disknames "${MDDKDEVS:-/^[sw]d[0-9][0-9]* /s/ .*//p}") } get_cddevs () { echo $(scan_disknames "${MDCDDEVS:-/^cd[0-9][0-9]* /s/ .*//p}") } get_ifdevs() { ifconfig "$@" 2>&- \ | egrep -v '^[[:space:]]|(bridge|enc|gif|gre|lo|pflog|pfsync|ppp|sl|tun)[[:digit:]]+:' \ | sed -ne 's/^\(.*\):.*/\1/p' } get_drive() { ask_which "$1" "contains the $MODE media" "$2" "$3" [[ $resp == done ]] && return 1 makedev $resp || return 1 return 0 } mount_mnt2() { local _dev=$1 _opts _file=/tmp/parts.$1 _parts disklabel $_dev 2>/dev/null | grep '^ [a-p]: ' \ | egrep -v "swap|unused" >$_file _parts=$(sed -e 's/^ \(.\): .*/\1/' $_file) set -- $_parts [[ $# == 0 ]] && { echo "No filesystems found on $_dev." ; return 1 ; } if isin "c" $_parts; then # Don't ask questions if 'c' contains a filesystem. resp=c elif [[ $# == 1 ]]; then # Don't ask questions if there's only one choice. resp=$1 else # Display partitions with filesystems and ask which to use. cat /tmp/parts.$_dev ask_which "$_dev partition" "has the $MODE sets" \ '$(disklabel '$_dev' 2>/dev/null | grep "^ [a-p]: " | egrep -v "swap|unused" | sed '\''s/^ \(.\): .*/\1/'\'')' [[ $resp == done ]] && return 1 fi # Always mount msdos partitions with -s to get lower case names. grep -q "^ $resp: .*MSDOS" $_file && _opts="-s" mount -o ro,$_opts /dev/$_dev$resp /mnt2 } # Ask for a password, saving the input in $resp. # Display $1 as the prompt. # *Don't* allow the '!' options that ask does. # *Don't* echo input. # *Don't* interpret "\" as escape character. askpass() { set -o noglob stty -echo read -r resp?"$1 " stty echo set +o noglob echo } # Make sure lock is initially released rm -df /tmp/lock # Acquire lock lock() { while ! mkdir /tmp/lock 2>&- && sleep .1; do done } # Release lock unlock() { rm -d /tmp/lock 2>&- } # Add trap to kill the listener process retrap() { trap '>&- && kill -KILL $cppid 2>&-; echo; stty echo; exit 0' \ INT EXIT TERM } # The dmesg listener will check for the existance of this file and send a # signal to the child process if the dmesg output differs from the contents # of that file rm -f /tmp/update # Start listener process looking for dmesg changes ( while :; do lock if test -e /tmp/update && [[ "`dmesg`" != "`cat /tmp/update`" ]]; then dmesg >/tmp/update kill -TERM 2>&- $$ || exit 1 fi unlock sleep .5 done ) |& cppid=$! # Kill the child on exit retrap # Issue a read into the global variable $resp. If the dmesg output is # changed while inside this function, the current read will be aborted # and the function will return a non-zero value. Normally, the caller # will then reprint any prompt and call the function again. _ask() { local _int _redo=0 _pid trap "_int=1" INT trap "_redo=1" TERM lock; dmesg >/tmp/update; unlock read resp lock; rm /tmp/update; unlock if (( _redo )); then stty raw stty -raw else case $resp in !) echo "Type 'exit' to return to install." sh _redo=1 ;; !*) eval "${resp#?}" _redo=1 ;; esac fi retrap (( _int )) && kill -INT $$ return $_redo } # Ask for user input. # # $1 = the question to ask the user # $2 = the default answer # # Save the user input (or the default) in $resp. # # Allow the user to escape to shells ('!') or execute commands # ('!foo') before entering the input. ask() { local _question=$1 _default=$2 while :; do echo -n "$_question " [[ -z $_default ]] || echo -n "[$_default] " _ask && : ${resp:=$_default} && break done } # Ask for a password twice, saving the input in $_password askpassword() { local _oifs=$IFS IFS= while :; do askpass "Password for $1 account? (will not echo)" _password=$resp askpass "Password for $1 account? (again)" # N.B.: Need quotes around $resp and $_password to preserve leading # or trailing spaces. [[ "$resp" == "$_password" ]] && break echo "Passwords do not match, try again." done IFS=$_oifs } user_setup() { local _q="Setup a user? (enter a lower-case loginname, or 'no')" while :; do ask "$_q" no case $resp in n|no) return ;; y|yes) _q="No really, what is the lower-case loginname, or 'no'?" continue ;; root|daemon|operator|bin|smmsp|popa3d) ;; sshd|uucp|www|named|proxy|nobody|ftp) ;; [a-z]*([a-z0-9_])) (( ${#resp} <= 31 )) && break ;; esac echo "$resp is not a useable loginname." done user=$resp while :; do ask "Full user name for $user?" $user case $resp in *[:\&,]*) echo "':', '&' or ',' are not allowed." ;; *) (( ${#resp} <= 100 )) && break echo "Too long." ;; esac done username=$resp askpassword $user userpass=$_password if [[ $sshd == y ]]; then ask_yn "Since you set up a user, disable sshd(8) logins to root?" yes sshd_disableroot=$resp fi } # Ask for user input until a non-empty reply is entered. # # $1 = the question to ask the user # $2 = the default answer # # Save the user input (or the default) in $resp. ask_until() { resp= while [[ -z $resp ]] ; do ask "$1" "$2" done } # Ask the user for a y or n, and insist on 'y', 'yes', 'n' or 'no'. # # $1 = the question to ask the user # $2 = the default answer (assumed to be 'n' if empty). # # Return 'y' or 'n' in $resp. ask_yn() { local _q=$1 _a=${2:-no} _resp typeset -l _resp while :; do ask "$_q" "$_a" _resp=$resp case $_resp in y|yes) resp=y ; return ;; n|no) resp=n ; return ;; esac done } # Ask for the user to select one value from a list, or 'done'. # # $1 = name of the list items (disk, cd, etc.) # $2 = question to ask # $3 = list of valid choices # $4 = default choice, if it is not specified use the first item in $3 # # N.B.! $3 and $4 will be "expanded" using eval, so be sure to escape them # if they contain spooky stuff # # At exit $resp holds selected item, or 'done' ask_which() { local _name=$1 _query=$2 _list=$3 _def=$4 _dynlist _dyndef while :; do # Put both lines in ask prompt, rather than use a # separate 'echo' to ensure the entire question is # re-ask'ed after a '!' or '!foo' shell escape. eval "_dynlist=\"$_list\"" eval "_dyndef=\"$_def\"" # Clean away whitespace and determine the default set -o noglob set -- $_dyndef; _dyndef="$1" set -- $_dynlist; _dynlist="$*" set +o noglob (( $# < 1 )) && resp=done && return : ${_dyndef:=$1} echo "Available ${_name}s are: $_dynlist." echo -n "Which one $_query? (or 'done') " [[ -n $_dyndef ]] && echo -n "[$_dyndef] " _ask || continue [[ -z $resp ]] && resp="$_dyndef" # Quote $resp to prevent user from confusing isin() by # entering something like 'a a'. isin "$resp" $_dynlist done && break echo "'$resp' is not a valid choice." done } # test the first argument against the remaining ones, return success on a match isin() { local _a=$1 _b shift for _b; do [[ $_a == $_b ]] && return 0 done return 1 } # add first argument to list formed by the remaining arguments # adds to the tail if the element does not already exist addel() { local _a=$1 shift echo -n "$*" isin "$_a" $* || echo -n " $_a" } # remove all occurrences of first argument from list formed by # the remaining arguments rmel() { local _a=$1 _b shift for _b; do [[ $_a != $_b ]] && echo -n "$_b " done } bsort() { local _l _a=$1 _b [[ $# -gt 0 ]] || return shift for _b; do if [[ $_a != $_b ]] ; then if [[ $_a > $_b ]] ; then _l="$_a $_l"; _a=$_b else _l="$_b $_l" fi fi done # Output the smallest value found. echo -n "$_a " # Sort remaining values. bsort $_l } # show a list (passed via ordered arguments) in column output using ls showcols() { local _l _cdir=/tmp/cdir set -A _clist mkdir -p $_cdir rm -rf -- $_cdir/* while read _l; do [ "$_l" ] || continue mkdir -p /tmp/cdir/"$_l" _clist[${#_clist[*]}]="$_l" done (cd $_cdir; ls -Cdf "${_clist[@]}") rm -rf -- $_cdir } # Offer to shell out for manual network configuration, and do so if # the user accepts the offer. manual_net_cfg() { ask_yn "Do you want to do any manual network configuration?" [[ $resp == y ]] && { echo "Type 'exit' to return to $MODE." ; sh ; } } # Create a device. # # $1 = name of the device to create. makedev() { local _dev=$1 if [[ ! -r /dev/MAKEDEV ]] ; then echo "MAKEDEV not found. Can't create device nodes." return 1 fi (cd /dev && sh MAKEDEV "$_dev") >/dev/null 2>&1 } # Create an entry in the hosts file. If an entry with the # same symbolic name and address family already exists, delete it. # $1 - IP address (v6 if it contains ':', else v4) # $2 - symbolic name addhostent() { local _addr=$1 _name=$2 _delim="." [[ -z $_addr || -z $_name ]] && return [[ $_addr == *:* ]] && _delim=":" sed "/^[0-9a-fA-F]*[$_delim].*[ ]$_name\$/d" /tmp/hosts \ >/tmp/hosts.new 2>/dev/null mv /tmp/hosts.new /tmp/hosts echo "$_addr $_name" >>/tmp/hosts } # Show list of available sets and let the user select which sets to install. # # $1 = available sets # $2 = already selected sets # # Set $resp to list of selected sets. select_sets() { local _avail=$1 _selected=$2 _f _action _col=$COLUMNS # account for 4 spaces added to the sets list let COLUMNS=_col-8 cat <<__EOT Select sets by entering a set name, a file name pattern or 'all'. De-select sets by prepending a '-' to the set name, file name pattern or 'all'. Selected sets are labelled '[X]'. __EOT while :; do for _f in $_avail; do isin $_f $_selected && echo "[X] $_f" || echo "[ ] $_f" done | showcols | sed 's/^/ /' ask "Set name(s)? (or 'abort' or 'done')" done set -o noglob for resp in $resp; do case $resp in abort) _selected=; break 2 ;; done) break 2 ;; -*) _action=rmel ;; *) _action=addel ;; esac resp=${resp#[+-]} [[ $resp = all ]] && resp=* for _f in $_avail; do [[ $_f = $resp ]] && _selected=$($_action $_f $_selected) done done done set +o noglob COLUMNS=$_col resp=$_selected } configure_ifs() { local _first _ifdevs _ifs _name _hn _vl=0 _vd _vi _p _tags # In case of restart, discover last vlan configured. while :; do _vd=$(ifconfig vlan$_vl 2>&1) [[ $_vd == @(*no such interface*) ]] && break [[ $_vd == @(vlan$_vl: flags=0<>*) ]] && break : $(( _vl++ )) done _vd= while :; do # Create new vlan if possible. ifconfig vlan$_vl create >/dev/null 2>&1 ask_which "network interface" "do you wish to configure" \ '$(get_ifdevs)' \ ${_p:-'$( (ifconfig netboot 2>/dev/null | sed -n '\''1s/:.*//p'\''; get_ifdevs) | sed q )'} [[ $resp == done ]] && break _ifs=$resp _hn=/tmp/hostname.$_ifs rm -f $_hn # If the offered vlan is chosen, ask the relevant # questions and bring it up if [[ $_ifs == vlan[0-9]* ]]; then # Get existing tag for this vlan. _vi=$(ifconfig $_ifs 2>/dev/null | \ sed -n 's/vlan: \([0-9]*\).*/\1/p') # Get list of all in-use tags. _tags=$(ifconfig vlan 2>/dev/null | \ sed -n 's/vlan: \([0-9]*\).*/\1/p') # Current tag is a valid tag for this vlan. [[ -n $_tags ]] && _tags=$(rmel "$_vi" $_tags) if [[ -z $_vi ]]; then _vi=0 while (( (_vi += 1) < 4096 )); do ! isin "$_vi" $_tags && break done fi _ifdevs=$(get_ifdevs) set -- $_ifdevs while [[ $1 == vlan[0-9]* ]]; do shift done ask "Which interface:tag should $_ifs be on?" "${_vd:=$1}:$_vi" _vd=${resp%%:*} _vi=${resp##*:} # Validate that $_vd is a real interface if ! (isin "$_vd" $_ifdevs && [[ $_vd != vlan[0-9]* ]]); then echo "Invalid interface choice '$_vd'" _vd= continue fi # Validate range of $_vi as 1-4095, and $_vi not in use. if (( _vi < 1 || _vi > 4095 )) || isin "$_vi" $_tags; then echo "Invalid or in-use vlan tag '$_vi'" continue fi # hostname.$_vd must say something, anything, to # make sure it is up. grep -qs "^up" /tmp/hostname.$_vd || \ echo "up" >>/tmp/hostname.$_vd ifconfig $_vd up # Make sure a hostname.$_ifs is created with this info. ifconfig $_ifs destroy >/dev/null 2>&1 ifconfig $_ifs vlan $_vi vlandev $_vd echo "vlan $_vi vlandev $_vd" >>$_hn # Create a new vlan if we just configured the highest. [[ ${_ifs##vlan} == $_vl ]] && (( _vl += 1 )) fi # Test if it is an 802.11 interface ifconfig $_ifs 2>&- | grep -q "^[[:space:]]*ieee80211:" && ieee80211_config $_ifs $_hn # First interface configured will use the hostname without # asking the user. resp=$(hostname -s) [[ -n $_first && $_first != $_ifs ]] && \ ask "Symbolic (host) name for $_ifs?" $resp _name=$resp v4_config $_ifs $_name $_hn v6_config $_ifs $_name $_hn if [[ -f $_hn ]]; then chmod 640 $_hn (( nifs += 1 )) : ${_first:=$_ifs} _p=done fi done } # Output ' [ ]'. # # $1 == interface v4_info() { ifconfig $1 inet | sed -n ' 1s/.* '. # # $1 == interface v6_info() { ifconfig $1 inet6 | sed -n ' 1s/.*/etc/resolv.conf.tail if [[ -n $_hn ]]; then _hn="send host-name \"$_hn\";" echo "Issuing hostname-associated DHCP request for $_ifs." else echo "Issuing free-roaming DHCP request for $_ifs." fi cat >/etc/dhclient.conf <<__EOT initial-interval 1; $_hn request subnet-mask, broadcast-address, routers, domain-name, domain-name-servers, host-name; __EOT ifconfig $_ifs group dhcp >/dev/null 2>&1 dhclient $_ifs set -- $(v4_info $_ifs) if [[ $1 == UP && -n $2 ]]; then # Move configuration files to where they will be copied to the # installed system. Overwrites configuration information from # last successful dhcp attempt. mv /etc/dhclient.conf /tmp/dhclient.conf mv /etc/resolv.conf.tail /tmp/resolv.conf.tail return 0 fi ifconfig $_ifs delete down -group dhcp 2>&- rm /etc/dhclient.conf /etc/resolv.conf.tail return 1 } # Convert a hex value to dotted decimal format hextodec() { local _d _b for _b in $(echo ${1#0x} | sed 's/\(..\)/\1 /g'); do _d=$_d.$((0x$_b)) done echo ${_d#.} } # Perform an 802.11 interface network scan. # The result is cached in $WLANLIST # $1 == interface ieee80211_scan() { # N.B. Skipping quoted nwid's for now [[ -f $WLANLIST ]] || ifconfig $1 scan | sed -n 's/^ nwid \([^"]\)/\1/p' > $WLANLIST cat $WLANLIST } # Configures an 802.11 interface # # $1 == interface # $2 == hostname ieee80211_config() { local _ifs=$1 _hn=$2 _prompt _nwid _haswpa=0 _err # Reset 802.11 settings and determine wpa capability ifconfig $_ifs -nwid -nwkey ifconfig $_ifs -wpa 2>&- && _haswpa=1 # Empty scan cache rm -f $WLANLIST while [[ -z $_nwid ]]; do ask_until "Access point? (ESSID, 'any', list# or '?')" "any" case "$resp" in +([0-9])) _nwid=$(ieee80211_scan $_ifs | sed -n "${resp}s/ .*//p") [[ -z $_nwid ]] && echo "There is no line $resp." ;; \?) ieee80211_scan $_ifs | sed -n 's/^\([^ ]*\) chan .* bssid \([^ ]*\) .*$/ \1 (\2)/p' | less -XEN ;; *) _nwid=$resp ;; esac done # 'any' implies that only open access points are considered if [[ $_nwid != any ]]; then ifconfig $_ifs nwid "$_nwid" quote nwid "$_nwid" >>$_hn _prompt="Security protocol? (O)pen, (W)EP" [[ $_haswpa == 1 ]] && _prompt="$_prompt, WPA-(P)SK" while :; do ask_until "$_prompt" "O" case "$_haswpa-$resp" in ?-[Oo]) break ;; ?-[Ww]) ask_until "WEP key? (will echo)" # Make sure ifconfig accepts the key if _err=$(ifconfig $_ifs nwkey "$resp" 2>&1) && [[ -z $_err ]]; then quote nwkey "$resp" >>$_hn break fi echo "$_err" ;; 1-[Pp]) ask_until "WPA passphrase? (will echo)" # Make sure ifconfig accepts the key if ifconfig $_ifs wpakey "$resp"; then quote wpakey "$resp" >>$_hn break fi ;; *) echo "'$resp' is not a valid choice." ;; esac done fi } v4_config() { local _ifs=$1 _name=$2 _hn=$3 _prompt _addr _mask if ifconfig $_ifs | grep 'groups:.* dhcp' >/dev/null 2>&1; then _addr=dhcp else set -- $(v4_info $_ifs) if [[ -n $2 ]]; then _addr=$2; _mask=$(hextodec $3) ifconfig $_ifs inet $_addr delete fi fi if [[ -x /sbin/dhclient ]]; then _prompt="or 'dhcp' " # Don't make 'dhcp' the default if dhcp was already used. ifconfig dhcp >/dev/null 2>&1 || _addr=dhcp fi _prompt="IPv4 address for $_ifs? (${_prompt}or 'none')" ask_until "$_prompt" "$_addr" case $resp in none) ;; dhcp) if [[ ! -x /sbin/dhclient ]]; then echo "DHCP not possible - no /sbin/dhclient." elif dhcp_request $_ifs "$_name" || dhcp_request $_ifs ; then # Add hosts entry. Overwrites previous entry if any. set -- $(v4_info $_ifs) addhostent "$2" "$_name" echo "dhcp" >>$_hn # Create a new bpf in case we start another dhclient makedev bpf$(ls /dev | grep -c "^bpf[0-9]") fi ;; *) _addr=$resp ask_until "Netmask?" "${_mask:=255.255.255.0}" ifconfig $_ifs -group dhcp >/dev/null 2>&1 if ifconfig $_ifs inet $_addr netmask $resp up ; then addhostent "$_addr" "$_name" echo "inet $_addr $resp" >>$_hn fi ;; esac } v6_config() { local _ifs=$1 _name=$2 _hn=$3 _addr _prefixlen _prompt ifconfig lo0 inet6 >/dev/null 2>&1 || return set -- $(v6_info $_ifs) [[ -n $2 ]] && { _addr=$2; _prefixlen=$3; } [[ -x /sbin/rtsol ]] && _prompt="or 'rtsol' " _prompt="IPv6 address for $_ifs? (${_prompt}or 'none')" ask_until "$_prompt" "${_addr:-none}" case $resp in none) return ;; rtsol) [[ ! -x /sbin/rtsol ]] && { echo "No /sbin/rtsol." ; return ; } ifconfig $_ifs up if rtsol -F $_ifs; then set -- $(v6_info $_ifs) addhostent "$2" "$_name" echo "up\nrtsol" >>$_hn fi return ;; esac _addr=$resp ask_until "IPv6 prefix length for $_ifs?" "${_prefixlen:=64}" ifconfig $_ifs inet6 $_addr prefixlen $resp up || return echo "inet6 $_addr $resp" >>$_hn addhostent "$_addr" "$_name" v6_defroute $_ifs [[ $resp == none ]] && return route -n add -inet6 -host default "$resp" || return echo "$resp" >>/tmp/mygate } v4_defroute() { local _dr _prompt=" or 'none'" # Get/Confirm an IPv4 default route if an IPv4 address was configured. [[ -n $(ifconfig | sed -ne '/[ ]inet .* broadcast /p') ]] || return # If only one interface, and it is running dhclient, ask nothing [[ -f /tmp/dhclient.conf && $nifs == 1 ]] && return [[ -x /sbin/dhclient ]] && _prompt=", 'dhcp'$_prompt" _prompt="Default IPv4 route? (IPv4 address$_prompt)" _dr=$(route -n show -inet | sed -ne '/^default */{s///; s/ .*//; p;}') [[ -f /tmp/dhclient.conf ]] && _dr=dhcp while :; do ask_until "$_prompt" "$_dr" [[ $resp == @(none|dhcp) ]] && break route delete -inet default >/dev/null 2>&1 route -n add -inet -host default "$resp" && { echo "$resp" >/tmp/mygate ; break ; } # Put the old default route back. The new one did not work. route -n add -inet -host default $_dr >/dev/null 2>&1 done } v6_defroute() { local _if=$1 _routers _oifs if [[ -z $(route -n show -inet6 | sed -ne '/^default */{s///; s/ .*//; p;}') ]]; then resp=none return fi if [[ -x /sbin/ping6 ]]; then _routers=$(ping6 -n -c 2 ff02::2%$_if 2>&1 | sed -n \ -e '/bytes from/{s/^.*from //;s/,.*$//;p;}') fi _oifs=$IFS IFS= PS3="IPv6 default router? (list #, IPv6 address or 'none'): " select i in $_routers; do case $i in "") resp=$REPLY [[ -n $resp ]] && break ;; *) resp=$i break ;; esac done IFS=$_oifs } # Much of this is gratuitously stolen from /etc/netstart. enable_network() { local _f _gw # Copy any network configuration files. N.B.: hosts already copied. for _f in dhclient.conf resolv.conf resolv.conf.tail; do if [ -f /mnt/etc/$_f ]; then cp /mnt/etc/$_f /etc/$_f fi done # Set the address for the loopback interface. Bringing the # interface up, automatically invokes the IPv6 address ::1. ifconfig lo0 inet 127.0.0.1/8 # configure all of the non-loopback interfaces which we know about. # refer to hostname.if(5) for hn in /mnt/etc/hostname.*; do (( nifs += 1 )) # Strip off /mnt/etc/hostname. prefix if=${hn#/mnt/etc/hostname.} # Check for ifconfig'able interface. (ifconfig $if||ifconfig $if create)> /dev/null 2>&1 || continue # Now parse the hostname.* file while :; do if [ "$cmd2" ]; then # we are carrying over from the 'read dt dtaddr' last time set -- $cmd2 af=$1 name=$2 mask=$3 bcaddr=$4 ext1=$5 cmd2= # make sure and get any remaining args in ext2, like the read below i=1; while [ i -lt 6 -a -n "$1" ]; do shift; let i=i+1; done ext2="$@" else # read the next line or exit the while loop read af name mask bcaddr ext1 ext2 || break fi # $af can be "dhcp", "up", "rtsol", an address family, commands, or # a comment. case $af in "#"*|"!"*|"bridge"|"") # skip comments, user commands, bridges, # and empty lines continue ;; "dhcp") [ "$name" = "NONE" ] && name= [ "$mask" = "NONE" ] && mask= [ "$bcaddr" = "NONE" ] && bcaddr= dhcpif="$dhcpif $if" cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 down" cmd="$cmd; dhclient $if" # Create a new bpf in case we start another dhclient makedev bpf$(ls /dev | grep -c "^bpf[0-9]") ;; "rtsol") rtsolif="$rtsolif $if" cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 up" ;; "up") # The only one of these guaranteed to be set is $if # the remaining ones exist so that media controls work cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 up" ;; *) read dt dtaddr if [ "$name" = "alias" ]; then # perform a 'shift' of sorts alias=$name name=$mask mask=$bcaddr bcaddr=$ext1 ext1=$ext2 ext2= else alias= fi cmd="ifconfig $if $af $alias $name " case $dt in dest) cmd="$cmd $dtaddr" ;; [a-z!]*) cmd2="$dt $dtaddr" ;; esac if [ -z "$name" ]; then echo "/mnt/etc/hostname.$if: invalid network configuration file" return fi case $af in inet) [ "$mask" ] && cmd="$cmd netmask $mask" if [ "$bcaddr" -a "$bcaddr" != "NONE" ]; then cmd="$cmd broadcast $bcaddr" fi [ "$alias" ] && rtcmd="; route -qn add -host $name 127.0.0.1" ;; inet6) [ "$mask" ] && cmd="$cmd prefixlen $mask" cmd="$cmd $bcaddr" ;; *) cmd="$cmd $mask $bcaddr" ;; esac cmd="$cmd $ext1 $ext2$rtcmd" rtcmd= ;; esac eval "$cmd" done /dev/null 2>&1 route -qn add -host default $_gw && break done [[ -z $rtsolif ]] && stripcom /mnt/etc/mygate | while read _gw; do [[ $_gw == !(*:*) ]] && continue route -qn delete -inet6 default >/dev/null 2>&1 route -qn add -host -inet6 default $_gw && break done route -qn add -net 127 127.0.0.1 -reject >/dev/null } # Install a user-selected subset of the files in $2 from the source # named in $1. Display an error message for failed installs so the # user will know to try again. install_files() { local _src=$1 _files=$2 _f _sets _get_sets _n _col=$COLUMNS # Initialize _sets to the list of sets found in _src, and initialize # _get_sets to the intersection of _sets and DEFAULTSETS. # # Sets will be installed in the order given in THESETS to ensure proper # installation. So, to minimize user confusion display the sets in the # order in which they will be installed. for _f in $THESETS; do isin $_f $_files || continue; _sets=$(addel $_f $_sets) if [[ -z $DISPLAY && ! -d /mnt/etc/X11 ]]; then # No displays and X isn't installed ==> skip X sets isin ${_f%${VERSION}.tgz} xbase xetc xshare xfont xserv && continue fi isin $_f $DEFAULTSETS "site$VERSION-$(hostname -s).tgz" && \ _get_sets=$(addel $_f $_get_sets) done if [[ -z $_sets ]]; then # Show $_src, but delete any ftp password. echo -n "Looked at " echo $_src | sed -e 's/\(^ftp:\/\/[^/]*\)\(:[^/]*\)\(@.*\)/\1\3/' echo "and found no $OBSD sets. The set names looked for were:" let COLUMNS=_col-8 for _n in $THESETS; do echo $_n; done | showcols | sed 's/^/ /' COLUMNS=$_col echo return fi resp=y isin INSTALL.$ARCH $_files || ask_yn "INSTALL.$ARCH not found. Use sets found here anyway?" [[ $resp = n ]] && return select_sets "$_sets" "$_get_sets" [[ -n $resp ]] || return _get_sets=$resp [[ $resp = n ]] && return shacmd="cat" [[ -x /bin/sha256 ]] && shacmd="sha256 /tmp/h" # XXX Fix xkb mistake in 5.0 # XXX Remove this chunk after 5.2 release local _bad_dir=/mnt/usr/X11R6/share/X11/xkb/symbols/srvr_ctrl isin xshare${VERSION}.tgz $_get_sets && [[ -d $_bad_dir ]] && rm -rf $_bad_dir for _f in $THESETS ; do isin $_f $_get_sets || continue echo -n "Getting $_f ..." rm -f /tmp/h case $_f in *.tgz) ftp $FTPOPTS -o - -m "$_src/$_f" | \ $shacmd | tar zxphf - -C /mnt ;; *) ftp $FTPOPTS -o - -m "$_src/$_f" | \ $shacmd > "/mnt/$_f" ;; esac if [ $? -ne 0 ]; then echo "'$_f' did not install correctly." elif [ -f /tmp/h -a -f "/var/hash/$_f" ]; then if [ "$(/dev/null 2>&1 && \ SM_ARGS="-s /tmp/etc$VERSION.tgz" [[ $_f == xbase$VERSION.tgz ]] && \ ftp $FTPOPTS -o /mnt/tmp/xetc$VERSION.tgz \ -m "$_src/xetc$VERSION.tgz" >/dev/null 2>&1 && \ SM_ARGSX="-x /tmp/xetc$VERSION.tgz" done } # Encode $1 as specified for usercodes and passwords in RFC 1738 # section 3.1 and section 5. # # Escape everything between 0x20 and 0x7e to avoid both illegal url # characters and characters causing problems during script processing. # # *NOTE* # 1) quotes around $1 are required to preserve trailing or # embedded blanks in usercodes and passwords. # 2) substitute '%' FIRST so it doesn't eliminate '%' chars we insert. encode_for_url() { echo "$1" | sed -e " s/%/%25/g s/ /%20/g s/!/%21/g s/\"/%22/g s/#/%23/g s/\\\$/%24/g s/&/%26/g s/'/%27/g s/(/%28/g s/)/%29/g s/\*/%2a/g s/+/%2b/g s/,/%2c/g s/-/%2d/g s/\./%2e/g s/\//%2f/g s/:/%3a/g s/;/%3b/g s//%3e/g s/?/%3f/g s/@/%40/g s/\[/%5b/g s/\\\\/%5c/g s/]/%5d/g s/\^/%5e/g s/_/%5f/g s/\`/%60/g s/{/%7b/g s/|/%7c/g s/}/%7d/g s/~/%7e/g " } # Check for the presence of an error message in the output of the ftp commands # used to get the list of files in a directory. # # $1 = error message to look for # $2 = ftp command output ftp_error() { if [[ -n $(echo "$2" | grep "$1") ]]; then echo $1 return 0 fi return 1 } startftplist() { # If no networks are configured, we do not need the ftplist file (( nifs < 1 )) && return # Make sure the ftp subshell gets its own process group set -m ( # ftp.openbsd.org == 129.128.5.191 and will remain at # that address for the foreseeable future. ftp $FTPOPTS -a -o - "http://129.128.5.191/cgi-bin/ftplist.cgi?path=$FTPSETDIR" \ 2>/tmp/ftplisterr > $SERVERLISTALL # Remember finish time for adjusting the received timestamp echo -n $SECONDS >$SERVERLISTSEC ) & ftppid=$! set +m # If the ftp process takes more than 12 seconds, kill it # XXX We are relying on the pid space not randomly biting us -- # XXX ftp could terminate early, and the pid could be reused (sleep 12; kill -INT -$ftppid >/dev/null 2>&1) & } # Wait for the ftp process to finish, or be killed after the timeout # XXX contains a bit of debug code for now waitftplist() { local _dot # XXX [[ -z $ftppid ]] && return while [[ -n $(jobs $ftppid 2>/dev/null) ]]; do echo -n . # XXX _dot=. # XXX sleep 0.2 # XXX done [[ -n $_dot ]] && echo # XXX } # If possible, print the timestamp received from the ftplist.cgi output, # adjusted with the time elapsed since it was received ftp_time() { local _ftplist_sec=$(cat $SERVERLISTSEC 2>&-) local _time=$(sed '/^TIME=\([0-9]*\)$/!d;s//\1/;q' $SERVERLISTALL 2>&-) [[ -n $_ftplist_sec && -n $_time ]] && echo $((_time + SECONDS - _ftplist_sec)) } # Get several parameters from the user, and xfer # files from the server. # $1 = url type (ftp or http) # Note: _ftp_server_ip, _ftp_server_dir, _ftp_server_login, # and FTPOPTS must be global. install_url() { local _url_type=$1 _file_list _url_base _oifs _prompt _passwd _mirror eval local _server_ip=\$_${_url_type}_server_ip \ _server_dir=\$_${_url_type}_server_dir waitftplist ask "HTTP/FTP proxy URL? (e.g. 'http://proxy:8080', or 'none')" \ "${ftp_proxy:-none}" unset ftp_proxy http_proxy [[ $resp == none ]] || export ftp_proxy=$resp http_proxy=$resp if [[ -s $SERVERLISTALL ]]; then _prompt="Server? (hostname, list#, 'done' or '?')" sed -n "s,^${_url_type}://"'\([[A-Za-z0-9\:_][]A-Za-z0-9:._-]*\),\1,p' \ $SERVERLISTALL > $SERVERLIST set -- $(sed q $SERVERLIST) _server_ip=${1%%/*} else echo "(Was not able to get ftplist from ftp.openbsd.org, but that is OK)" _prompt="Server? (hostname or 'done')" fi # Get server IP address or hostname while :; do ask_until "$_prompt" "$_server_ip" case $resp in done) return ;; "?") [[ -s $SERVERLIST ]] || continue less -XEN < $SERVERLIST ;; +([0-9])) # A numeric hostname is ignored. A number is only used # as a line number in $SERVERLIST. [[ -s $SERVERLIST ]] || continue set -- $(sed -n "${resp}p" $SERVERLIST) [[ $# -lt 1 ]] && { echo "There is no line $resp." ; continue ; } _server_ip=${1%%/*} # Repeat loop to get user to confirm server address. ;; +([A-Za-z0-9\:.\[\]_-])) _server_ip=$resp break ;; *) echo "'$resp' is not a valid hostname." ;; esac done eval _${_url_type}_server_ip=$_server_ip # Get directory info from *last* line starting with the server # name. This means the last install from a mirror will not keep # the specific directory info. But an install from a local # server *will* remember the specific directory info. set -- $(sed "/^$_server_ip/x;\$!d;x" $SERVERLIST 2>&-) resp=${1#*/} # If there is no directory specified, don't use the server name! [[ $resp == "$1" ]] && resp= if (( $# > 1 )); then # It's a mirror, since it has location info. resp=$resp/$FTPSETDIR _mirror=yes fi ask_until "Server directory?" "${resp:-pub/OpenBSD/$FTPSETDIR}" _server_dir=$resp eval _${_url_type}_server_dir=$_server_dir if [[ $_url_type == ftp ]]; then # Get login name, setting IFS to nothing so trailing or # embedded blanks are preserved! _oifs=$IFS IFS= ask_until "Login?" "${_ftp_server_login:=anonymous}" _ftp_server_login=$resp # Get password unless login in 'anonymous' or 'ftp' if [[ $_ftp_server_login == @(anonymous|ftp) ]]; then _passwd=root@`hostname` else resp= while [[ -z $resp ]] ; do askpass "Password? (will not echo)" done _passwd=$resp fi IFS=$_oifs fi # Build up the base url since it is so nasty... _url_base=$_url_type:// if [[ $_url_type == ftp && $_ftp_server_login != anonymous ]]; then _url_base=$_url_base$(encode_for_url "$_ftp_server_login"):$(encode_for_url "$_passwd")@ fi _url_base=$_url_base$_server_ip/$_server_dir # XXX Workaround for problems ftp'ing out from a v6 only host. ifconfig lo0 127.0.0.1 # Get list of files from the server. if [[ $_url_type == ftp && -z $ftp_proxy ]] ; then _file_list=$(ftp $FTPOPTS "$_url_base/") ftp_error "Login failed." "$_file_list" && return ftp_error "No such file or directory." "$_file_list" && return else # Assumes index file is "index.txt" for http (or proxy) # We can't use index.html since the format is server-dependent _file_list=$(ftp $FTPOPTS -o - "$_url_base/index.txt" | \ sed -e 's/^.* //' | sed -e 's/ //') fi install_files "$_url_base" "$_file_list" # Remember where we installed from installedfrom=$_url_type://$_server_ip/$_server_dir # Bake a package path if we installed from a mirror if [[ -n $_mirror ]]; then package_path=$(print -r -- "$installedfrom" | sed -E "/\/(snapshots|[0-9]\.[0-9])\/($ARCH)\/*$/!d s!!/\1/packages/$(arch -s)/!;q") else package_path= fi } install_mounted_fs() { local _dir while :; do ask_until "Pathname to the sets? (or 'done')" "$SETDIR" [[ $resp == done ]] && return # Accept a valid /mnt2 or /mnt relative path. [[ -d /mnt2/$resp ]] && { _dir=/mnt2/$resp ; break ; } [[ -d /mnt/$resp ]] && { _dir=/mnt/$resp ; break ; } # Accept a valid absolute path. [[ -d /$resp ]] && { _dir=/$resp ; break ; } echo "The directory '$resp' does not exist." done install_files "file://$_dir" "$(ls $_dir/)" } install_cdrom() { get_drive "CD-ROM" '$(get_cddevs)' || return mount_mnt2 $resp || return install_mounted_fs } install_disk() { ask_yn "Is the disk partition already mounted?" if [[ $resp == n ]]; then get_drive "disk" '$(bsort $(get_dkdevs))' \ '$(bsort $(rmel $ROOTDISK $(get_dkdevs)))' || return mount_mnt2 $resp || return fi install_mounted_fs } install_nfs() { local _tcp # Get the IP address of the server. ask_until "Server IP address or hostname?" "$NFS_ADDR" NFS_ADDR=$resp # Get the server path to mount. ask_until "Filesystem on server to mount?" "$NFS_PATH" NFS_PATH=$resp # Determine use of TCP ask_yn "Use TCP transport? (requires TCP-capable NFS server)" [[ $resp == y ]] && _tcp=-T # Mount the server mount_nfs $_tcp -o ro -R 5 $NFS_ADDR:$NFS_PATH /mnt2 || return install_mounted_fs } install_tape() { local _z _bs # Get the name of the tape device. get_drive "tape drive" '$MTDEVS' || return export TAPE=/dev/nr$resp if [[ ! -c $TAPE ]]; then echo "$TAPE is not a character special file." return fi # Rewind the tape device. echo -n "Rewinding $TAPE (mt rewind)..." mt rewind || return echo "done." # Extract the desired files. while :; do ask_until "Skip how many files? (or 'done')" 0 [[ $resp == done ]] && return [[ $resp == +([0-9]) ]] || continue (($resp < 0)) && continue if (($resp > 0)); then echo -n "Skipping $resp file(s)..." mt fsf $resp || return echo "done." elif [[ -n $_bs ]]; then # Dance to start of next file. mt bsf ; mt fsf fi unset _z ask_yn "Is the file gzipped?" yes [[ $resp == y ]] && _z=z # Get the blocksize to use. If the file isn't gzipped then # default to the 20 x 512 = 10,240 byte tar default. [[ $_z == z ]] || _bs=10240 ask_until "Blocksize for this file?" "${_bs:-8k}" [[ $resp == done ]] && return _bs=$resp dd if=$TAPE bs=$_bs | tar ${_z}xvphf - -C /mnt || return done } set_timezone() { local _zonefile=$1 _zonepath _zsed _tz _zoneroot=/usr/share/zoneinfo # If the timezone file is not available, # return immediately. [[ ! -f $_zonefile ]] && return # If configured in a previous call, return immediately [[ -n $TZ ]] && return if [[ -h /mnt/etc/localtime ]]; then TZ=$(ls -l /mnt/etc/localtime 2>/dev/null) TZ=${TZ#*${_zoneroot#/mnt}/} fi waitftplist if [[ -s $SERVERLISTALL ]]; then _tz=$(sed -ne '/^TZ=/s/TZ=//p' <$SERVERLISTALL) [[ -n $_tz ]] && isin "$_tz" `cat $_zonefile` && TZ=$_tz fi # If neither the base or SERVERLIST gave a hint, and this is the # early question, give up, and ask after the sets are installed [[ $_zonefile = /var/tzlist && -z $TZ ]] && return while :; do ask "What timezone are you in? ('?' for list)" "$TZ" _zonepath=${resp%%*(/)} case $_zonepath in "") continue ;; "?") grep -v /. $_zonefile | showcols continue ;; esac while isin "$_zonepath/" $(cat $_zonefile); do ask "What sub-timezone of '$_zonepath' are you in? ('?' for list)" _zsed=$(echo $_zonepath/ | sed 's,/,\\/,g') resp=${resp%%*(/)} case $resp in "") ;; "?") sed -n "/^$_zsed/{s/$_zsed//;/\/./!p;}" $_zonefile | showcols ;; *) _zonepath=$_zonepath/$resp ;; esac done if isin "$_zonepath" $(cat $_zonefile); then TZ=${_zonepath#$_zoneroot} return fi echo -n "'${_zonepath}'" echo " is not a valid timezone on this system." done } # Check that missing required sets were deliberately skipped. sane_install() { local _q=$1 _s _m for _s in $SANESETS; do isin $_s $DEFAULTSETS || continue # If sane_install has no argument, harass the user. resp=n [[ -z $_q ]] && ask_yn "Are you *SURE* your $MODE is complete without '$_s'?" [[ $resp == n ]] && _m="$_m $_s" done [[ -n $_m ]] && return 1 return 0 } # Ask the user for locations of sets, and then install whatever sets the # user selects from that location. Repeat as many times as the user # needs to get all desired sets. install_sets() { local _d _locs="disk ftp http" echo [[ -s $SERVERLISTALL ]] && \ _d=$(sed -ne '/^method=/s/method=//p' $SERVERLISTALL) ifconfig netboot >/dev/null 2>&1 && : ${_d:=http} [[ -n $(get_cddevs) ]] && { _locs="cd $_locs" ; : ${_d:=cd} ; } [[ -x /sbin/mount_nfs ]] && _locs="$_locs nfs" [[ -n $MTDEVS && -x /bin/mt ]] && _locs="$_locs tape" : ${_d:=http} if ! isin "$_d" $_locs; then for a in http ftp cd nfs tape disk; do isin $a $_locs && _d=$a && break done fi echo "Let's $MODE the sets!" while :; do umount -f /mnt2 >/dev/null 2>&1 [[ -n $method ]] && _d=$method sane_install quiet && _d=done ask "Location of sets? ($_locs or 'done')" "$_d" case $resp in done) sane_install && return ;; c*|C*) isin cd $_locs && install_cdrom && method=cd ;; d*|D*) install_disk && method=disk ;; f*|F*) isin ftp $_locs && install_url ftp && method=ftp ;; h*|H*) isin http $_locs && install_url http && method=http ;; n*|N*) isin nfs $_locs && install_nfs && method=nfs ;; t*|T*) isin tape $_locs && install_tape && method=tape ;; esac done } run_sysmerge() { echo "Please run sysmerge(8) after rebooting to repair your /etc configuration." #if [[ -n "$SM_ARGS" || -n "$SM_ARGSX" ]]; then # ask_yn "Merge the new etc/xetc install sets using sysmerge(8)?" no # if [[ $resp == y ]]; then \ # /mnt/usr/sbin/chroot /mnt /usr/sbin/sysmerge $SM_ARGS $SM_ARGSX # fi #fi } update_firmware() { local _get=Install [[ $MODE == upgrade ]] && _get=Update ask_yn "$_get non-free firmware files on first boot?" no [[ $resp == y ]] && \ echo "/usr/sbin/fw_update -v" >> /mnt/etc/rc.firsttime } # Create a skeletal but useful /etc/fstab from /tmp/fstab by stripping all # comment lines and dropping all filesystems which # # 1) can't be mounted (no mount_* command is found), # 2) have 'xx' in the option field (usually /altroot), # 3) have 'noauto' in the option field, # 4) are nfs (since name resolution may not be present), # 5) are on a vnd device. # # In addition, # # 1) delete 'softdep' options (no soft updates in ramdisk kernels), # 2) mount non-ffs filesystems read only, # 3) prepend '/mnt' to all mount points, # 4) delete any trailing '/' from the mount point (e.g. root), # # If no /etc/fstab is created, do not proceed with install/upgrade. munge_fstab() { local _dev _mp _fstype _opt _rest while read _dev _mp _fstype _opt _rest; do # Drop irrelevant lines and filesystems. [[ $_dev == @(/dev/vnd*|\#*) || \ $_fstype == nfs || \ ! -f /sbin/mount_$_fstype || \ $_opt == *noauto* || \ $_opt == *xx* ]] && continue # Remove any softdep options, as soft updates are not # available in the ramdisk kernels. _opt=$(echo $_opt | sed -e 's/softdep//') # Change read-only ffs to read-write since we'll potentially # write to these filesystems. [[ $_fstype == ffs ]] && _opt=$(echo $_opt | sed -e 's/ro/rw/') # Mount non-ffs filesystems read only. [[ $_fstype == ffs ]] || _opt=$(echo $_opt | sed -e 's/rw/ro/') # Write fs entry in fstab. # 1) prepend '/mnt' to the mount point. # 2) remove a trailing '/' from the mount point (e.g. root). echo $_dev /mnt${_mp%/} $_fstype $_opt $_rest done /etc/fstab # If no /etc/fstab was created, we have nowhere to $MODE to. if [ ! -s /etc/fstab ]; then echo "Unable to create valid /etc/fstab." exit fi } # Must mount filesystems manually, one at a time, so we can make # sure the mount points exist. mount_fs() { local _async=$1 _dev _mp _fstype _opt _rest _msg _fail while read _dev _mp _fstype _opt _rest; do # If not the root filesystem, make sure the mount # point is present. [ "$_mp" = "/mnt" ] || mkdir -p $_mp # Mount the filesystem. Remember any failure. _msg=$(mount -v -t $_fstype $_async -o $_opt $_dev $_mp) || _fail="$_fail\n$_mp ($_dev)" echo $_msg | sed -e 's/, ctime=[^,)]*//' done 0, showing individual results, but skipping $ROOTDEV. This was # already fsck'ed successfully. # # Exit if any fsck's fail (but do them all before exiting!). check_fs() { local _dev _mp _fstype _rest _fail _f _passno ask_yn "Force checking of clean non-root filesystems?" [[ $resp == y ]] && _f=f while read _dev _mp _fstype _rest _rest _passno _rest; do [ "$_dev" != /dev/"$ROOTDEV" ] || continue [ -f "/sbin/fsck_$_fstype" ] || continue # Make sure device exists before fsck'ing it. makedev "$(getdevname "$_dev")" || continue [[ $_passno > 0 ]] || continue echo -n "fsck -${_f}p $_dev..." if ! fsck -${_f}p $_dev >/dev/null 2>&1; then echo "FAILED. You must fsck $_dev manually." _fail=y else echo "OK." fi done /tmp/resolv.conf for _ns in $resp; do echo "nameserver $_ns" >>/tmp/resolv.conf done cp /tmp/resolv.conf /tmp/resolv.conf.shadow fi manual_net_cfg } populateusrlocal() { if [ -f /mnt/etc/mtree/BSD.local.dist ]; then /mnt/usr/sbin/chroot /mnt /usr/sbin/mtree -Uedqn -p /usr/local -f /etc/mtree/BSD.local.dist >/dev/null fi } apply() { if [[ $sshd == n ]]; then echo "sshd_flags=NO # disabled during install" \ >>/mnt/etc/rc.conf.local fi if [[ $sshd_disableroot == y ]]; then sed -e "/^#\(PermitRootLogin\) yes/s//\1 no/" \ < /mnt/etc/ssh/sshd_config > /tmp/sshd_config cp /tmp/sshd_config /mnt/etc/ssh/sshd_config fi if [[ $ntpd == y ]]; then echo "ntpd_flags= # enabled during install" \ >>/mnt/etc/rc.conf.local if [[ $ntpd_server != default ]]; then # Comment out the default 'servers' line, and add a # 'servers' line with the first token in $resp as the # server. set -- $ntpd_server sed -e "s/^servers /#&/;/#server /a\\ servers $1 " /mnt/etc/ntpd.conf >/tmp/ntpd.conf cp /tmp/ntpd.conf /mnt/etc/ntpd.conf fi fi if [[ $x11 == y ]]; then sed -e "/^#\(machdep\.allowaperture=${MDXAPERTURE}\)/s//\1 /" \ /mnt/etc/sysctl.conf >/tmp/sysctl.conf cp /tmp/sysctl.conf /mnt/etc/sysctl.conf fi if [[ $xdm == y && -x /mnt/usr/X11R6/bin/xdm ]]; then echo "xdm_flags= # enabled during install" \ >>/mnt/etc/rc.conf.local fi if [[ $defcons == y ]]; then cp /mnt/etc/ttys /tmp/ttys sed -e "/^$CTTY/s/std.9600/std.${CSPEED}/" \ -e "/^$CTTY/s/unknown/vt220 /" \ -e "/$CTTY/s/off.*/on secure/" /tmp/ttys >/mnt/etc/ttys [[ -n $CPROM ]] && \ echo "stty $CPROM $CSPEED\nset tty $CPROM" >>/mnt/etc/boot.conf fi ln -sf /usr/share/zoneinfo/$TZ /mnt/etc/localtime } questions() { local _d _xdmask=y _def ask_yn "Start sshd(8) by default?" yes sshd=$resp ask_yn "Start ntpd(8) by default?" ntpd=$resp if [[ $resp == y ]]; then ask "NTP server? (hostname or 'default')" default ntpd_server=$resp fi def=no [[ -n $DISPLAY ]] && def=yes if [[ -n $MDXAPERTURE ]]; then ask_yn "Do you expect to run the X Window System?" $def x11=$resp _xdmask=$resp # if aperture was n, do not ask for xdm fi if [[ -n $MDXDM && $_xdmask == y ]]; then ask_yn "Do you want the X Window System to be started by xdm(1)?" xdm=$resp fi if [[ -n $CDEV ]]; then _d=${CPROM:-$CDEV} ask_yn "Change the default console to $_d?" defcons=$resp if [[ $resp == y ]]; then ask_which "speed" "should $_d use" \ "9600 19200 38400 57600 115200" $CSPEED case $resp in done) defcons=n ;; *) CSPEED=$resp ;; esac fi fi } finish_up() { local _dev _mp _fstype _rest # Mount all known swap partitions. This gives systems with little # memory a better chance at running 'MAKEDEV all'. if [[ -x /mnt/sbin/swapctl ]]; then /mnt/sbin/swapctl -a /dev/$SWAPDEV >/dev/null 2>&1 # Can't do chmod && swapctl -A because devices are not yet # created on install'ed systems. On upgrade'ed system there # is a small chance the device does not exist on the ramdisk # and will thus not get mounted. while read _dev _mp _fstype _rest; do [[ $_fstype == swap ]] && \ /mnt/sbin/swapctl -a $_dev >/dev/null 2>&1 done /var/run/dmesg.boot # Are we in a real release, or a snapshot? If this is a snapshot # install media, default us to a snapshot directory. FTPSETDIR=$SETDIR set -- $(scan_dmesg "/^OpenBSD $VNAME\([^ ]*\).*$/s//\1/p") [[ $1 == -!(stable) ]] && FTPSETDIR=snapshots/$ARCH # Scan /var/run/dmesg.boot for interesting devices. MTDEVS=$(scan_dmesg "${MDMTDEVS:-/^[cms]t[0-9][0-9]* /s/ .*//p}") nifs=0 DISPLAY=$(scan_dmesg '/^wsdisplay[0-9]* /s/ .*//p') CONSOLE=$(scan_dmesg '/^\([^ ]*\).*: console$/s//\1/p') CONSOLE=${CONSOLE% } [[ -n $CONSOLE ]] && CSPEED=$(stty speed) # Look for the serial device matching the console. If we are not installing # from a serial console, just find the first serial device that could be used # as a console. If a suitable device is found, set CDEV, CTTY, CSPEED, CPROM. md_consoleinfo # Selected sets will be installed in the order they are listed in $THESETS. # Ensure that siteXX.tgz is the *last* set listed so its contents overwrite # the contents of the other sets, not the other way around. THESETS="bsd bsd.rd bsd.mp $MDSETS" : ${DEFAULTSETS:="bsd bsd.rd"} for _set in base etc comp man game xbase xetc xshare xfont xserv site ; do [[ $MODE == upgrade && ( $_set == etc || $_set == xetc ) ]] && continue THESETS="$THESETS ${_set}${VERSION}.tgz" isin $_set site && continue DEFAULTSETS="$DEFAULTSETS ${_set}${VERSION}.tgz" done # Since etc${VERSION}.tgz is not in DEFAULTSETS for upgrades, it can always be # in SANESETS. SANESETS="${SANESETS:-bsd} base${VERSION}.tgz etc${VERSION}.tgz" # prepare COLUMNS sanely COLUMNS=$(stty -a | sed -n '/columns/{s/^.* \([0-9]*\) columns.*$/\1/;p;}') [ COLUMNS -eq 0 ] && COLUMNS=80 # decide upon an editor : ${EDITOR:=ed} [[ -x /usr/bin/vi ]] && EDITOR=vi export EDITOR COLUMNS # umount all filesystems, just in case we are re-running install or upgrade. cd / umount -af 1>/dev/null 2>&1 # make sure only successful dhcp requests retain their state for _ifs in $(get_ifdevs dhcp); do set -- $(v4_info $_ifs) [[ $1 == UP && -n $2 ]] && continue ifconfig $_ifs delete down -group dhcp 2>&- done cat <<__EOT At any prompt except password prompts you can escape to a shell by typing '!'. Default answers are shown in []'s and are selected by pressing RETURN. You can exit this program at any time by pressing Control-C, but this can leave your system in an inconsistent state. __EOT # Configure the terminal and keyboard. set_term if [[ $MODE == install ]]; then ask_until "System hostname? (short form, e.g. 'foo')" "$(hostname -s)" [[ ${resp%%.*} != $(hostname -s) ]] && hostname $resp THESETS="$THESETS site$VERSION-$(hostname -s).tgz" echo donetconfig (( nifs != 0 )) && startftplist echo while :; do askpassword root _rootpass="$_password" [[ -n "$_password" ]] && break echo "The root password must be set." done questions user_setup set_timezone /var/tzlist echo fi # Get ROOTDISK, ROOTDEV and SWAPDEV. while :; do ask_which "disk" "is the root disk" '$(get_dkdevs | sed s,^$,none, )' [[ $resp == done ]] && exit [[ $resp != none ]] && break done makedev $resp || exit ROOTDISK=$resp ROOTDEV=${ROOTDISK}a SWAPDEV=${ROOTDISK}b