#! /bin/bash . /usr/lib/network/globals usage() { cat << END Usage: netctl {COMMAND} [PROFILE] [--help|--version] Commands: list List available profiles store Save which profiles are active restore Load saved profiles stop-all Stops all profiles start [PROFILE] Start a profile stop [PROFILE] Stop a profile restart [PROFILE] Restart a profile switch-to [PROFILE] Switch to a profile status [PROFILE] Show runtime status of a profile enable [PROFILE] Enable the systemd unit for a profile disable [PROFILE] Disable the systemd unit for a profile reenable [PROFILE] Reenable the systemd unit for a profile is-enabled [PROFILE] Check whether a profile is enabled edit [PROFILE] Edit a profile END } sd_booted() { [[ -d /run/systemd/system ]] } sd_escape() { local IFS='' # Prevent a recursion loop on backspaces set -- "${@//\\/\\x5c}" while [[ "$*" =~ [^[:alnum:].:_/\\] ]]; do set -- "${@//$BASH_REMATCH/$(printf '\\x%x' "'$BASH_REMATCH")}" done printf '%s\n' "${@//\//-}" } # Wrapper around systemctl to convert profile names to unit names sd_call() { local command=$1 shift set -- $(sd_escape "$@") systemctl $command $(printf 'netctl@%s.service\n' "$@") } list() { local indicators=( '*' ' ' ) list_profiles | while read -r Profile; do sd_call "is-active --quiet" "$Profile" &> /dev/null # Make the return value boolean printf '%s %s\n' "${indicators[!! $?]}" "$Profile" done } store() { mkdir -p "$(dirname "$STATE_FILE")" list_profiles | while read -r Profile; do if sd_call "is-active --quiet" "$Profile" &> /dev/null; then printf "%s\n" "$Profile" fi done > "$STATE_FILE" } restore() { if [[ ! -r $STATE_FILE ]]; then exit_error "Could not read state file '$STATE_FILE'" elif [[ ! -s $STATE_FILE ]]; then report_debug "No profiles to restore in state file '$STATE_FILE'" else mapfile -t Profiles < "$STATE_FILE" do_debug sd_call start "${Profiles[@]}" fi } stop_all() { # We cannot pipe to mapfile, as the end of a pipe is inside a subshell mapfile -t Profiles < <(list_profiles) [[ $Profiles ]] && do_debug sd_call stop "${Profiles[@]}" 2> \ >(grep -Fv "not loaded" >&2) } switch_to() { cd "$PROFILE_DIR" if [[ ! -r $1 ]]; then exit_error "Profile '$1' does not exist or is not readable" fi # We assume interface names are not quoted # Using read removes leading whitespace read InterfaceLine < \ <(grep -om1 "^[[:space:]]*Interface=[[:alnum:]:._-]\+" "$1") if [[ -z $InterfaceLine ]]; then exit_error "Profile '$1' does not specify an interface" fi mapfile -t AllProfiles < <(list_profiles) mapfile -t Profiles < <(grep -Fl "$InterfaceLine" "${AllProfiles[@]}") [[ $Profiles ]] && do_debug sd_call stop "${Profiles[@]}" 2> \ >(grep -Fv "not loaded" >&2) do_debug sd_call start "$1" } unit_enable() { local unit="@systemdsystemconfdir@/netctl@$(sd_escape "$1").service" if [[ -e $unit ]]; then report_error "A unit file for profile '$1' already exists" return 1 fi load_profile "$1" do_readable touch "$unit" echo ".include @systemdsystemunitdir@/netctl@.service" > "$unit" echo -e "\n[Unit]" >> "$unit" [[ -n $Description ]] && echo "Description=$Description" >> "$unit" declare -p BindsToInterfaces &> /dev/null || BindsToInterfaces=$Interface if (( ${#BindsToInterfaces[@]} )); then : ${InterfaceRoot=sys/subsystem/net/devices/} printf "BindsTo=$(sd_escape "$InterfaceRoot")%s.device\n" \ $(sd_escape "${BindsToInterfaces[@]}") >> "$unit" printf "After=$(sd_escape "$InterfaceRoot")%s.device\n" \ $(sd_escape "${BindsToInterfaces[@]}") >> "$unit" fi if (( ${#After[@]} )); then printf 'After="netctl@%s.service"\n' \ $(sd_escape "${After[@]}") >> "$unit" fi mkdir -p "@systemdsystemconfdir@/multi-user.target.wants" echo "ln -s '$unit' '${unit/system\//system/multi-user.target.wants/}'" ln -s "$unit" "${unit/system\//system/multi-user.target.wants/}" } unit_disable() { local unit="@systemdsystemconfdir@/netctl@$(sd_escape "$1").service" if sd_call "is-enabled --quiet" "$1" &> /dev/null; then sd_call disable "$1" fi if [[ ! -f $unit ]]; then report_error "No regular unit file found for profile '$1'" return 1 fi do_debug rm "$unit" } case $# in 1) case $1 in --version) report_notice "netctl version $NETCTL_VERSION";; --help) usage;; list) list;; store|restore) ensure_root "$(basename "$0")" "$1";; stop-all) stop_all;; *) exit_error "$(usage)";; esac;; 2) case $1 in start|stop|restart|status|is-enabled) sd_call "$1" "$2";; switch-to) ensure_root "$(basename "$0")" switch_to "$2";; enable|disable) ensure_root "$(basename "$0")" "unit_$1" "$2" if sd_booted; then systemctl daemon-reload fi;; reenable) ensure_root "$(basename "$0")" unit_disable "$2" unit_enable "$2" if sd_booted; then systemctl daemon-reload fi;; edit) exec ${EDITOR:-nano} "$PROFILE_DIR/$2";; *) exit_error "$(usage)";; esac;; *) exit_error "$(usage)";; esac # vim: ft=sh ts=4 et sw=4: