From: rpe Date: Mon, 24 Apr 2017 20:31:48 +0000 (+0000) Subject: Introduce a new function parse_hn_line() that replaces the existing X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=8222f376e866f896c3500d69e238ce248168b7aa;p=openbsd Introduce a new function parse_hn_line() that replaces the existing hostname.if(5) parsing code in ifstart(). Add a -n option to netstart to only print the interface configuration commands instead of executing them. Add a HN_DIR variable, that points to the directory of the hostname.if files (default /etc) that allows for future regression tests. - add new parse_hn_line() function - change ifstart() - rename $if to $_if - don't ifconfig or ifconfig create if -n option is used - replace hostname.if(5) parsing code with new parse_hn_line() - just print configuration commands if -n option is used - autoconf now happens in ifstart(), remove ifv6autoconf() - introduce HN_DIR variable for the hostname.if file location - add handling of the -n option to only print config commands - ensure -n is only used if interfaces are specified as parameters Discussed with and positive feedback from many 'commit' deraadt@ OK sthen@ --- diff --git a/etc/netstart b/etc/netstart index f8228583b1c..7c1d8ea5114 100644 --- a/etc/netstart +++ b/etc/netstart @@ -1,6 +1,6 @@ #!/bin/sh - # -# $OpenBSD: netstart,v 1.176 2017/04/08 08:33:05 rpe Exp $ +# $OpenBSD: netstart,v 1.177 2017/04/24 20:31:48 rpe Exp $ # Turn off Strict Bourne shell mode. set +o sh @@ -19,21 +19,80 @@ stripcom() { done <$_file } +# Parse and "unpack" a hostname.if(5) line given as positional parameters. +# Fill the _cmds array with the resulting interface configuration commands. +parse_hn_line() { + local _af=0 _name=1 _mask=2 _bc=3 _prefix=2 _c _cmd _prev _daddr + set -A _c -- "$@" + set -o noglob + + case ${_c[_af]} in + ''|*([[:blank:]])'#'*) + return + ;; + inet) ((${#_c[*]} > 1)) || return + [[ ${_c[_name]} == alias ]] && _mask=3 _bc=4 + [[ -n ${_c[_mask]} ]] && _c[_mask]="netmask ${_c[_mask]}" + if [[ -n ${_c[_bc]} ]]; then + _c[_bc]="broadcast ${_c[_bc]}" + [[ ${_c[_bc]} == *NONE ]] && _c[_bc]= + fi + _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}" + ;; + inet6) ((${#_c[*]} > 1)) || return + if [[ ${_c[_name]} == autoconf ]]; then + _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}" + rtsolif="$rtsolif $_if" + return + fi + [[ ${_c[_name]} == alias ]] && _prefix=3 + [[ -n ${_c[_prefix]} ]] && _c[_prefix]="prefixlen ${_c[_prefix]}" + _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}" + ;; + dest) ((${#_c[*]} == 2)) && _daddr=${_c[1]} || return + _prev=$((${#_cmds[*]} - 1)) + ((_prev >= 0)) || return + set -A _c -- ${_cmds[_prev]} + _name=3 + [[ ${_c[_name]} == alias ]] && _name=4 + _c[_name]="${_c[_name]} $_daddr" + _cmds[$_prev]="${_c[@]}" + ;; + dhcp) _c[0]= + _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]} down;dhclient $_if" + dhcpif="$dhcpif $_if" + ;; + rtsol) # XXX Support the rtsol keyword for some time to enable a smooth + # XXX transition to autoconf. + _c[0]= + _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]} up" + _cmds[${#_cmds[*]}]="ifconfig $_if inet6 autoconf" + rtsolif="$rtsolif $_if" + ;; + '!'*) _cmd=$(print -- "${_c[@]}" | sed 's/\$if/'$_if'/g') + _cmds[${#_cmds[*]}]="${_cmd#!}" + ;; + *) _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}" + ;; + esac + unset _c +} + # Start a single interface. # Usage: ifstart if1 ifstart() { - # Note: Do not rename the 'if' variable which is documented as being - # usable in hostname.if(5) files. - local if=$1 _file=/etc/hostname.$1 _stat + local _if=$1 _file=$HN_DIR/hostname.$1 _cmds _i=0 _line _stat + set -A _cmds # Interface names must be alphanumeric only. We check to avoid # configuring backup or temp files, and to catch the "*" case. - [[ $if != +([[:alpha:]])+([[:digit:]]) ]] && return + [[ $_if != +([[:alpha:]])+([[:digit:]]) ]] && return if [[ ! -f $_file ]]; then echo "netstart: $_file: No such file or directory" return fi + # Not using stat(1), we can't rely on having /usr yet. set -A _stat -- $(ls -nL $_file) if [ "${_stat[0]#???????} ${_stat[2]} ${_stat[3]}" != "--- 0 0" ]; then @@ -41,97 +100,30 @@ ifstart() { chmod -LR o-rwx $_file chown -LR root.wheel $_file fi - # Check for ifconfig'able interface. - (ifconfig $if || ifconfig $if create) >/dev/null 2>&1 || return - - # 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="$@" + + # Check for ifconfig'able interface, except if -n option is specified. + if ! $PRINT_ONLY; then + (ifconfig $_if || ifconfig $_if create) >/dev/null 2>&1 || + return + fi + + # Parse the hostname.if(5) file and fill _cmds array with interface + # configuration commands. + set -o noglob + while IFS= read -- _line; do + parse_hn_line $_line + done <$_file + + # Apply the interface configuration commands stored in _cmds array. + while ((_i < ${#_cmds[*]})); do + if $PRINT_ONLY; then + print -r -- "${_cmds[_i]}" else - # Read the next line or exit the while loop. - read af name mask bcaddr ext1 ext2 || break + eval "${_cmds[_i]}" fi - # $af can be "dhcp", "up", "rtsol", an address family, commands, - # or a comment. - case "$af" in - "#"*|"") - # Skip comments and empty lines. - continue - ;; - "!"*) # Parse commands. - cmd="${af#*!} ${name} ${mask} ${bcaddr} ${ext1} ${ext2}" - ;; - "dhcp") - [ "$name" = "NONE" ] && name= - [ "$mask" = "NONE" ] && mask= - [ "$bcaddr" = "NONE" ] && bcaddr= - cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 down" - cmd="$cmd;dhclient $if" - dhcpif="$dhcpif $if" - ;; - "rtsol") - rtsolif="$rtsolif $if" - 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" - ;; - *) - cmd2="$dt $dtaddr" - ;; - esac - case $af in - inet) - if [ ! -n "$name" ]; then - echo "/etc/hostname.$if: inet alone is invalid" - return - fi - [ "$mask" ] && cmd="$cmd netmask $mask" - if [ "$bcaddr" -a "X$bcaddr" != "XNONE" ]; then - cmd="$cmd broadcast $bcaddr" - fi - ;; - inet6) - if [ ! -n "$name" ]; then - echo "/etc/hostname.$if: inet6 alone is invalid" - return - fi - [ "$mask" ] && cmd="$cmd prefixlen $mask" - cmd="$cmd $bcaddr" - ;; - *) - cmd="$cmd $mask $bcaddr" - ;; - esac - cmd="$cmd $ext1 $ext2" - ;; - esac - eval "$cmd" - done <$_file + ((_i++)) + done + unset _cmds } # Start multiple interfaces by driver name. @@ -196,6 +188,23 @@ fi FUNCS_ONLY=1 . /etc/rc.d/rc.subr _rc_parse_conf +HN_DIR=${HN_DIR:-/etc} +PRINT_ONLY=false +USAGE="USAGE: ${0##*/} [-n] [interface ...]" +while getopts ":n" opt; do + case $opt in + n) PRINT_ONLY=true;; + *) print -u2 "$USAGE"; exit 1;; + esac +done +shift $((OPTIND-1)) + +# Option -n is only supported if interface names are specified as parameters. +if $PRINT_ONLY && (($# == 0)); then + print -u2 "Missing parameters.\n$USAGE" + exit 1 +fi + # If we were invoked with a list of interface names, just reconfigure these # interfaces (or bridges), add default routes and return. if (($# > 0)); then