[netctl] More uniform profile loading and filtering

Message ID YJU6LQEQUe/st9TD@Mindship-03
State New
Headers show
Series [netctl] More uniform profile loading and filtering | expand

Commit Message

Jouke Witteveen May 7, 2021, 1:01 p.m. UTC
Hooks and interface scripts are now sourced more systematically.
Settings of wireless profiles that depend on hooks are now picked up
correctly by netctl-auto.

Suggested by: Vitor Sakaguti <vis@tutao.de>
---

Inspired by a report on GitHub:
https://github.com/joukewitteveen/netctl/issues/185

 src/ifplugd.action  |  5 ++--
 src/lib/auto.action |  3 ---
 src/lib/globals     | 56 ++++++++++++++++++++++++++++++++++-----------
 src/lib/interface   |  9 --------
 src/lib/network     |  1 -
 src/netctl-auto     |  6 ++---
 src/netctl.in       | 11 +++------
 7 files changed, 50 insertions(+), 41 deletions(-)

Patch

diff --git a/src/ifplugd.action b/src/ifplugd.action
index c1f0e7d..0cebb34 100755
--- a/src/ifplugd.action
+++ b/src/ifplugd.action
@@ -14,11 +14,10 @@  case "$2" in
             printf '%s' "$profile" > "$PROFILE_FILE"
             exit 0
         fi
-    done < <(list_profiles | while IFS= read -r profile; do
+    done < <(filter_profiles "$1" ethernet | while IFS= read -r profile; do
         report_debug "Examining profile '$profile'"
         (
-          source "$PROFILE_DIR/$profile" > /dev/null
-          [[ $Interface == "$1" && $Connection == "ethernet" ]] || exit
+          load_profile "$profile" > /dev/null
           # Prioritize dhcp based profiles as they can outright fail, whereas
           # it is difficult to tell if a profile with a static address fails
           if [[ $IP == "dhcp" || $IP6 == dhcp* ]]; then
diff --git a/src/lib/auto.action b/src/lib/auto.action
index 6c13651..717d205 100755
--- a/src/lib/auto.action
+++ b/src/lib/auto.action
@@ -1,7 +1,6 @@ 
 #! /bin/bash
 
 . /usr/lib/netctl/globals
-. "$SUBR_DIR/interface"
 . "$SUBR_DIR/ip"
 
 export INTERFACE="$1"
@@ -11,7 +10,6 @@  PROFILE_FILE="$STATE_DIR/netctl-auto-$INTERFACE.profile"
 case $ACTION in
   CONNECTED)
     load_profile "$WPA_ID_STR"
-    load_interface_config "$INTERFACE"
     DhcpcdOptions+=" -K -L"
     ip_set || exit 1
     mkdir -p "$(dirname "$PROFILE_FILE")"
@@ -26,7 +24,6 @@  case $ACTION in
   DISCONNECTED)
     if [[ -s "$PROFILE_FILE" ]]; then
         load_profile "$(< "$PROFILE_FILE")"
-        load_interface_config "$INTERFACE"
         rm -f "$PROFILE_FILE"
         # Sandbox the eval
         if ! ( do_debug eval "$ExecDownPre" ); then
diff --git a/src/lib/globals b/src/lib/globals
index 32a1802..3d2c50d 100644
--- a/src/lib/globals
+++ b/src/lib/globals
@@ -103,41 +103,71 @@  timeout_wait() {
 
 ### Profile management
 
-## 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'
+## 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)
 }
 
-## Exit if a profile file is not syntactically correct
-# $1: profile name
-verify_profile() {
-    /bin/bash -n "$PROFILE_DIR/$1" || exit 1
+## 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() {
-    local hook
     # 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
-    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 -u)
-    unset hook
+    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" || exit
+          [[ -z $1 || $1 == "$Interface" ]] || exit
+          load_interface_config "$Interface"
+          [[ -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
diff --git a/src/lib/interface b/src/lib/interface
index 1a51c07..8504191 100644
--- a/src/lib/interface
+++ b/src/lib/interface
@@ -1,15 +1,6 @@ 
 ## /usr/lib/netctl/globals needs to be sourced before this file
 
 
-## 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
-}
-
 ## Check if a string represents a network interface
 # $1: potential interface name
 is_interface() {
diff --git a/src/lib/network b/src/lib/network
index b6926e3..cb79b77 100755
--- a/src/lib/network
+++ b/src/lib/network
@@ -88,7 +88,6 @@  elif [[ $# -eq 2 && $1 == @(start|stop) ]]; then
     # Expose the command
     Command=$1
     load_profile "$2"
-    load_interface_config "$Interface"
     "network_$1"
 else
     exit_error "Usage: $0 {start|stop|wait-online} [profile]"
diff --git a/src/netctl-auto b/src/netctl-auto
index ec3f7a2..720949e 100755
--- a/src/netctl-auto
+++ b/src/netctl-auto
@@ -211,12 +211,10 @@  start() {
     # Disable p2p to prevent wpa_supplicant from creating another control interface
     echo "p2p_disabled=1" >> "$WPAConfigFile"
 
-    local profile
-    list_profiles | while IFS= read -r profile; do
+    filter_profiles "$interface" wireless | while IFS= read -r profile; do
         report_debug "Examining profile '$profile'"
         (
-          source "$PROFILE_DIR/$profile"
-          [[ $Interface == "$interface" && $Connection == "wireless" ]] || exit
+          load_profile "$profile"
           is_yes "${ExcludeAuto:-no}" && exit
           # Set default and exclude wpa-config as it does not fit this scheme
           [[ ${Security:=none} != "wpa-config" ]] || exit
diff --git a/src/netctl.in b/src/netctl.in
index 929eb1e..3a96f12 100644
--- a/src/netctl.in
+++ b/src/netctl.in
@@ -71,16 +71,11 @@  stop_all() {
 }
 
 switch_to() {
-    cd "$PROFILE_DIR"
-    # We assume interface names are not quoted
-    # Using read removes leading whitespace
-    read InterfaceLine < \
-      <(grep -om1 '^[[:space:]]*Interface=[[:alnum:]:._-]\+' "$1")
-    if [[ -z $InterfaceLine ]]; then
+    Interface=$(. "$PROFILE_DIR/$1" >/dev/null; printf '%s' "$Interface")
+    if [[ -z $Interface ]]; then
         exit_error "Profile '$1' does not specify an interface"
     fi
-    mapfile -t AllProfiles < <(list_profiles)
-    mapfile -t Profiles < <(grep -Fl "$InterfaceLine" "${AllProfiles[@]}")
+    mapfile -t Profiles < <(filter_profiles "$Interface")
     if (( ${#Profiles[@]} )); then
         do_debug sd_call stop "${Profiles[@]}" 2> >(grep -Fv 'not loaded' >&2)
     fi