diff options
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network | 460 |
1 files changed, 290 insertions, 170 deletions
diff --git a/src/network b/src/network index b45caaa..2720caa 100644 --- a/src/network +++ b/src/network @@ -1,272 +1,391 @@ -### Globals -PROFILE_DIR="/etc/network.d/" -SUBR_DIR="/usr/lib/network/" -CONN_DIR="${SUBR_DIR}/connections/" -STATE_DIR="/var/run/network/" - -### Messages +################################## ## -# err msg -# output specified message -err_append() { - echo " - $*" -} +# /usr/lib/network/network +## +################################## + +. /usr/lib/network/globals -err() { - printhl "$*" -} -### Profile loading +### Profile listing and loading ## +# list_profiles +# Outputs a list of all profiles +list_profiles() { + # JP: follow aliases with -L, also skip profiles that start with '.' or end with '~' or '.conf' (so profile.conf can be the wpa.conf file for profile) + find -L "$PROFILE_DIR/" -maxdepth 1 -type f -not -name '*~' -not -name '*.conf' -not -name '.*' -printf "%f\n" +} # load_profile profile # source the profile load_profile() { - validate_profile $1 || return 1 - . $PROFILE_DIR/$1 + validate_profile "$1" || return 1 + . "$PROFILE_DIR/$1" } # validate_profile profile # check whether profile exists and is usable -validate_profile() -{ +validate_profile() { [[ -z "$1" ]] && return 1 - if [[ ! -f $PROFILE_DIR/$1 ]]; then - err "Profile \"$1\" does not exist" + if [[ ! -f "$PROFILE_DIR/$1" ]]; then + report_err "Profile \"$1\" does not exist." return 1 fi - . $PROFILE_DIR/$1 + . "$PROFILE_DIR/$1" if [[ -z "$INTERFACE" ]]; then - err "Profile missing an interface to configure" + report_err "Profile error: no INTERFACE to configure." return 1 fi - if [[ ! -f $CONN_DIR/$CONNECTION ]]; then - err "$CONNECTION is not a valid connection, check spelling or look at examples" + if [[ ! -f "$CONN_DIR/$CONNECTION" ]]; then + report_err "Profile error: $CONNECTION is not a valid connection, check spelling or look at examples." return 1 fi } -### Profile up/down +### Profile suspend/resume ## -# all_down -# take all profiles down -# -all_down() -{ - for prof in $(find ${STATE_DIR}/profiles/ -maxdepth 1 -type f -printf '%f\n'); do - profile_down $prof +# interface_suspend interface/all [call_profile_down? default=yes] +# store a list of running profiles and take them down (unless $2 is no) +interface_suspend() { + report_debug interface_suspend "$@" + + [[ ! -d "$STATE_DIR" ]] && mkdir -p "$STATE_DIR"/{interfaces,profiles} + [[ ! -d "$STATE_DIR/suspend" ]] && mkdir "$STATE_DIR/suspend" + + find "$STATE_DIR/profiles/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + # the pipe to "while read" will create a subshell, so sourced variables will already be in a sandbox + # we just need to clear INTERFACE which is all we care about + unset INTERFACE + . "$STATE_DIR/profiles/$prof" + if [[ "$1" == all || "$1" == "$INTERFACE" ]]; then + report_notify "suspending interface $INTERFACE with profile $prof" + cp "$STATE_DIR/profiles/$prof" "$STATE_DIR/suspend/" + if checkyesno "${2:-yes}"; then + profile_down "$prof" + fi + fi done } - # all_suspend # store a list of running profiles and take them down -# -all_suspend() -{ - [[ ! -d $STATE_DIR ]] && mkdir -p $STATE_DIR/{interfaces,profiles} - [[ ! -d $STATE_DIR/suspend ]] && mkdir $STATE_DIR/suspend - - for prof in $(find $STATE_DIR/profiles/ -maxdepth 1 -type f); do - cp $prof $STATE_DIR/suspend/ - profile_down $(basename $prof) - done +all_suspend() { + interface_suspend all } +# all_resume +# resume suspended interfaces +# optional arguments: interfaces not to resume (e.g., because they're disabled) +all_resume() { + report_debug all_resume "$@" -# all_suspend -# store a list of running profiles and take them down -# -all_resume() -{ - for prof in $(find $STATE_DIR/suspend/ -maxdepth 1 -type f); do - profile_up $(basename $prof) - rm $prof + find "$STATE_DIR/suspend/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + # the pipe to "while read" will create a subshell, so sourced variables will already be in a sandbox + # we just need to clear INTERFACE which is all we care about + unset INTERFACE + . "$STATE_DIR/suspend/$prof" + if [[ $# -eq 0 || ! " $* " =~ " $INTERFACE " ]]; then + report_notify "resuming interface $INTERFACE with profile $prof" + profile_up "$prof" + rm -f "$STATE_DIR/suspend/$prof" # if profile_up succeeds, it will have already removed this + fi done } -# profile_up profile +### Profile up/down +## +# profile_up profile [literal essid] # put all profiles up # -profile_up() -{ +profile_up() { ( # Keep inside subshell so that options from one profile don't cross to others - # exit 1 used as a subshell is effectively a new process - [[ ! -d $STATE_DIR ]] && mkdir -p $STATE_DIR/{interfaces,profiles} + # exit 1 used in a subshell is effectively exiting a new process + [[ ! -d "$STATE_DIR" ]] && mkdir -p "$STATE_DIR"/{interfaces,profiles,suspend} + + local status PROFILE="$1" # save PROFILE in a variable so that it's available to PREUP/POSTDOWN etc hooks - load_profile $1 || exit 1 + load_profile "$PROFILE" || exit 1 - check_profile $1 && err "$1 already connected" && exit 1 + if check_profile "$PROFILE"; then + report_err "$PROFILE already connected." + exit 1 + fi # NETWORKS_EXCLUSIVE, rc.conf: Profiles are globally mutually exclusive # EXCLUSIVE, network.d/profile: Individual profile is mutually exclusive - if checkyesno $NETWORKS_EXCLUSIVE || checkyesno $EXCLUSIVE; then + if checkyesno "$NETWORKS_EXCLUSIVE" || checkyesno "$EXCLUSIVE"; then all_down fi - stat_busy "$1 up" + report_try "$PROFILE up" - if check_iface $INTERFACE; then - if checkyesno $CHECK; then - err_append "Interface $INTERFACE already in use" - stat_fail && exit 1 - else - interface_down $INTERFACE || exit 1 - load_profile $1 - fi - fi + status=$(query_iface "$INTERFACE" "$CONNECTION") + report_debug "status reported to profile_up as: $status" + + if [[ -n "$status" && "$status" != "$PROFILE" ]]; then + case "$status" in + disabled) + # interface radio-switch disabled + report_fail "INTERFACE $INTERFACE is disabled." + exit 1 + ;; + external) + # interface being controlled externally + report_fail "INTERFACE $INTERFACE was configured by another application." + exit 1 + ;; + *) + # report_fail if iface in use by another profile? (default no) + if checkyesno "$CHECK"; then + report_fail "INTERFACE $INTERFACE already in use by another profile." + exit 1 + elif ! ( # use subshell so interface_down -> profile_down doesn't overwrite loaded profile + interface_down "$INTERFACE" "$CONNECTION" # supply current CONNECTION-type as hint + ); then + report_fail + exit 1 + fi + ;; + esac + fi - eval $PRE_UP || exit 1 + + if ! ( eval $PRE_UP ); then # JP: sandbox the eval so variables don't bleed into current function + report_debug profile_up "PRE_UP failed" + report_fail + exit 1 + fi - if ! ${CONN_DIR}/${CONNECTION} up $1; then - stat_fail + if ! "$CONN_DIR/$CONNECTION" up "$PROFILE" "$2"; then + report_debug profile_up "connect failed" + report_fail + # "$CONN_DIR/$CONNECTION" down "$PROFILE" "$2" # JP: should we do this to make sure? exit 1 fi - eval $POST_UP || exit 1 + if ! ( eval $POST_UP ); then # JP: sandbox the eval + report_debug profile_up "POST_UP failed" + report_fail + # failing POST_UP will take interface down + "$CONN_DIR/$CONNECTION" down "$PROFILE" "$2" + exit 1 + fi - set_profile up $1 - unset EXCLUSIVE - - stat_done + set_profile up "$PROFILE" + # unset EXCLUSIVE + report_success ); return $? } - # profile_down profile # take profile down # -profile_down() -{ +profile_down() { ( - [[ ! -d $STATE_DIR ]] && mkdir -p $STATE_DIR/{interfaces,profiles} + [[ ! -d "$STATE_DIR" ]] && mkdir -p "$STATE_DIR"/{interfaces,profiles,suspend} - load_profile $1 || exit 1 + local status PROFILE="$1" # save PROFILE in a variable so that it's available to PREUP/POSTDOWN etc hooks + + load_profile "$PROFILE" || exit 1 + + status=$(query_iface "$INTERFACE" "$CONNECTION") + report_debug "status reported to profile_down as: $status" - if ! check_profile $1; then - err "Profile not connected" - exit 1 - fi + if [[ "$status" != "$PROFILE" ]]; then + # if interface not available to be controlled by netcfg, then + # any profiles will have been removed by check_iface + # else another profile is running + report_err "Profile wasn't connected." + exit 1 + fi - stat_busy "$1 down" - if [[ "$(get_iface_prof $INTERFACE)" == "external" ]]; then - err_append "$interface was connected by another application" - stat_fail - exit 1 - fi + report_try "$PROFILE down" +# JP: the following will have already been caught by the block above... +# if [[ "$status" == external ]]; then +# report_fail "Interface $INTERFACE was configured by another application." +# exit 1 +# fi - eval $PRE_DOWN || exit 1 + if ! ( eval $PRE_DOWN ); then # JP: sandbox the eval + report_debug profile_down "PRE_DOWN failed" + true # JP: did we want failing PRE_DOWN to leave the profile active? + # report_fail + # exit 1 + fi - if ! ${CONN_DIR}/${CONNECTION} down $1; then - stat_fail + if ! "$CONN_DIR/$CONNECTION" down "$PROFILE" "$2"; then + report_debug profile_up "disconnect failed" + report_fail exit 1 fi - eval $POST_DOWN || exit 1 + if ! ( eval $POST_DOWN ); then # JP: sandbox the eval + report_debug profile_down "POST_DOWN failed" + report_fail + exit 1 + fi - set_profile down $1 - stat_done + set_profile down "$PROFILE" + report_success ); return $? } - -# Check if variable is a member of an array -inarray() -{ -search=$1 -shift -for item in $*; do - if [[ "$item" == "$search" ]]; then - return 0 - fi -done -return 1 +# all_down +# take all registered profiles down +all_down() { + find "$STATE_DIR/profiles/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + profile_down "$prof" + done +} +# interface_down interface +# take interface down +# +interface_down() { + local status=$(query_iface "$@") # we may not be hinting what CONNECTION type to use, e.g. when called from /usr/bin/netcfg + case "$status" in + none|disabled) return 0;; + external) return 1;; + *) profile_down "$status";; + esac } -quirk() { -inarray $1 ${QUIRKS[@]} -return $? + +### Status setting functions +## +# set_profile up/down profile +# Set profile state, either up or down +# +set_profile() { + if [[ "$1" == up ]]; then + ( # subshell creates sandbox for sourced variables + . "$PROFILE_DIR/$2" + cp "$PROFILE_DIR/$2" "$STATE_DIR/profiles/" + echo "$2" > "$STATE_DIR/last_profile" + set_iface up "$INTERFACE" "$2" + ) + elif [[ "$1" == down && -f "$STATE_DIR/profiles/$2" ]]; then + ( # subshell + . "$STATE_DIR/profiles/$2" + rm "$STATE_DIR/profiles/$2" + set_iface down "$INTERFACE" "$2" + ) + fi } -# interface_down interface -# take interface down +# set_iface up/down interface [profile/disabled/external] +# Set interface status to up/down (really up means "unavailable" (may be down & disabled) and down means "available" +# optionally link it to a profile. # -interface_down() -{ - local prof=$(get_iface_prof $1) - profile_down $prof - return $? +set_iface() { + local PROFILE="${3:-external}" # JP: this is equivalent to the two-line block that was here before + if [[ "$1" == up ]]; then + echo "PROFILE=$PROFILE" > "$STATE_DIR/interfaces/$2" + elif [[ "$1" == down ]]; then + rm -f "$STATE_DIR/interfaces/$2" # JP: add -f so we don't complain if the interface isn't up + fi } -### Query functions + + +### Checking/querying functions ## # check_iface interface # Return 0 if interface up # Return 1 if interface down # check_iface() { - [[ -f $STATE_DIR/interfaces/$1 ]] && return 0 - return 1 + [[ -f "$STATE_DIR/interfaces/$1" ]] && return 0 + return 1 } +############################################################################ +# Note: get_iface_prof left in for compatibility with any external code that calls it +# but in this codebase, it's superceded by query_iface # get_iface_prof interface # Echo interface profile and return 0 if up # Return 1 if down. # get_iface_prof() { - if check_iface $1; then - . $STATE_DIR/interfaces/$1 + if check_iface "$1"; then + . "$STATE_DIR/interfaces/$1" echo $PROFILE else return 1 fi } - -# list_profiles -# Outputs a list of all profiles -list_profiles() { - find $PROFILE_DIR/ -maxdepth 1 -type f -printf "%f\n" -} +############################################################################ # check_profile profile -# Return 0 if profile up -# Return 1 if profile down +# Return 0 if profile registered as being up +# Return 1 if profile not registered # check_profile() { - [[ -f $STATE_DIR/profiles/$1 ]] && return 0 + [[ -f "$STATE_DIR/profiles/$1" && ! -f "$STATE_DIR/suspend/$1" ]] && return 0 return 1 -} +} + + +# query_iface interface [connection] +# Return 0 if interface up or otherwise unavailable for connecting +# Return 1 if interface down (and so free to be connected) +# echoes which profile controls the interface (or "external" or "disabled") + +query_iface() { + + local INTERFACE="$1" PROFILE connection="$2" + + if [[ -z "$connection" ]]; then # JP: the difficulty with factoring this code into the CONNECTION routines + # is that we sometimes want to query/control the interface without knowing what CONNECTION type it instantiates + # so we supply the CONNECTION type as a hint ($2) when possible + # and when CONNECTION wasn't hinted, we default to wireless-dbus if iface verifies as wireless, else ethernet-iproute + if "$CONN_DIR/wireless-dbus" verify "$INTERFACE"; then + connection=wireless-dbus + else + connection=ethernet-iproute + fi + fi + + if ! "$CONN_DIR/$connection" query "$INTERFACE" active; then + # interface is not up + if [[ -f "$STATE_DIR/interfaces/$INTERFACE" ]]; then # but the interface thinks it's running a profile... + . "$STATE_DIR/interfaces/$INTERFACE" # these files should only contain a PROFILE=... statement + if [[ "$PROFILE" == disabled ]]; then + echo "disabled" + return 0 + elif [[ "$PROFILE" != external ]]; then + report_warn "INTERFACE $INTERFACE no longer being controlled by $PROFILE" + set_profile down "$PROFILE" + PROFILE= + fi + set_iface down "$INTERFACE" # update iface status: no longer profile/external + fi + echo "$PROFILE" + return 1 + else + "$CONN_DIR/$connection" query "$INTERFACE" profile + return $? + fi -### Status setting functions -## -# set_profile up/down profile -# Set profile state, either up or down -# -set_profile() { - if [[ "$1" == "up" ]]; then - . $PROFILE_DIR/$2 - cp $PROFILE_DIR/$2 $STATE_DIR/profiles/ - echo $2 > $STATE_DIR/last_profile - set_iface up $INTERFACE $2 - - elif [[ "$1" == "down" ]]; then - . $STATE_DIR/profiles/$2 - rm $STATE_DIR/profiles/$2 - set_iface down $INTERFACE $2 - fi } -# set_iface up/down interface [profile] -# Set interface status to up/down -# optionally link it to a profile. -# -set_iface() { - PROFILE=$3 - [[ -z "$PROFILE" ]] && PROFILE=external - if [[ "$1" == "up" ]]; then - echo "PROFILE=$PROFILE" > $STATE_DIR/interfaces/$2 - elif [[ "$1" == "down" ]]; then - rm $STATE_DIR/interfaces/$2 - fi + +### Utility functions +## +# Check if variable is a member of an array +inarray() { + local item search="$1" + shift + for item; do + if [[ "$item" == "$search" ]]; then + return 0 + fi + done + return 1 } +quirk() { + inarray "$1" "${QUIRKS[@]}" +} ### From FreeBSD's /etc/rc.subr ## @@ -274,11 +393,10 @@ set_iface() { # Test $1 variable, and warn if not set to YES or NO. # Return 0 if it's "yes" (et al), nonzero otherwise. # -checkyesno() -{ - _value=${1} +checkyesno() { + local _value="$1" #debug "checkyesno: $1 is set to $_value." - case $_value in + case "$_value" in # "yes", "true", "on", or "1" [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) @@ -295,3 +413,5 @@ checkyesno() ;; esac } + + |