### Globals . /usr/lib/network/globals # will load hooks ## Loads a profile. # $1: profile name load_profile() { unset ROUTES [[ -z "$1" ]] && return 1 if [[ ! -f "$PROFILE_DIR/$1" ]]; then report_err "Profile \"$1\" does not exist" return 1 fi report_debug "Loading profile $1" INTERFACE=$(. "$PROFILE_DIR/$1"; echo "$INTERFACE") report_debug "Configuring interface $INTERFACE" if [[ -z "$INTERFACE" ]]; then report_err "Profile missing an interface to configure" return 1 fi if [[ -f "$IFACE_DIR/$INTERFACE" ]]; then report_debug "Interface level configuration enabled: $IFACE_DIR/$INTERFACE" . "$IFACE_DIR/$INTERFACE" fi . "$PROFILE_DIR/$1" # we want profile settings to override, so need to source profile again if [[ ! -f "$CONN_DIR/$CONNECTION" ]]; then report_err "$CONNECTION is not a valid connection, check spelling or look at examples" return 1 fi } ################## # Profile up/down ################## ## Take all registered profiles down. all_down() { for prof in $(find "$STATE_DIR/profiles/" -maxdepth 1 -type f -printf '%f\n'); do profile_down "$prof" done } ## Create the state dir and sub directories if they don't already exist. check_make_state_dir() { [[ ! -d "$STATE_DIR" ]] && mkdir -p "$STATE_DIR"/{interfaces,profiles} for d in interfaces profiles suspend; do [[ ! -d "$STATE_DIR/$d" ]] && mkdir "$STATE_DIR/$d" done } ## Save the list of running profiles and take them down if needed # $1: interface name or "all" # $2: take associated profiles down (optional, default="yes") interface_suspend() { report_debug interface_suspend "$@" check_make_state_dir find "$STATE_DIR/profiles/" -maxdepth 1 -type f -printf '%f\n' \ | while read prof; do # the pipe to "while read" will create a subshell INTERFACE=$(. "$STATE_DIR/profiles/$prof"; echo "$INTERFACE") 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 } ## Save the list of all running profiles and take them down all_suspend() { interface_suspend all } ## Restore saved profiles (for resume purposes). # $@: a list of interfaces not to resume (e.g., because they're disabled) all_resume() { report_debug all_resume "$@" find "$STATE_DIR/suspend/" -maxdepth 1 -type f -printf '%f\n' \ | while read prof; do # the pipe to "while read" will create a subshell INTERFACE=$(. "$STATE_DIR/suspend/$prof"; echo "$INTERFACE") 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 } ## Puts up a profile. # $1: the profile name profile_up() { ( # Keep inside subshell so that options from one profile don't cross to others # exit 1 used in a subshell is effectively exiting a new process check_make_state_dir local status PROFILE="$1" # save PROFILE in a variable so that it's available to PRE_UP/POST_DOWN etc hooks load_profile "$PROFILE" || 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 all_down fi report_try "$PROFILE up" status=$(check_iface "$INTERFACE") report_debug "status reported to profile_up as: $status" case "$status" in external) report_fail "Interface $INTERFACE externally controlled" exit 1 ;; disabled) report_fail "Interface $INTERFACE is disabled" exit 1 ;; "") ;; *) if checkyesno "$CHECK"; then report_fail "Interface $INTERFACE already in use" exit 1 # not necessary to sandbox this call or reload PROFILE afterwards elif ! interface_down "$INTERFACE"; then report_fail exit 1 fi ;; esac 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 "$PROFILE"; then report_debug profile_up "connect failed" report_fail # "$CONN_DIR/$CONNECTION" down "$PROFILE" # JP: should we do this to make sure? exit 1 fi 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" exit 1 fi set_profile up "$PROFILE" unset EXCLUSIVE # Successfully running a new profile; erase any suspended profiles on this interface local iface="$INTERFACE" find "$STATE_DIR/suspend/" -maxdepth 1 -type f -printf '%f\n' \ | while read prof; do # the pipe to "while read" will create a subshell INTERFACE=$(. "$STATE_DIR/suspend/$prof"; echo "$INTERFACE") if [[ "$iface" == "$INTERFACE" ]]; then rm "$STATE_DIR/suspend/$prof" fi done report_success ); return $? } ## Puts a profile down. # $1: the profile name profile_down() { ( check_make_state_dir local status PROFILE="$1" # save PROFILE in a variable so that it's available to PRE_UP/POST_DOWN etc hooks load_profile "$PROFILE" || exit 1 status=$(check_iface "$INTERFACE") report_debug "status reported to profile_down as: $status" if [[ "$status" != "$PROFILE" ]]; then # if interface not available to be controlled by netcfg, then # any profiles should have been removed by check_iface # else we get here if another profile is running report_err "Profile not connected" exit 1 fi report_try "$PROFILE down" if [[ "$(check_iface "$INTERFACE")" == "external" ]]; then report_fail "$interface was connected by another application" exit 1 fi 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 "$PROFILE"; then report_debug profile_up "disconnect failed" report_fail exit 1 fi if ! ( eval $POST_DOWN ); then # JP: sandbox the eval report_debug profile_down "POST_DOWN failed" report_fail exit 1 fi set_profile down "$PROFILE" report_success ); return $? } ## Check if variable is a member of an array # $1: the variable to find # $2...: the array elements inarray() { local item search="$1" shift for item in "$@"; do if [[ "$item" == "$search" ]]; then return 0 fi done return 1 } ## Check if a given quirk is enabled # $1: the quirk name quirk() { inarray "$1" "${QUIRKS[@]}" return $? } # interface_down interface # take interface down # interface_down() { local profile=$(check_iface "$1") case "$profile" in ""|disabled) return 0 ;; external) return 1 ;; *) profile_down "$profile" ;; esac } # interface_reconnect interface # reconnects the profile active on interface interface_reconnect() { local profile=$(check_iface "$1") case "$profile" in ""|disabled|external) return 1 ;; *) profile_down "$profile" profile_up "$profile" ;; esac } ## # check_iface interface # Return 0 if interface unavailable (in use by a profile or externally, or disabled) # Return 1 if interface down and available to be used # check_iface() { if [[ -f "$STATE_DIR/interfaces/$1" ]]; then ( . "$STATE_DIR/interfaces/$1" echo "$PROFILE" # may be: external, disabled, or a profile name return 0 ) return 0 else return 1 fi } # 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" } # check_profile profile # Return 0 if profile registered as being up # Return 1 if profile not registered # check_profile() { [[ -f "$STATE_DIR/profiles/$1" && ! -f "$STATE_DIR/suspend/$1" ]] && return 0 return 1 } ### Status setting functions ## # set_profile up/down profile # Set profile state, either up or down # set_profile() { local INTERFACE if [[ "$1" == "up" ]]; then INTERFACE=$(. "$PROFILE_DIR/$2"; echo "$INTERFACE") 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 # JP: skip if profile not already up INTERFACE=$(. "$STATE_DIR/profiles/$2"; echo "$INTERFACE") 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() { local PROFILE="$3" [[ -z "$PROFILE" ]] && PROFILE=external 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 } is_interface() { local INTERFACE="$1" if [[ ! -e "/sys/class/net/$INTERFACE" ]]; then if ! echo "$INTERFACE" | fgrep -q ":"; then return 1 fi fi return 0 } ## Changes a network interface state. # $1: up, flush, or down. # $2: the interface name bring_interface() { local INTERFACE="$2" case "$1" in up) if ! ( eval $IFACE_UP ); then return 1 fi ip link set dev "$INTERFACE" up &>/dev/null sleep "${UP_SLEEP:-2}" ;; flush|down) if ! ( eval $IFACE_DOWN ); then return 1 fi ip addr flush dev "$INTERFACE" &>/dev/null ;;& down) ip link set dev "$INTERFACE" down &>/dev/null ;; esac } # vim: ft=sh ts=4 et sw=4: