summaryrefslogtreecommitdiffstats
path: root/src/netctl.in
diff options
context:
space:
mode:
Diffstat (limited to 'src/netctl.in')
-rw-r--r--src/netctl.in181
1 files changed, 181 insertions, 0 deletions
diff --git a/src/netctl.in b/src/netctl.in
new file mode 100644
index 0000000..2d04d28
--- /dev/null
+++ b/src/netctl.in
@@ -0,0 +1,181 @@
+#! /bin/bash
+
+. /usr/lib/network/globals
+
+NETCTL_VERSION=notpackaged
+
+
+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
+END
+}
+
+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 variable boolean
+ [[ $? -eq 0 ]]; 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"
+ echo ".include @systemdsystemunitdir@/netctl@.service" > "$unit"
+ echo -e "\n[Unit]" >> "$unit"
+ [[ -n $Description ]] && echo "Description=$Description" >> "$unit"
+ if [[ -n ${BindsToInterfaces=$Interface} ]]; 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 [[ -n $After ]]; then
+ printf 'After="netctl@%s.service"\n' \
+ $(sd_escape "${After[@]}") >> "$unit"
+ fi
+ echo "ln -s '$unit' '${unit/system\//system/multi-user.target.wants/}'"
+ ln -s "$unit" "${unit/system\//system/multi-user.target.wants/}"
+ systemctl daemon-reload
+}
+
+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"
+ systemctl daemon-reload
+}
+
+
+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)
+ sd_call "$1" "$2";;
+ switch-to)
+ ensure_root "$(basename "$0")"
+ switch_to "$2";;
+ enable|disable)
+ ensure_root "$(basename "$0")"
+ "unit_$1" "$2";;
+ reenable)
+ ensure_root "$(basename "$0")"
+ unit_disable "$2"
+ unit_enable "$2";;
+ *)
+ exit_error "$(usage)";;
+ esac;;
+ *)
+ exit_error "$(usage)";;
+esac
+
+
+# vim: ft=sh ts=4 et sw=4: