NETCTL_VERSION=1.29 PROFILE_DIR="/etc/netctl" SUBR_DIR="/usr/lib/netctl" STATE_DIR="/run/netctl" STATE_FILE="${NETCTL_STATE_FILE:-/var/lib/netctl/netctl.state}" ### Logging/Error reporting report_notice() { printf '%s\n' "$*" } report_error() { local prefix="<3>" suffix="" if [[ -t 2 ]]; then prefix=$(tput bold; tput setaf 1) suffix=$(tput sgr0) fi printf '%s%s%s\n' "$prefix" "$*" "$suffix" >&2 } report_debug() { is_yes "${NETCTL_DEBUG:-no}" && printf 'DEBUG: %s\n' "$*" >&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 : # vim: ft=sh ts=4 et sw=4: