From 8f6e396f8ee47d52faac67922d0ddf6fdfbabecd Mon Sep 17 00:00:00 2001 From: Sebastian Wicki Date: Sun, 21 Jul 2013 17:05:03 +0200 Subject: Rewrite of netctl-auto This adds a command line interface to the netctl-auto script to allow users more control over the automatic profile selection. --- src/netctl-auto | 268 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 228 insertions(+), 40 deletions(-) (limited to 'src/netctl-auto') diff --git a/src/netctl-auto b/src/netctl-auto index ea692ca..429fa3f 100755 --- a/src/netctl-auto +++ b/src/netctl-auto @@ -4,71 +4,259 @@ . "$SUBR_DIR/wpa" . "$SUBR_DIR/rfkill" -: ${ACTIOND:=wpa_actiond -p /run/wpa_supplicant} -: ${ACTION_SCRIPT:=$SUBR_DIR/auto.action} - -if [[ $# != 2 || $1 != @(start|stop) ]]; then - exit_error "Usage: netctl-auto [start|stop] " -fi - -STARTSTOP=$1 -INTERFACE=$2 -PIDFILE="$STATE_DIR/wpa_actiond_$INTERFACE.pid" -shift 2 - -case $STARTSTOP in - start) - if wpa_is_active "$INTERFACE"; then - exit_error "The interface ($INTERFACE) is already in use" +usage() { + cat << END +Usage: netctl-auto {COMMAND} ... + [--help|--version] + +Commands: + list List available profiles (active='*', disabled='!') + current List currently active profiles + switch-to [PROFILE] Switch to a profile, enable it if necessary + enable [PROFILE] Enable a profile for automatic selection + disable [PROFILE] Disable a profile temporarily for automatic selection + enable-all Enable all profiles for automatic selection + disable-all Disable all profiles temporarily for automatic selection +END +} + +## Print a list of interfaces for which wpa_actiond is active +list_actiond_interfaces() { + find "$STATE_DIR" -maxdepth 1 -type f -name 'wpa_actiond_*.pid' \ + -exec basename -s ".pid" -a {} + | cut -d'_' -f3- +} + +## List all profiles available to the WPA supplicant +## Output format: INTERFACE ID FLAG PROFILE.. +## INTERFACE network interface of the profile +## ID wpa_supplicant numerical network id +## FLAG 'e'=enabled, 'd'=disabled, 'a'=active +## PROFILE.. profile name, may contain spaces +list_wpa_profiles() { + local interface + for interface in $(list_actiond_interfaces); do + local id ssid bssid flags + while IFS=$'\t' read -r id ssid bssid flags; do + local flag="e" + if [[ "$flags" =~ \[CURRENT\] ]]; then + flag="a" + elif [[ "$flags" =~ \[DISABLED\] ]]; then + flag="d" + fi + + local profile=$(wpa_call $interface get_network $id id_str) + profile=$(wpa_unquote "$profile") + + echo $interface $id $flag "$profile" + done < <(wpa_call $interface list_networks | tail -n+2) + done +} + +## Get WPA supplicant network id and interface for the given profile +## Output format: INTERFACE ID +# $1: profile name +get_wpa_network_id() { + local interface id flag profile + while read -r interface id flag profile; do + if [[ "$1" == "$profile" ]]; then + echo $interface $id + return 0 + fi + done < <(list_wpa_profiles) + + report_error "Profile '$1' does not exist or is not available" >&2 + return 1 +} + +## Enable or disable profiles in WPA supplicant +# $1: profile action: "enable", "disable", "enable-all" or "disable-all" +# $2: profile name if action is "enable" or "disable" +profile_enable_disable() { + local action=$1 profile="$2" + local id interfaces wpa_cmd + + if [ -n "$profile" ]; then + read -r interfaces id < <(get_wpa_network_id "$profile") || return 1 + else + interfaces=$(list_actiond_interfaces) + fi + + case $action in + enable) + wpa_cmd=(enable_network $id);; + disable) + wpa_cmd=(disable_network $id);; + enable-all) + wpa_cmd=(enable_network all);; + disable-all) + wpa_cmd=(disable_network all);; + *) + return 1; + esac + + local interface + for interface in $interfaces; do + wpa_call $interface ${wpa_cmd[@]} >/dev/null + if [[ "${wpa_cmd[0]}" == "enable_network" ]]; then + wpa_call $interface reassociate >/dev/null + fi + done +} + +## Select profile in WPA supplicant, but preserve state of all other networks +# $1: profile name +switch_to() { + local profile="$1" + local id interface timeout + + # Load profile interface, WPA network id and timeout + read -r interface id < <(get_wpa_network_id "$profile") || return 1 + timeout=$(. "$PROFILE_DIR/$profile" >/dev/null; echo ${TimeoutWPA:=15}) + + # List of enabled networks + local enabled_networks=$(wpa_call $interface list_networks | tail -n+2 | \ + cut -f 1,4 | fgrep -v '[DISABLED]' | cut -f 1 | tr "\n" ' ') + + reenable_networks() { + for network in $enabled_networks; do + wpa_call $interface enable_network $network >/dev/null + done + + if [[ $(wpa_get_state $interface) != "COMPLETED" ]]; then + if ! in_array $id $enabled_networks; then + wpa_call $interface disable_network $id >/dev/null + fi + fi + } + + # Reenable networks in case user aborts + trap "reenable_networks; exit 1" SIGINT SIGTERM + + # select_network will disable all other networks on that interface + wpa_call $interface select_network $id >/dev/null + if ! wpa_wait_until_state $timeout $interface "COMPLETED"; then + report_error "WPA association/authentication failed for interface '$interface'" fi - if [[ -x "$PROFILE_DIR/interfaces/$INTERFACE" ]]; then - source "$PROFILE_DIR/interfaces/$INTERFACE" + + reenable_networks +} + +## List currently active profiles +current() { + local interface + for interface in $(list_actiond_interfaces); do + local state=$(wpa_get_state $interface) + if [[ "$state" == "COMPLETED" ]]; then + wpa_call $interface status | grep '^id_str=' | cut -d'=' -f2- + fi + done +} + +## List all available profiles and their status +list() { + local interface id flag profile + while read -r interface id flag profile; do + echo "$(echo $flag | tr 'aed' '* !')" "$profile" + done < <(list_wpa_profiles) +} + +## Start and generate config file for the WPA supplicant, start wpa_actiond +# $1: interface +start() { + local interface=$1 + local pidfile="$STATE_DIR/wpa_actiond_$1.pid" + + if wpa_is_active "$interface"; then + exit_error "The interface ($interface) is already in use" + fi + if [[ -x "$PROFILE_DIR/interfaces/$interface" ]]; then + source "$PROFILE_DIR/interfaces/$interface" fi if [[ $RFKill ]]; then - enable_rf "$INTERFACE" "$RFKill" || exit 1 + enable_rf "$interface" "$RFKill" || return 1 fi - if ! WPA_CONF=$(wpa_make_config_file "$INTERFACE"); then - exit_error "Could not create the configuration file for interface '$INTERFACE'" + + local wpa_conf + if ! wpa_conf=$(wpa_make_config_file "$interface"); then + exit_error "Could not create the configuration file for interface '$interface'" fi + + local profile list_profiles | while read -r profile; do report_debug "Examining profile '$profile'" ( source "$PROFILE_DIR/$profile" - [[ $Interface == "$INTERFACE" ]] || continue + [[ $Interface == "$interface" ]] || continue is_yes "${ExcludeAuto:-no}" && exit 1 [[ $Connection != "wireless" ]] && exit 1 : ${Security:=none} # Exclude wpa-config, the wpa_conf is 'complete' and doesn't fit in this scheme [[ $Security == "wpa-config" ]] && exit 1 - printf "%s\n" "network={" "$(wpa_make_config_block)" "id_str=\"$profile\"" "}" >> "$WPA_CONF" + printf "%s\n" "network={" "$(wpa_make_config_block)" "id_str=\"$profile\"" "}" >> "$wpa_conf" report_notice "Included profile '$profile'" ) done - # Start the WPA supplicant + # Start the WPA supplicant and wpa_actiond : ${WPADriver:=nl80211,wext} WPAOptions+=" -W" - if wpa_start "$INTERFACE" "$WPADriver" "$WPA_CONF"; then - if $ACTIOND -i "$INTERFACE" -P "$PIDFILE" -a "$ACTION_SCRIPT" "$@"; then - exit 0 + if wpa_start "$interface" "$WPADriver" "$wpa_conf"; then + if $ACTIOND -i "$interface" -P "$pidfile" -a "$ACTION_SCRIPT"; then + return 0 fi - wpa_stop "$INTERFACE" + wpa_stop "$interface" fi - exit 1 - ;; - stop) - kill "$(< "$PIDFILE")" - if [[ -x "$PROFILE_DIR/interfaces/$INTERFACE" ]]; then - source "$PROFILE_DIR/interfaces/$INTERFACE" + return 1 +} + +## Stop wpa_supplicant and wpa_actiond +# $1: interface +stop() { + local interface=$1 + local pidfile="$STATE_DIR/wpa_actiond_$1.pid" + + [[ -e "$pidfile" ]] && kill "$(< "$pidfile")" + if [[ -x "$PROFILE_DIR/interfaces/$interface" ]]; then + source "$PROFILE_DIR/interfaces/$interface" fi - timeout_wait 1 '! wpa_is_active "$INTERFACE"' || wpa_stop "$INTERFACE" - ip link set dev "$INTERFACE" down - [[ $RFKill ]] && disable_rf "$INTERFACE" "$RFKill" - exit 0 - ;; -esac + timeout_wait 1 '! wpa_is_active "$interface"' || wpa_stop "$interface" + ip link set dev "$interface" down + [[ $RFKill ]] && disable_rf "$interface" "$RFKill" + return 0 +} +case $# in + 1) + case $1 in + --version) + report_notice "netctl version $NETCTL_VERSION";; + --help) + usage;; + list|current) + "$1";; + enable-all|disable-all) + profile_enable_disable $1;; + *) + exit_error "$(usage)";; + esac;; + 2) + case $1 in + enable|disable) + profile_enable_disable $1 "$2";; + switch-to) + switch_to "$2";; + start|stop) + ensure_root "$(basename "$0")" + : ${ACTIOND:=wpa_actiond -p /run/wpa_supplicant} + : ${ACTION_SCRIPT:=$SUBR_DIR/auto.action} + $1 "$2";; + *) + exit_error "$(usage)";; + esac;; + *) + exit_error "$(usage)";; +esac # vim: ft=sh ts=4 et sw=4: -- cgit v1.2.3-24-g4f1b From 6bd3fb709988d931e92ddaa5c493be6906a07890 Mon Sep 17 00:00:00 2001 From: Sebastian Wicki Date: Tue, 23 Jul 2013 11:53:01 +0200 Subject: More consistent quoting in netctl-auto --- src/netctl-auto | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'src/netctl-auto') diff --git a/src/netctl-auto b/src/netctl-auto index 429fa3f..a7e1250 100755 --- a/src/netctl-auto +++ b/src/netctl-auto @@ -44,11 +44,11 @@ list_wpa_profiles() { flag="d" fi - local profile=$(wpa_call $interface get_network $id id_str) + local profile=$(wpa_call "$interface" get_network "$id" id_str) profile=$(wpa_unquote "$profile") - echo $interface $id $flag "$profile" - done < <(wpa_call $interface list_networks | tail -n+2) + echo "$interface" "$id" "$flag" "$profile" + done < <(wpa_call "$interface" list_networks | tail -n+2) done } @@ -59,7 +59,7 @@ get_wpa_network_id() { local interface id flag profile while read -r interface id flag profile; do if [[ "$1" == "$profile" ]]; then - echo $interface $id + echo "$interface" "$id" return 0 fi done < <(list_wpa_profiles) @@ -72,7 +72,7 @@ get_wpa_network_id() { # $1: profile action: "enable", "disable", "enable-all" or "disable-all" # $2: profile name if action is "enable" or "disable" profile_enable_disable() { - local action=$1 profile="$2" + local action="$1" profile="$2" local id interfaces wpa_cmd if [ -n "$profile" ]; then @@ -83,9 +83,9 @@ profile_enable_disable() { case $action in enable) - wpa_cmd=(enable_network $id);; + wpa_cmd=(enable_network "$id");; disable) - wpa_cmd=(disable_network $id);; + wpa_cmd=(disable_network "$id");; enable-all) wpa_cmd=(enable_network all);; disable-all) @@ -96,9 +96,9 @@ profile_enable_disable() { local interface for interface in $interfaces; do - wpa_call $interface ${wpa_cmd[@]} >/dev/null + wpa_call "$interface" "${wpa_cmd[@]}" >/dev/null if [[ "${wpa_cmd[0]}" == "enable_network" ]]; then - wpa_call $interface reassociate >/dev/null + wpa_call "$interface" reassociate >/dev/null fi done } @@ -114,17 +114,17 @@ switch_to() { timeout=$(. "$PROFILE_DIR/$profile" >/dev/null; echo ${TimeoutWPA:=15}) # List of enabled networks - local enabled_networks=$(wpa_call $interface list_networks | tail -n+2 | \ - cut -f 1,4 | fgrep -v '[DISABLED]' | cut -f 1 | tr "\n" ' ') + local enabled_networks=$(wpa_call "$interface" list_networks | tail -n+2 | \ + cut -f 1,4 | fgrep -v "[DISABLED]" | cut -f 1 | tr "\n" ' ') reenable_networks() { for network in $enabled_networks; do - wpa_call $interface enable_network $network >/dev/null + wpa_call "$interface" enable_network "$network" >/dev/null done - if [[ $(wpa_get_state $interface) != "COMPLETED" ]]; then - if ! in_array $id $enabled_networks; then - wpa_call $interface disable_network $id >/dev/null + if [[ $(wpa_get_state "$interface") != "COMPLETED" ]]; then + if ! in_array "$id" $enabled_networks; then + wpa_call "$interface" disable_network "$id" >/dev/null fi fi } @@ -133,8 +133,8 @@ switch_to() { trap "reenable_networks; exit 1" SIGINT SIGTERM # select_network will disable all other networks on that interface - wpa_call $interface select_network $id >/dev/null - if ! wpa_wait_until_state $timeout $interface "COMPLETED"; then + wpa_call "$interface" select_network "$id" >/dev/null + if ! wpa_wait_until_state "$timeout" "$interface" "COMPLETED"; then report_error "WPA association/authentication failed for interface '$interface'" fi @@ -145,9 +145,9 @@ switch_to() { current() { local interface for interface in $(list_actiond_interfaces); do - local state=$(wpa_get_state $interface) + local state=$(wpa_get_state "$interface") if [[ "$state" == "COMPLETED" ]]; then - wpa_call $interface status | grep '^id_str=' | cut -d'=' -f2- + wpa_call "$interface" status | grep '^id_str=' | cut -d'=' -f2- fi done } @@ -163,7 +163,7 @@ list() { ## Start and generate config file for the WPA supplicant, start wpa_actiond # $1: interface start() { - local interface=$1 + local interface="$1" local pidfile="$STATE_DIR/wpa_actiond_$1.pid" if wpa_is_active "$interface"; then @@ -214,7 +214,7 @@ start() { ## Stop wpa_supplicant and wpa_actiond # $1: interface stop() { - local interface=$1 + local interface="$1" local pidfile="$STATE_DIR/wpa_actiond_$1.pid" [[ -e "$pidfile" ]] && kill "$(< "$pidfile")" @@ -237,21 +237,21 @@ case $# in list|current) "$1";; enable-all|disable-all) - profile_enable_disable $1;; + profile_enable_disable "$1";; *) exit_error "$(usage)";; esac;; 2) case $1 in enable|disable) - profile_enable_disable $1 "$2";; + profile_enable_disable "$1" "$2";; switch-to) switch_to "$2";; start|stop) ensure_root "$(basename "$0")" : ${ACTIOND:=wpa_actiond -p /run/wpa_supplicant} : ${ACTION_SCRIPT:=$SUBR_DIR/auto.action} - $1 "$2";; + "$1" "$2";; *) exit_error "$(usage)";; esac;; -- cgit v1.2.3-24-g4f1b From 5b0c7cae3e82c96058605941273e33632ec95094 Mon Sep 17 00:00:00 2001 From: Sebastian Wicki Date: Tue, 23 Jul 2013 21:44:40 +0200 Subject: Rework some lines in netctl-auto --- src/netctl-auto | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/netctl-auto') diff --git a/src/netctl-auto b/src/netctl-auto index a7e1250..7519325 100755 --- a/src/netctl-auto +++ b/src/netctl-auto @@ -1,9 +1,13 @@ #! /bin/bash +# Contributed by: Sebastian Wicki . /usr/lib/network/globals . "$SUBR_DIR/wpa" . "$SUBR_DIR/rfkill" +: ${ACTIOND:=wpa_actiond -p /run/wpa_supplicant} +: ${ACTION_SCRIPT:=$SUBR_DIR/auto.action} + usage() { cat << END Usage: netctl-auto {COMMAND} ... @@ -115,7 +119,7 @@ switch_to() { # List of enabled networks local enabled_networks=$(wpa_call "$interface" list_networks | tail -n+2 | \ - cut -f 1,4 | fgrep -v "[DISABLED]" | cut -f 1 | tr "\n" ' ') + cut -f 1,4 | grep -Fv "[DISABLED]" | cut -f 1 | tr "\n" ' ') reenable_networks() { for network in $enabled_networks; do @@ -147,7 +151,7 @@ current() { for interface in $(list_actiond_interfaces); do local state=$(wpa_get_state "$interface") if [[ "$state" == "COMPLETED" ]]; then - wpa_call "$interface" status | grep '^id_str=' | cut -d'=' -f2- + wpa_call "$interface" status | sed -n 's/^id_str=//p' fi done } @@ -249,8 +253,6 @@ case $# in switch_to "$2";; start|stop) ensure_root "$(basename "$0")" - : ${ACTIOND:=wpa_actiond -p /run/wpa_supplicant} - : ${ACTION_SCRIPT:=$SUBR_DIR/auto.action} "$1" "$2";; *) exit_error "$(usage)";; -- cgit v1.2.3-24-g4f1b