# $OpenBSD: install.sub,v 1.371 2005/04/03 19:33:59 krw Exp $ # $NetBSD: install.sub,v 1.5.2.8 1996/09/02 23:25:02 pk Exp $ # # Copyright (c) 1997-2005 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. # 3. All advertising materials mentioning features or use of this software # must display the following acknowledgement: # This product includes software developed by the NetBSD # Foundation, Inc. and its contributors. # 4. Neither the name of The NetBSD Foundation nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # 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 # # The following variables can be provided if required: # MDSETS - list of files to add to THESETS # MDTERM - 'vt220' assumed if not provided # MDFSTYPE - nothing assumed if not provided # MDFSOPTS - nothing 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 - '/^[cmsw]t[0-9][0-9]* /s/ .*//p' # MDXAPERTURE - set machdep.allowaperture=value in sysctl.conf . install.md set_term() { local _tables [[ -n $TERM ]] && return ask "Terminal type?" ${MDTERM:-vt220} TERM=$resp export TERM [[ -x /sbin/kbd ]] || return _tables=$(bsort $(kbd -l | egrep -v "^(user|tables|encoding)")) ask_which "kbd(8) table" "best matches your keyboard" "$_tables" us [[ $resp != @(done|us) ]] && kbd $resp && echo $resp >/tmp/kbdtype } welcome() { local _q cat <<__EOT Welcome to the $OBSD $MODE program. This program will help you $MODE OpenBSD in a simple and rational way. At any prompt except password prompts you can run a shell command by typing '!foo', or escape to a shell by typing '!'. Default answers are shown in []'s and are selected by pressing RETURN. At any time you can exit this program by pressing Control-C and then RETURN, but quitting during an $MODE can leave your system in an inconsistent state. __EOT # Configure the terminal and keyboard. set_term cat <<__EOT IS YOUR DATA BACKED UP? As with anything that modifies disk contents, this program can cause SIGNIFICANT data loss. __EOT case $MODE in upgrade) cat <<__EOT NOTE: once your system has been upgraded, you must manually merge any changes to files in the 'etc' set into the files already on your system. __EOT _q="Proceed with upgrade?" ;; install) cat <<__EOT It is often helpful to have the installation notes handy. For complex disk configurations, relevant disk hardware manuals and a calculator are useful. __EOT if [ -f /etc/fstab ]; then cat <<__EOT You seem to be trying to restart an interrupted installation! You can skip the disk preparation steps and continue, or you can reboot and start over. __EOT _q="Skip disk initialization?" else _q="Proceed with install?" fi ;; esac ask_yn "$_q" if [[ $resp == n ]]; then cat <<__EOT Enter 'halt' at the prompt to gracefully exit OpenBSD. You can then power cycle the machine and boot your original OS. __EOT exit fi echo "Cool! Let's get to it..." } scan_dmesg() { bsort $(sed -ne "$1" /var/run/dmesg.boot) } get_ifdevs() { ifconfig -a \ | egrep -v '^[[:space:]]|(bridge|enc|gif|gre|lo|pflog|pfsync|ppp|sl|tun|vlan)[[:digit:]]+:' \ | sed -ne 's/^\(.*\):.*/\1/p' } # Get the first (lowest unit #) serial device if any, if MDSERIAL is set. # NOTE: Only single digit serial devices (0 -> 9) are looked for. get_serialdev() { local _d _bd _td [[ -n $MDSERIAL ]] || exit set -- $MDSERIAL _d=$1 _bd=$2 _td=$3 set -- $(scan_dmesg "/^${_d}\([0-9]\) .*/s//\1/p") echo "$_bd$1 $_td$1" } get_drive() { ask_which "$1" "contains the $MODE media" "$2" [[ $resp == done ]] && return 1 makedev $resp || return 1 return 0 } get_partition() { local _drive=$1 _fstypes=$2 _part _fst # Create file /tmp/parts.$_drive where each line is of the # form " ". disklabel $_drive 2>/dev/null \ | grep '^ [a-p]: ' \ | egrep -v "swap|unused" \ | sed -e 's/^ \(.\): *[^ ]* *[^ ]* *\([^ ]*\) .*/\1 \2/' \ >/tmp/parts.$_drive disklabel $_drive 2>/dev/null | grep '^ .:' ask_which "$_drive partition" "has the $MODE sets" \ "$(sed -e 's/^\(.\).*/\1/' /tmp/parts.$_drive)" [[ $resp == done ]] && return 1 _part=$resp _fst=$(sed -ne "/^$_part /s///p" /tmp/parts.$_drive) ask_which "filesystem type" "should be used to mount $_drive$_part" "$_fst $_fstypes ffs" case $resp in done) return 1 ;; $_fst) resp="$_part" ;; *) resp="$_part $resp" ;; esac return 0 } # 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. askpass() { set -o noglob stty -echo read resp?"$1 " stty echo set +o noglob echo } # 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 set -o noglob while :; do echo -n "$_question " [[ -z $_default ]] || echo -n "[$_default] " read resp case $resp in !) echo "Type 'exit' to return to install." sh ;; !*) eval ${resp#?} ;; *) : ${resp:=$_default} break ;; esac done set +o noglob } # 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 # $5 = error message if no items in $3, defaults to 'No $1s found.' # # At exit $resp holds selected item, or 'done' ask_which() { local _name=$1 _query=$2 _list=$3 _def=$4 _err=$5 set -- $_list if (( $# < 1 )); then echo "${_err:=No ${_name}s found}." resp=done return fi : ${_def:=$1} # Eliminate extraneous (especially trailing) whitespace in _list. _list="$*" 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. ask "Available ${_name}s are: $_list.\nWhich one $_query? (or 'done')" "$_def" # Quote $resp to prevent user from confusing isin() by # entering something like 'a a'. isin "$resp" $_list 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 "$_a " # Sort remaining values. bsort $_l } # Add interesting/useful comments from mnt/etc/$1 to /tmp/$1. # # $1 == file in /tmp and /mnt/etc directories save_comments() { local _file=$1 if [[ -f /mnt/etc/$_file ]]; then grep "^#" /mnt/etc/$_file >/tmp/$_file.new [[ -f /tmp/$_file ]] && cat /tmp/$_file >>/tmp/$_file.new mv /tmp/$_file.new /tmp/$_file fi } # Offer to edit a file in /tmp and execute ${EDITOR} to do so if the user # accepts the offer. # # $1 == file in /tmp to edit edit_tmp_file() { local _file=$1 ask_yn "Edit $_file with $EDITOR?" [[ $resp == y ]] && $EDITOR /tmp/$_file } # 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 ; } } # log in via ftp to host $1 as user $2 with password $3 # and return a list of all files in the directory $4 on stdout ftp_list_files() { ftp ${_ftp_active} -V -n "$1" <<__EOT user "$2" "$3" cd "$4" ls quit __EOT } # 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 || return 1 ; cd - >/dev/null DEVSMADE=$(addel $_dev $DEVSMADE) } # 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 sed "/^[0-9a-fA-F]*[:.].* $_name\$/d" /tmp/hosts >/tmp/hosts.new 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 _next _f _action while :; do _action= cat <<__EOT The following sets are available. Enter a filename, 'all' to select all the sets, or 'done'. You may de-select a set by prepending a '-' to its name. __EOT _next= for _f in $_avail; do if isin $_f $_selected; then echo " [X] $_f" else echo " [ ] $_f" : ${_next:=$_f} fi done : ${_next:=done} ask "\nFile name? (or 'done')" "$_next" case $resp in done) break ;; -*) _action=rmel ;; esac : ${_action:=addel} resp=${resp#+|-} case $resp in "") continue ;; all) resp=* ;; esac # Use @($resp) rather than just $resp to protect # against silly user input that might cause syntax # errors. for _f in $_avail; do eval "case $_f in @($resp)) _selected=\`$_action $_f \$_selected\` ;; esac" done done resp=$_selected } configure_ifs() { local _IFDEVS=$IFDEVS _ifs _name _media _hn while :; do ask_which "interface" "do you wish to initialize" "$_IFDEVS" \ "" "No more interfaces to initialize" [[ $resp == done ]] && break _ifs=$resp _hn=/tmp/hostname.$_ifs # Get symbolic name - will be used in DHCP requests. ask "Symbolic (host) name for $_ifs?" "$(hostname -s)" _name=$resp # Get and apply media options. _media=$(ifconfig -m $_ifs | grep "media ") if [[ -n $_media ]]; then cat <<__EOT The media options for $_ifs are currently $(ifconfig -m $_ifs | sed -n '/supported/D;/media:/p') __EOT ask_yn "Do you want to change the media options?" case $resp in y) cat <<__EOT Supported media options for $_ifs are: $_media __EOT ask "Media options for $_ifs?" _media=$resp ifconfig $_ifs $_media || return 1 ;; n) _media= ;; esac fi rm -f $_hn v4_config "$_ifs" "$_media" "$_name" "$_hn" v6_config "$_ifs" "$_media" "$_name" "$_hn" [[ -f $_hn ]] && _IFDEVS=$(rmel "$_ifs" $_IFDEVS) done } # Output ' [ ]'. # # $1 == interface v4_info() { ifconfig $1 inet | sed -n ' 1s/.* '. # # $1 == interface v6_info() { ifconfig $1 inet6 | sed -n ' 1s/.*/etc/dhclient.conf if [[ -n $_hostname ]]; then echo "send host-name \"$_hostname\";" >>/etc/dhclient.conf echo "Issuing hostname-associated DHCP request for $_ifs." else echo "Issuing free-roaming DHCP request for $_ifs." fi cat >>/etc/dhclient.conf <<__EOT request subnet-mask, broadcast-address, routers, domain-name, domain-name-servers, host-name; __EOT cat >>/etc/resolv.conf.tail <<__EOT lookup file bind __EOT ifconfig $_ifs group dhcp >/dev/null 2>&1 dhclient $_ifs set -- $(v4_info $_ifs) if [[ $1 == UP && $2 == "0.0.0.0" ]]; then ifconfig $_ifs delete down rm /etc/dhclient.conf /etc/resolv.conf.tail return 1 fi # 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 } v4_config() { local _ifs=$1 _media=$2 _name=$3 _hn=$4 _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=$3 ifconfig $_ifs inet $_addr delete fi fi [[ -x /sbin/dhclient ]] && _prompt=" or 'dhcp'" _prompt="IPv4 address for $_ifs? (or 'none'$_prompt)" 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 addhostent "127.0.0.1" "$_name" echo "dhcp NONE NONE NONE $_media" >>$_hn 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 NONE $_media" >$_hn fi ;; esac } v6_config() { local _ifs=$1 _media=$2 _name=$3 _hn=$4 _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" case $resp in none) return ;; rtsol) if [[ ! -x /sbin/rtsol ]]; then echo "rtsol not possible - no /sbin/rtsol." return fi ifconfig $_ifs up rtsol -F $_ifs addhostent "::1" "$_name" echo "up\nrtsol $media" >>$_hn 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 $media" >>$_hn addhostent "$_addr" "$_name" v6_defroute $_ifs [[ $resp == none ]] && return route -n add -inet6 -host default "$resp" || return echo "!route -n add -host -inet6 default $resp" >>$_hn } v4_defroute() { local _dr _prompt=" or 'none'" [[ -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 _netfile # Copy any required or optional files found for _netfile in hosts dhclient.conf resolv.conf resolv.conf.tail protocols services; do if [ -f /mnt/etc/${_netfile} ]; then cp /mnt/etc/${_netfile} /etc/${_netfile} 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 # configure all of the non-loopback interfaces which we know about. # refer to hostname.if(5) for hn in /mnt/etc/hostname.*; do # Strip off /mnt/etc/hostname. prefix if=${hn#/mnt/etc/hostname.} # Check for ifconfig'able interface. ifconfig $if >/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= ifconfig $if $name $mask $bcaddr $ext1 $ext2 down cmd="dhclient $if" ;; "rtsol") ifconfig $if $name $mask $bcaddr $ext1 $ext2 up rtsif="$rtsif $if" cmd= ;; "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 [ ! -n "$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 $(/dev/null route -qn add -net 127 127.0.0.1 -reject >/dev/null # Display results... echo "Network interface configuration:" ifconfig -am # enable the resolver if resolv.conf is available route -n show if [ -f /etc/resolv.conf ]; then echo "\nResolver enabled." else echo "\nResolver not enabled." fi } # 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 # 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) isin $_f $DEFAULTSETS && _get_sets=$(addel $_f $_get_sets) done if [[ -z $_sets ]]; then # Show $_src, but delete any ftp password. cat <<__EOT No $OBSD sets were found at $(echo $_src | sed -e 's/\(^ftp:\/\/[^/]*\)\(:[^/]*\)\(@.*\)/\1\3/') Set names are: $THESETS __EOT return fi select_sets "$_sets" "$_get_sets" [[ -n $resp ]] || return _get_sets=$resp ask_yn "Ready to $MODE sets?" yes [[ $resp = n ]] && return for _f in $THESETS ; do isin $_f $_get_sets || continue echo "Getting $_f ..." case $_f in *.tgz) ftp $_ftp_active -o - -V -m "$_src/$_f" | tar zxphf - -C /mnt ;; *) ftp $_ftp_active -o "/mnt/$_f" -V -m "$_src/$_f" ;; esac if [ $? -ne 0 ]; then echo "'$_f' did not install correctly." else DEFAULTSETS=$(rmel $_f $DEFAULTSETS) fi 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 # embeddded 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 } # 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 _ftp_active must be global. install_url() { local _url_type=$1 _file_list _url_base _oifs _prompt _passwd 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 rm -f $SERVERLIST ask_yn "Display the list of known $_url_type servers?" "${_get_server_list:-yes}" _get_server_list=$resp if [[ $_get_server_list == y ]]; then # ftp.openbsd.org == 129.128.5.191 and will remain at # that address for the forseeable future. echo -n "Getting the list from 129.128.5.191 (ftp.openbsd.org)..." ftp $_ftp_active -V -a -o - \ ftp://129.128.5.191/$FTPDIR/ftplist 2>/tmp/ftplisterr \ | sed -ne "/^${_url_type}:\/\//s///p" >$SERVERLIST if [[ -s $SERVERLIST ]]; then echo "done." _prompt="Server? (IP address, hostname, list#, 'done' or '?')" cat -n $SERVERLIST | less -XE else echo "FAILED." cat /tmp/ftplisterr fi fi # Get server IP address or hostname : ${_prompt:="Server? (IP address, hostname or 'done')"} while :; do eval resp=\$_${_url_type}_server_ip ask_until "$_prompt" "$resp" case $resp in done) return ;; "?") [[ -s $SERVERLIST ]] || continue cat -n $SERVERLIST | less -XE ;; +([0-9])) # A numeric hostname is ignored. A number is only used # as a line number in $SERVERLIST. [[ -s $SERVERLIST ]] || continue set -- $(sed -ne "${resp}p" $SERVERLIST) [[ $# -lt 1 ]] && { echo "There is no line $resp." ; continue ; } echo "Using $*" eval _${_url_type}_server_ip=${1%%/*} eval _${_url_type}_server_dir=${1#*/}/$SETDIR # Repeat loop to get user to confirm server address. ;; *) eval _${_url_type}_server_ip=$resp break ;; esac done # Some older servers lie about their support for passive mode ftp, so # ask the user if it worth trying passive mode to the chosen server. # Irrelevant if using a proxy. if [[ $_url_type == ftp && -z $ftp_proxy ]]; then case $_ftp_active in -A) resp=no ;; *) resp=yes ;; esac unset _ftp_active ask_yn "Does the server support passive mode ftp?" $resp [[ $resp == n ]] && _ftp_active=-A fi # Get server directory eval resp=\$_${_url_type}_server_dir ask_until "Server directory?" "${resp:-pub/OpenBSD/$SETDIR}" eval _${_url_type}_server_dir=$resp 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 anonymous _passwd=root@`hostname` if [[ $_ftp_server_login != anonymous ]]; then 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 eval _url_base=$_url_base\$_${_url_type}_server_ip/\$_${_url_type}_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_list_files "$_ftp_server_ip" "$_ftp_server_login" "$_passwd" "$_ftp_server_dir") 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 -o - -V "$_url_base/index.txt" | sed 's/ //') fi install_files "$_url_base" "$_file_list" } # $1 - mount point directory is relative to # $2 - default directory install_mounted_fs() { local _mp=$1 _dir=$2 while :; do ask_until "Pathname to the sets? (or 'done')" "$_dir" [[ $resp == done ]] && return # Accept a valid $_mp relative path. [[ -d $_mp/$resp ]] && { _dir=$_mp/$resp ; break ; } # Accept a valid absolute path. [[ -d /$resp ]] && { _dir=/$resp ; break ; } # Otherwise ask again, with original default dir. echo "The directory '$resp' does not exist." done install_files "file://$_dir" "$(ls -l $_dir)" } install_cdrom() { local _drive _part=c _fstype="-t cd9660" get_drive "CD-ROM" "$CDDEVS" || return _drive=$resp # Only ask detail questions for non-ISO9660 CD-ROMs. if [[ -z $(disklabel $_drive 2>&1 | grep '^ *c: .*ISO9660') ]]; then unset _fstype get_partition $_drive "cd9660" || return set -- $resp _part=$1 [[ -n $2 ]] && _fstype="-t $2" fi mount $_fstype -o ro /dev/$_drive$_part /mnt2 || return install_mounted_fs /mnt2 "$SETDIR" umount -f /mnt2 >/dev/null 2>&1 } install_disk() { local _drive _part _fstype _fsopts get_drive "disk" "$DKDEVS" || return _drive=$resp get_partition $_drive "$MDFSTYPE" || return set -- $resp _part=$1 [[ -n $2 ]] && _fstype="-t $2" [[ $2 == $MDFSTYPE ]] && _fsopts=$MDFSOPTS mount $_fstype -o ro,$_fsopts /dev/$_drive$_part /mnt2 || return install_mounted_fs /mnt2 umount -f /mnt2 >/dev/null 2>&1 } install_nfs() { # Can we actually mount NFS filesystems? if [ ! -f /sbin/mount_nfs ]; then echo "/sbin/mount_nfs not found. Cannot mount NFS filesystems." return fi # Get the IP address of the server ask_until "Server IP address or hostname?" "$_nfs_server_ip" _nfs_server_ip=$resp # Get server path to mount ask_until "Filesystem on server to mount?" "$_nfs_server_path" _nfs_server_path=$resp # Determine use of TCP _nfs_tcp= ask_yn "Use TCP transport? (only works with capable NFS server)" [[ $resp == y ]] && _nfs_tcp=-T # Mount the server if ! mount_nfs $_nfs_tcp -o ro ${_nfs_server_ip}:${_nfs_server_path} /mnt2 ; then echo "Cannot mount NFS server." return fi install_mounted_fs /mnt2 umount -f /mnt2 >/dev/null 2>&1 } install_tape() { local _z _bs # Can't use tape without /bin/mt. if [[ ! -x /bin/mt ]]; then echo "/bin/mt not found. Can't $MODE from tape." return fi # 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 _zoneroot=/mnt/usr/share/zoneinfo/ _zonepath # If the timezone directory structure is not # available, return immediately. [[ ! -d $_zoneroot ]] && return if [[ -L /mnt/etc/localtime ]]; then TZ=$(ls -l /mnt/etc/localtime 2>/dev/null) TZ=${TZ#*${_zoneroot#/mnt}} fi : ${TZ:=GMT} while :; do _zonepath=$_zoneroot ask "What timezone are you in? ('?' for list)" "$TZ" if [[ $resp == ? ]]; then ls -F ${_zonepath} continue; fi _zonepath=${_zonepath}${resp} while [[ -d $_zonepath ]]; do ask "What sub-timezone of '${_zonepath#$_zoneroot}' are you in? ('?' for list)" case $resp in "") ;; ?) ls -F $_zonepath ;; *) _zonepath=$_zonepath/$resp ;; esac done if [[ -f $_zonepath ]]; then TZ=${_zonepath#$_zoneroot} echo -n "Setting local timezone to '$TZ'..." ln -sf /usr/share/zoneinfo/$TZ /mnt/etc/localtime echo "done." return fi echo -n "'${_zonepath#$_zoneroot}'" echo " is not a valid timezone on this system." done } # Check with the user that missing required sets were deliberately skipped. sane_install() { local _s _m for _s in $SANESETS; do isin $_s $DEFAULTSETS || continue ask_yn "'$_s' was not installed.\nAre 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 cat <<__EOT You will now specify the location and names of the $MODE sets you want to load. You will be able to repeat this step until all of your sets have been successfully loaded. If you are not sure what sets to $MODE, refer to the installation notes for details on the contents of each. __EOT while :; do _d= cat <<__EOT Sets can be located on a (m)ounted filesystem; a (c)drom, (d)isk or (t)ape device; or a (f)tp, (n)fs or (h)ttp server. __EOT [[ -z $DEFAULTSETS ]] && _d=done ask "Where are the $MODE sets? (or 'done')" "$_d" case $resp in done) sane_install && return ;; c*|C*) install_cdrom ;; d*|D*) install_disk ;; f*|F*) install_url ftp ;; h*|H*) install_url http ;; m*|M*) install_mounted_fs /mnt ;; n*|N*) install_nfs ;; t*|T*) install_tape ;; *) ;; esac done } # 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). # # 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), # 5) leave out fs_freq and fs_passno fields. # # 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 == \#* || \ $_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//') # 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). # 3) leave out fs_freq and fs_passno fields (i.e. $_rest). echo $_dev /mnt${_mp%/} $_fstype $_opt 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 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. If the mount fails, exit. if ! mount -v -t $_fstype $_async -o $_opt $_dev $_mp ; then # In addition to the error message displayed by mount ... cat <<__EOT FATAL ERROR: Cannot mount filesystems. Double-check your configuration and restart the $MODE. __EOT exit fi done /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 ask_yn "Use the nameserver now?" yes [[ $resp == y ]] && cp /tmp/resolv.conf /tmp/resolv.conf.shadow fi # Get/Confirm an IPv4 default route if an IPv4 address was configured. [[ -n $(ifconfig -a | sed -ne '/[ ]inet .* broadcast /p') ]] && v4_defroute edit_tmp_file hosts 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 } questions() { local _bd _td ask_yn "Start sshd(8) by default?" yes if [[ $resp == n ]]; then echo "sshd_flags=NO # disabled during install" \ >>/mnt/etc/rc.conf.local fi ask_yn "Start ntpd(8) by default?" no if [[ $resp == y ]]; then echo "ntpd_flags= # enabled during install" \ >>/mnt/etc/rc.conf.local fi if [[ -n $MDXAPERTURE ]]; then ask_yn "Do you expect to run the X Window System?" yes if [[ $resp == 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 fi [[ -z $SERIALDEV ]] && return set -- $SERIALDEV _bd=$1 _td=$2 ask_yn "Change the default console to $_bd?" [[ $resp == n ]] && return ask_which "speed" "should $_bd use" "9600 19200 38400 57600 115200" [[ $resp == done ]] && return echo "stty $_bd $resp\nset tty $_bd" >>/mnt/etc/boot.conf sed -e "/^${_td}/s/std.9600/std.${resp}/" \ -e "/^${_td}/s/unknown/vt220 /" \ -e "/${_td}/s/off/on secure/" /mnt/etc/ttys >/tmp/ttys } 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 /tmp/sysctl.conf cp /tmp/sysctl.conf /mnt/etc/sysctl.conf fi echo -n "Making all device nodes..." cd /mnt/dev sh MAKEDEV all # Make sure any devices we added as a result of makedev() calls # are recreated in installed system. for _dev in $DEVSMADE; do sh MAKEDEV $_dev done echo "done." cd / md_installboot $ROOTDISK populateusrlocal [ -x /mnt/$MODE.site ] && /mnt/usr/sbin/chroot /mnt /$MODE.site # Pat on the back. cat <<__EOT CONGRATULATIONS! Your OpenBSD $MODE has been successfully completed! To boot the new system, enter halt at the command prompt. Once the system has halted, reset the machine and boot from the disk. __EOT md_congrats } # ####################################################################### # # Initial actions common to both installs and upgrades. # # Some may require machine dependent routines, which may # call functions defined above, so it's safest to put this # code here rather than at the top of the file. # # ####################################################################### ROOTDISK= ROOTDEV= VERSION=37 VNAME="$(( $VERSION / 10 )).$(( $VERSION % 10 ))" SETDIR="$VNAME/$ARCH" FTPDIR="pub/OpenBSD/$VNAME" OBSD="OpenBSD/$ARCH $VNAME" SERVERLIST=/tmp/serverlist # Do not limit ourselves during installs or upgrades. for _opt in d f l m n p s; do ulimit -$_opt unlimited done # Extract and save one boot's worth of dmesg dmesg | sed -ne '/^OpenBSD /h;/^OpenBSD /!H;${g;p;}' >/var/run/dmesg.boot # Scan /var/run/dmesg.boot for interesting devices. DKDEVS=$(scan_dmesg "${MDDKDEVS:-/^[sw]d[0-9][0-9]* /s/ .*//p}") CDDEVS=$(scan_dmesg "${MDCDDEVS:-/^cd[0-9][0-9]* /s/ .*//p}") MTDEVS=$(scan_dmesg "${MDMTDEVS:-/^[cmsw]t[0-9][0-9]* /s/ .*//p}") IFDEVS=$(get_ifdevs) SERIALDEV=$(get_serialdev) # Devices created with makedev(). DEVSMADE= # 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 misc comp man game xbase xetc xshare xfont xserv site ; do [[ $MODE == upgrade && ( $_set == etc || $_set == xetc ) ]] && continue THESETS="$THESETS ${_set}${VERSION}.tgz" isin $_set xbase xetc xshare xfont xserv 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="bsd base${VERSION}.tgz etc${version}.tgz" # decide upon an editor : ${EDITOR:=ed} [[ -x /usr/bin/vi ]] && EDITOR=vi export EDITOR # umount all filesystems, just in case we are re-running install or upgrade. [[ -f /etc/fstab ]] && umount -av 1>/dev/null 2>&1 umount -v /mnt 1>/dev/null 2>&1 # Introduce ourselves. welcome # Get ROOTDISK, ROOTDEV and SWAPDEV. if [[ $MODE == install && ! -f /etc/fstab ]]; then cat <<__EOT You will now initialize the disk(s) that OpenBSD will use. To enable all available security features you should configure the disk(s) to allow the creation of separate filesystems for /, /tmp, /var, /usr, and /home. __EOT fi set -- $DKDEVS [[ $# -gt 1 ]] && _defdsk=done ask_which "disk" "is the root disk" "$DKDEVS" "$_defdsk" [[ $resp == done ]] && exit makedev $resp || exit ROOTDISK=$resp ROOTDEV=${ROOTDISK}a SWAPDEV=${ROOTDISK}b