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:
