#! /bin/bash . /usr/lib/netctl/globals + . /usr/lib/netctl/globals NETCTL_VERSION=1.28 ++ NETCTL_VERSION=1.28 PROFILE_DIR="/etc/netctl" ++ PROFILE_DIR=/etc/netctl SUBR_DIR="/usr/lib/netctl" ++ SUBR_DIR=/usr/lib/netctl STATE_DIR="/run/netctl" ++ STATE_DIR=/run/netctl STATE_FILE="${NETCTL_STATE_FILE:-/var/lib/netctl/netctl.state}" ++ STATE_FILE=/var/lib/netctl/netctl.state ### Logging/Error reporting report_notice() { echo "$*" } report_error() { local prefix="<3>" suffix="" if [[ -t 2 ]]; then prefix=$(tput bold; tput setaf 1) suffix=$(tput sgr0) fi echo "$prefix$*$suffix" >&2 } report_debug() { is_yes "${NETCTL_DEBUG:-no}" && echo "DEBUG: $*" >&2 } exit_error() { report_error "$@" exit 1 } ### Variable management ## Check if a variable occurs in an array # $1: the variable to find # $2...: the array elements in_array() { local hay needle=$1 shift for hay; do [[ $hay == "$needle" ]] && return 0 done return 1 } ## Check if a variable denotes a positive truth value # $1: the variable to check, use is_yes "${VAR:-yes}" to set a default is_yes() { case ${1,,} in yes|true|on|1) return 0 ;; no|false|off|0) return 1 ;; *) report_error "Not a valid truth value: '$1'" return 2 ;; esac } ### Control flow ## Show what we evaluate when debugging, but always evaluate do_debug() { report_debug "${FUNCNAME[1]}:" "$@" "$@" } ## Evaluate with a permissive umask do_readable() { local result umask 022 "$@" result=$? umask 077 return $result } ## Exit if we are not effectively root # $1: program name (optional) ensure_root() { (( EUID == 0 )) || exit_error "${1-$0} needs root privileges" } ## Waits until a statement succeeds or a timeout occurs # $1: timeout in seconds # $2...: condition command timeout_wait() { local timeout=$1 (( timeout *= 5 )) shift until eval "$@"; do (( timeout-- > 0 )) || return 1 sleep 0.2 done return 0 } ### Profile management ## Load all available hooks load_hooks() { local hook while IFS= read -r hook; do source "$hook" done < <(find -L "$PROFILE_DIR/hooks" -maxdepth 1 -type f -executable -not -name '.*' -not -name '*~' -not -name $'*\n*' | sort) } ## Load interface configuration, if present # $1: interface name load_interface_config() { local config_file="$PROFILE_DIR/interfaces/$1" if [[ -x $config_file ]]; then source "$config_file" fi } ## Sources all hooks and a profile (but no interface configuration) # $1: profile name load_profile() { # Expose the profile name Profile=$1 if [[ -z $Profile || ! -r "$PROFILE_DIR/$Profile" ]]; then exit_error "Profile '$Profile' does not exist or is not readable" fi load_hooks source "$PROFILE_DIR/$Profile" if [[ -z $Interface ]]; then exit_error "Profile '$Profile' does not specify an interface" fi load_interface_config "$Interface" if [[ ! -r "${Connection:+$SUBR_DIR/connections/$Connection}" ]]; then exit_error "Profile '$Profile' does not specify a valid connection" fi source "$SUBR_DIR/connections/$Connection" } ## List all acceptable profiles names separated by newlines list_profiles() { # Follow aliases with -L, skip forbidden/reserved names find -L "$PROFILE_DIR/" -maxdepth 1 -type f -not -name '.*' -not -name '*~' -not -name $'*\n*' -not -name '*.action' -not -name '*.conf' -not -name '*.service' -printf '%f\n' } ## List names of profiles for a given interface and/or connection # $1: interface (optional) # $2: connection (optional) filter_profiles() { list_profiles | while IFS= read -r Profile; do if ( source "$PROFILE_DIR/$Profile" &> /dev/null [[ $Interface && ( -z $1 || $1 == "$Interface" ) ]] || exit load_interface_config "$Interface" > /dev/null [[ $Connection && ( -z $2 || $2 == "$Connection" ) ]] || exit ); then printf '%s\n' "$Profile" fi done } ## Exit if a profile file is not syntactically correct # $1: profile name verify_profile() { /bin/bash -n "$PROFILE_DIR/$1" || exit 1 } ## Wrapper around systemctl converting profile names to unit names # $1: systemctl command # $2...: profile names sd_call() { local command=$1 shift systemctl $command $(systemd-escape --template=netctl@.service "$@") } ## Retrieves the status string from the unit for a profile # $1: profile name sd_status_text() { sd_call "show --property=StatusText --value" "$1" } # Set a restrictive umask do_readable : ++ do_readable : ++ local result ++ umask 022 ++ : ++ result=0 ++ umask 077 ++ return 0 # vim: ft=sh ts=4 et sw=4: . "$SUBR_DIR/interface" + . /usr/lib/netctl/interface ## /usr/lib/netctl/globals needs to be sourced before this file ## Check if a string represents a network interface # $1: potential interface name is_interface() { # Strip any old school alias specifier [[ -d "/sys/class/net/${1%%:?*}" ]] } ## Add an interface # $1: interface type # $2: interface name # $3: interface MAC address (optional) # $4: interface link (optional) # $5...: additional type related arguments interface_add() { local type="$1" name="$2" address="$3" link="$4" if [[ -e "/sys/class/net/$address/address" ]]; then address=$(< "/sys/class/net/$address/address") fi do_debug ip link add ${link:+link "$link"} name "$name" ${address:+address "$address"} type "$type" "${@:5}" $LinkOptions || return load_interface_config "$name" } ## Delete an interface # $1: interface name interface_delete() { bring_interface_down "$1" ip link delete "$1" } ## Check if an interface is up # $1: interface name interface_is_up() { local flags read flags < "/sys/class/net/${1%%:?*}/flags" # IFF_UP is defined as 0x1 in linux/if.h (( flags & 0x1 )) } ## Activate an interface # $1: interface name bring_interface_up() { local interface=$1 ip link set dev "$interface" up &>/dev/null timeout_wait "${TimeoutUp:-5}" 'interface_is_up "$interface"' } ## Deactivate an interface # $1: interface name bring_interface_down() { local interface=$1 ip link set dev "$interface" down &>/dev/null # We reuse the up timeout (down normally is faster) timeout_wait "${TimeoutUp:-5}" '! interface_is_up "$interface"' } # vim: ft=sh ts=4 et sw=4: ## Indicate that the network stack for the profile is up network_ready() { if ! is_yes "${WaitOnline:-no}" && ! is_yes "${NETWORK_READY:-no}"; then do_debug systemd-notify --ready NETWORK_READY=yes fi } ## Describe the status of the service for the profile # $1: status string, should be "online" when the profile gets connected network_status() { do_debug systemd-notify --status="$1" if [[ $1 == "online" ]] && is_yes "${WaitOnline:-no}"; then WaitOnline=no network_ready fi } ## Start the loaded profile network_start() { report_notice "Starting network profile '$Profile'..." if is_interface "$Interface" && interface_is_up "$Interface" && \ ! is_yes "${ForceConnect:-no}"; then report_error "The interface of network profile '$Profile' is already up" exit 1 fi if ! "${Connection}_up"; then report_error "Failed to bring the network up for profile '$Profile'" exit 1 fi network_ready # Sandbox the eval if ! ( do_debug eval "$ExecUpPost" ); then report_error "ExecUpPost failed for network profile '$Profile'" # Failing ExecUpPost will take the connection down "${Connection}_down" exit 1 fi network_status "online" report_notice "Started network profile '$Profile'" } ## Stop the loaded profile network_stop() { report_notice "Stopping network profile '$Profile'..." # Sandbox the eval if ! ( do_debug eval "$ExecDownPre" ); then report_error "ExecDownPre failed for network profile '$Profile'" # Failing ExecDownPre will leave the profile active exit 1 fi if ! "${Connection}_down"; then report_error "Failed to bring the network down for profile '$Profile'" exit 1 fi network_status "" if is_interface "$Interface" && interface_is_up "$Interface" && \ ! is_yes "${ForceConnect:-no}"; then report_error "The interface of network profile '$Profile' did not go down" exit 1 fi report_notice "Stopped network profile '$Profile'" } ## Wait for all enabled profiles to come online within a single timeout network_wait_online() { mapfile -t Profiles < <(list_profiles) i=0 timeout_wait "${TIMEOUT_ONLINE:-120}" \ '! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done' } ensure_root netctl + ensure_root netctl + (( EUID == 0 )) # Ensure we are not in a transient directory cd / + cd / if [[ $# -eq 1 && $1 == wait-online ]]; then network_wait_online elif [[ $# -eq 2 && $1 == @(start|stop) ]]; then # Expose the command Command=$1 load_profile "$2" "network_$1" else exit_error "Usage: $0 {start|stop|wait-online} [profile]" fi + [[ 1 -eq 1 ]] + [[ wait-online == wait-online ]] + network_wait_online + mapfile -t Profiles ++ list_profiles ++ find -L /etc/netctl/ -maxdepth 1 -type f -not -name '.*' -not -name '*~' -not -name '* *' -not -name '*.action' -not -name '*.conf' -not -name '*.service' -printf '%f\n' + i=0 + timeout_wait 120 '! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done' + local timeout=120 + (( timeout *= 5 )) + shift + eval '! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done' ! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done +++ sd_call is-enabled enp9s0_dhcp +++ local command=is-enabled +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl is-enabled netctl@enp9s0_dhcp.service ++ [[ enabled == \e\n\a\b\l\e\d ]] +++ sd_status_text enp9s0_dhcp +++ sd_call 'show --property=StatusText --value' enp9s0_dhcp +++ local 'command=show --property=StatusText --value' +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl show --property=StatusText --value netctl@enp9s0_dhcp.service ++ [[ '' != \o\n\l\i\n\e ]] + (( timeout-- > 0 )) + sleep 0.2 + eval '! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done' ! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done +++ sd_call is-enabled enp9s0_dhcp +++ local command=is-enabled +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl is-enabled netctl@enp9s0_dhcp.service ++ [[ enabled == \e\n\a\b\l\e\d ]] +++ sd_status_text enp9s0_dhcp +++ sd_call 'show --property=StatusText --value' enp9s0_dhcp +++ local 'command=show --property=StatusText --value' +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl show --property=StatusText --value netctl@enp9s0_dhcp.service ++ [[ '' != \o\n\l\i\n\e ]] + (( timeout-- > 0 )) + sleep 0.2 + eval '! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done' ! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done +++ sd_call is-enabled enp9s0_dhcp +++ local command=is-enabled +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl is-enabled netctl@enp9s0_dhcp.service ++ [[ enabled == \e\n\a\b\l\e\d ]] +++ sd_status_text enp9s0_dhcp +++ sd_call 'show --property=StatusText --value' enp9s0_dhcp +++ local 'command=show --property=StatusText --value' +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl show --property=StatusText --value netctl@enp9s0_dhcp.service ++ [[ '' != \o\n\l\i\n\e ]] + (( timeout-- > 0 )) + sleep 0.2 + eval '! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done' ! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done +++ sd_call is-enabled enp9s0_dhcp +++ local command=is-enabled +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl is-enabled netctl@enp9s0_dhcp.service ++ [[ enabled == \e\n\a\b\l\e\d ]] +++ sd_status_text enp9s0_dhcp +++ sd_call 'show --property=StatusText --value' enp9s0_dhcp +++ local 'command=show --property=StatusText --value' +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl show --property=StatusText --value netctl@enp9s0_dhcp.service ++ [[ '' != \o\n\l\i\n\e ]] + (( timeout-- > 0 )) + sleep 0.2 + eval '! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done' ! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done +++ sd_call is-enabled enp9s0_dhcp +++ local command=is-enabled +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl is-enabled netctl@enp9s0_dhcp.service ++ [[ enabled == \e\n\a\b\l\e\d ]] +++ sd_status_text enp9s0_dhcp +++ sd_call 'show --property=StatusText --value' enp9s0_dhcp +++ local 'command=show --property=StatusText --value' +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl show --property=StatusText --value netctl@enp9s0_dhcp.service ++ [[ '' != \o\n\l\i\n\e ]] + (( timeout-- > 0 )) + sleep 0.2 + eval '! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done' ! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" && $(sd_status_text "${Profiles[i]}") != "online" ]]; do (( ++i < ${#Profiles[@]} )) || return 0; done +++ sd_call is-enabled enp9s0_dhcp +++ local command=is-enabled +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl is-enabled netctl@enp9s0_dhcp.service ++ [[ enabled == \e\n\a\b\l\e\d ]] +++ sd_status_text enp9s0_dhcp +++ sd_call 'show --property=StatusText --value' enp9s0_dhcp +++ local 'command=show --property=StatusText --value' +++ shift ++++ systemd-escape --template=netctl@.service enp9s0_dhcp +++ systemctl show --property=StatusText --value netctl@enp9s0_dhcp.service ++ [[ '' != \o\n\l\i\n\e ]] + (( timeout-- > 0 )) + sleep 0.2