diff options
author | Jouke Witteveen <j.witteveen@gmail.com> | 2013-07-25 00:08:09 +0200 |
---|---|---|
committer | Jouke Witteveen <j.witteveen@gmail.com> | 2013-07-25 00:08:09 +0200 |
commit | 906c6daa8a1ab95b3e169b09a925816ebce4831c (patch) | |
tree | 3074e2aefcd98a15f6cf322646a270a8d44c0300 /src/netctl-auto | |
parent | 20c66d6499f22adad6c1dae854a627f061c5fbcb (diff) | |
parent | 7d081a227528201ed65b8bab9ec07ccd8dfc1162 (diff) | |
download | netctl-906c6daa8a1ab95b3e169b09a925816ebce4831c.tar.gz netctl-906c6daa8a1ab95b3e169b09a925816ebce4831c.tar.xz |
Merge branch 'master' of git://github.com/gandro/netctl into gandro-master
Diffstat (limited to 'src/netctl-auto')
-rwxr-xr-x | src/netctl-auto | 260 |
1 files changed, 225 insertions, 35 deletions
diff --git a/src/netctl-auto b/src/netctl-auto index ea692ca..7519325 100755 --- a/src/netctl-auto +++ b/src/netctl-auto @@ -1,4 +1,5 @@ #! /bin/bash +# Contributed by: Sebastian Wicki <gandro@gmx.net> . /usr/lib/network/globals . "$SUBR_DIR/wpa" @@ -7,68 +8,257 @@ : ${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] <interface>" -fi +usage() { + cat << END +Usage: netctl-auto {COMMAND} ... + [--help|--version] -STARTSTOP=$1 -INTERFACE=$2 -PIDFILE="$STATE_DIR/wpa_actiond_$INTERFACE.pid" -shift 2 +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 +} -case $STARTSTOP in - start) - if wpa_is_active "$INTERFACE"; then - exit_error "The interface ($INTERFACE) is already in use" +## 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 | grep -Fv "[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 + + 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 | sed -n 's/^id_str=//p' + 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" + 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")" + "$1" "$2";; + *) + exit_error "$(usage)";; + esac;; + *) + exit_error "$(usage)";; +esac # vim: ft=sh ts=4 et sw=4: |