diff options
author | James Rayner <james@archlinux.org> | 2009-08-07 16:23:45 +0200 |
---|---|---|
committer | James Rayner <james@archlinux.org> | 2009-08-07 16:23:45 +0200 |
commit | 54e39849f1e60eb043f9d8f0904acf3c79d96a1c (patch) | |
tree | 9c10a01ea0ba58d31b3ab8282ead767da49264d3 | |
parent | f7168ae05837a24060b41511be6e4626cb26828a (diff) | |
download | netctl-54e39849f1e60eb043f9d8f0904acf3c79d96a1c.tar.gz netctl-54e39849f1e60eb043f9d8f0904acf3c79d96a1c.tar.xz |
Patch from Jim Pryor
-rw-r--r-- | Makefile | 10 | ||||
-rwxr-xr-x | contrib/common.hook | 49 | ||||
-rwxr-xr-x | contrib/ifplugd.action | 25 | ||||
-rwxr-xr-x | contrib/logging.hook | 86 | ||||
-rwxr-xr-x | contrib/pm-utils.handler | 68 | ||||
-rw-r--r-- | doc/ethernet-subr.html | 99 | ||||
-rw-r--r-- | doc/ethernet.html | 87 | ||||
-rw-r--r-- | doc/wireless | 7 | ||||
-rw-r--r-- | doc/wireless-dbus | 6 | ||||
-rw-r--r-- | doc/wireless-dbus.html | 53 | ||||
-rw-r--r-- | doc/wireless.html | 63 | ||||
-rw-r--r--[-rwxr-xr-x] | examples/ethernet-iproute | 0 | ||||
-rw-r--r-- | man/netcfg.8 | 22 | ||||
-rw-r--r--[-rwxr-xr-x] | src-wireless/net-auto | 43 | ||||
-rw-r--r--[-rwxr-xr-x] | src-wireless/netcfg-auto-wireless | 139 | ||||
-rw-r--r--[-rwxr-xr-x] | src-wireless/wireless-dbus | 146 | ||||
-rw-r--r-- | src/8021x | 36 | ||||
-rw-r--r-- | src/connections/ethernet | 211 | ||||
-rw-r--r-- | src/connections/ethernet-iproute | 214 | ||||
-rw-r--r-- | src/connections/ppp | 53 | ||||
-rw-r--r-- | src/connections/wireless | 368 | ||||
-rw-r--r-- | src/globals | 118 | ||||
-rw-r--r--[-rwxr-xr-x] | src/net-profiles | 63 | ||||
-rw-r--r--[-rwxr-xr-x] | src/net-rename | 10 | ||||
-rw-r--r-- | src/netcfg | 56 | ||||
-rw-r--r--[-rwxr-xr-x] | src/netcfg-menu | 59 | ||||
-rw-r--r-- | src/network | 460 | ||||
-rw-r--r-- | src/wireless | 141 |
28 files changed, 2165 insertions, 527 deletions
@@ -1,9 +1,10 @@ DESTDIR= -VERSION=2.2.1 +VERSION=2.3.0b1 VPATH = doc install: install -d $(DESTDIR)/usr/lib/network/connections $(DESTDIR)/etc/network.d/examples \ + $(DESTDIR)/etc/network.d/hooks \ $(DESTDIR)/var/run/network/{interfaces,profiles} \ $(DESTDIR)/usr/bin/ $(DESTDIR)/etc/rc.d/ \ $(DESTDIR)/usr/share/man/{man5,man8} @@ -12,7 +13,7 @@ install: install -m644 src/iftab $(DESTDIR)/etc/iftab install -m644 man/*.8 $(DESTDIR)/usr/share/man/man8 # Libs - install -m644 src/{network,wireless,8021x} $(DESTDIR)/usr/lib/network + install -m644 src/{network,wireless_utils,8021x,globals} $(DESTDIR)/usr/lib/network install -m755 src/connections/* ${DESTDIR}/usr/lib/network/connections # 'Binaries' install -m755 src/netcfg $(DESTDIR)/usr/bin/netcfg2 @@ -28,8 +29,9 @@ install-wireless: install -m755 src-wireless/net-auto $(DESTDIR)/etc/rc.d install-docs: docs - install -d $(DESTDIR)/usr/share/doc/netcfg + install -d $(DESTDIR)/usr/share/doc/netcfg/contrib install -m644 doc/*html $(DESTDIR)/usr/share/doc/netcfg/ + install -m644 contrib/* $(DESTDIR)/usr/share/doc/netcfg/contrib/ docs: doc/* for doc in $(?); do \ @@ -39,7 +41,7 @@ docs: doc/* tarball: sed -i "s/NETCFG_VER=.*/NETCFG_VER=$(VERSION)/g" src/netcfg mkdir -p netcfg-$(VERSION) - cp -r src src-wireless examples man Makefile LICENSE README netcfg-$(VERSION) + cp -r src src-wireless examples contrib man Makefile LICENSE README netcfg-$(VERSION) tar -zcvf netcfg-$(VERSION).tar.gz netcfg-$(VERSION) mv netcfg-$(VERSION).tar.gz ../ rm -rf netcfg-$(VERSION) diff --git a/contrib/common.hook b/contrib/common.hook new file mode 100755 index 0000000..bf5f192 --- /dev/null +++ b/contrib/common.hook @@ -0,0 +1,49 @@ +### Sample netcfg hook script showing how to declare shared settings +# +### These functions and variables will be available to all profiles +### (They can be manually overridden by any profile.) +### To install, make this executable and put it in /etc/network.d/hooks + +function RUNDAEMON { + # "RUNDAEMON daemon [ start | stop ]" will run the daemon only if it's enabled in the DAEMONS array + # "RUNDAEMON -f daemon [ start | stop ]" will run it no matter what + local enabled result + if [[ "$1" = "-f" ]]; then + enabled=1 + shift + fi + local daemon="$1" + shift + if [[ ! -x "/etc/rc.d/$daemon" ]]; then + echo "DAEMON $daemon isn't executable." >&2 + return 1 + elif [[ "$enabled" -ne 1 ]]; then + for f in "${DAEMONS[@]}"; do + if [[ "$f" = "$daemon" || "$f" = "@$daemon" ]]; then + enabled=1 + break + fi + done + fi + if [[ "$enabled" -eq 1 ]]; then + "/etc/rc.d/$daemon" "$@" + result=$? + stat_busy "Continuing $PROFILE..." # else we'll get a [DONE] or [FAIL] at the end of a third blank line, after messages from $daemon + return $result + fi + return 1 # $daemon isn't enabled in DAEMON array +} + +# Example of some things you might do in your POST_UP/PRE_DOWN scripts +# (In fact, I couldn't get awesome-client to work on its own in this context, I had to call a wrapper instead that +# sources the file in ~/.dbus/session-bus and then exports DBUS_SESSION_BUS_ADDRESS, and then calls awesome-client.) +# +POST_UP='( sleep 3 && echo "mynetworkfun()" | sudo -Hu me /usr/bin/awesome-client 2>/dev/null) & RUNDAEMON netfs start' +PRE_DOWN='RUNDAEMON netfs stop' +POST_DOWN='( sleep 3 && echo "mynetworkfun()" | sudo -Hu me /usr/bin/awesome-client 2>/dev/null) &' + +# Quirks and other variables defined here will apply to all your profiles... +QUIRKS=() +WPA_GROUP="network" + + diff --git a/contrib/ifplugd.action b/contrib/ifplugd.action new file mode 100755 index 0000000..4700c46 --- /dev/null +++ b/contrib/ifplugd.action @@ -0,0 +1,25 @@ +#!/bin/sh +# +# /etc/ifplugd/ifplugd.action script for Arch Linux +# can replace the one that comes with extra/ifplugd + +# . /etc/rc.conf +# . /etc/rc.d/functions + +case "$2" in + up) + if [ "${1:0:3}" == eth ]; then + /usr/bin/netcfg "myethernet" # replace with name of your desired profile + fi + ;; + down) + /usr/bin/netcfg iface-down "$1" + /sbin/ifconfig "$1" down # note that we'll return 0 even if the netcfg call failed, e.g. because iface was already down + # hence no "failure" messages to syslog + ;; + *) + echo "Wrong arguments" > /dev/stderr + exit 1 + ;; +esac + diff --git a/contrib/logging.hook b/contrib/logging.hook new file mode 100755 index 0000000..27a7c39 --- /dev/null +++ b/contrib/logging.hook @@ -0,0 +1,86 @@ +### Sample netcfg hook script for logging/debugging connections +### To install, make this executable and put it in /etc/network.d/hooks + + +# These functions are loaded after, and override, the ones in /usr/lib/network/globals + +# uncomment one of the following lines +#function report_log { report_syslog "$@"; } +#function report_log { report_stderr "$@"; } + + + + + +# What facility to send log messages to? +NETCFGLOG="${NETCFGLOG-local0}" + +function report_syslog { + if [[ -n "$NETCFGLOG" ]]; then + local caller level="$1" + shift + case "$0" in + net-auto|netcfg-auto-*) caller=net-auto;; + net-profiles) caller=net-profiles;; + net-rename) caller=net-rename;; + *) caller=netcfg;; + esac + logger -p "${NETCFGLOG}.$level" -t "$caller" -- "$*" + fi +} + +function report_stderr { + local level="$1" + shift + echo "netcfg $level: $*" >&2 +} + +function report_err { + report_log err "$*" + printhl "$*" +} + +function report_warn { + report_log warning "$*" + # printhl "$*" +} + +function report_notify { + report_log notice "$*" + # print "$*" >&2 +} + +function report_debug { + report_log debug "$*" +} + +function report_try { + report_log notice "trying $*..." + REPORT_TRYING=1 + stat_busy "$*" +} + +function report_fail { + if [[ -n "$*" ]]; then + report_log err "$*" + if [[ -n "$REPORT_TRYING" ]]; then + stat_append "- $*" + REPORT_TRYING= + stat_fail + else + printhl "$*" + fi + elif [[ -n "$REPORT_TRYING" ]]; then + REPORT_TRYING= + stat_fail + fi +} + +function report_success { + if [[ -n "$*" ]]; then + stat_append "- $*" + fi + report_log notice "${*:-succeeded}" + stat_done +} + diff --git a/contrib/pm-utils.handler b/contrib/pm-utils.handler new file mode 100755 index 0000000..5115b75 --- /dev/null +++ b/contrib/pm-utils.handler @@ -0,0 +1,68 @@ +#!/bin/bash + +. /etc/rc.conf +. /etc/rc.d/functions +. /usr/lib/pm-utils/functions + +[[ -f /usr/lib/network/network ]] || exit $NA +. /usr/lib/network/network + +auto_resume() +{ + all_resume + sleep 2 + # if didn't successfully resume wifi, and running net-auto daemon, then restart the daemon + if [[ -f /var/run/daemons/net-auto && -x /usr/bin/netcfg-auto-wireless ]]; then + if check_iface "$1"; then # ret=0 when iface down and available + /usr/bin/netcfg-auto-wireless "$1" + fi + fi +} + + +case "$1" in + hibernate|suspend_hybrid|suspend) + report_log notice "suspending all interfaces..." + interface_suspend all + ;; + thaw|resume) + if "$CONN_DIR/wireless" query wlan0 enabled; then + report_log notice "resuming all interfaces..." + auto_resume wlan0 + else + report_log notice "resuming all interfaces except wireless..." + all_resume wlan0 + fi + ;; + + # radio_off and radio_on aren't actions that pm-utils will ever send + # but if you're able to detect the toggling of your rfkill switch with acpid, + # you could have your acpid handler call this script as e.g. "SCRIPT radio_off" + radio_off) + report_log notice "suspending wireless interface..." + interface_suspend wlan0 no + "$CONN_DIR/wireless" control wlan0 disable + ifconfig wlan0 down + ;; + radio_on) + report_log notice "resuming wireless interface..." + auto_resume wlan0 + if [ -x /etc/pm/power.d/??wifi ]; then + /usr/bin/on_ac_power # this is in pm-utils + case $? in + 0) # on ac + /etc/pm/power.d/??wifi false + ;; + 1) # on battery + /etc/pm/power.d/??wifi true + ;; + esac + fi + ;; + *) + ;; +esac + +exit $? + + diff --git a/doc/ethernet-subr.html b/doc/ethernet-subr.html new file mode 100644 index 0000000..3d74cf8 --- /dev/null +++ b/doc/ethernet-subr.html @@ -0,0 +1,99 @@ +<h1 id="ethernet-iproute-connection-manual" +>ethernet-iproute Connection manual</h1 +><h2 id="description" +>Description</h2 +><p +>This connection method uses the iproute suite of tools and dhcpcd to gain an IP address.</p +><h2 id="options" +>Options</h2 +><dl +><dt + >INTERFACE (required)</dt + ><dd + >The wireless interface to configure</dd + ><dt + >IP (required)</dt + ><dd + >Can be either 'static' or 'dhcp'. Static requires at least one of ADDR or IPCFG.</dd + ><dt + >ADDR (requires IP of 'static')</dt + ><dd + >A single IP address to configure a static IP. For example:</dd + ><dt + >GATEWAY (requires IP of 'static')</dt + ><dd + >Set specified gateway</dd + ><dt + >IPCFG</dt + ><dd + >Array of arguments to pass to 'ip'. The power of this options is that it allows both simple and complicated routing configurations, within the framework of netcfg.</dd + ></dl +><h3 id="dns" +>DNS</h3 +><dl +><dt + >DNS</dt + ><dd + >Array of DNS nameservers. Simply specify the IP's of each of the DNS nameservers.</dd + ><dt + >SEARCH</dt + ><dd + >"search" line for /etc/resolv.conf</dd + ><dt + >DOMAIN</dt + ><dd + >"domain" line for /etc/resolv.conf</dd + ><dt + >HOSTNAME</dt + ><dd + >Set the system hostname. Ensure any hostname is correctly referenced in /etc/hosts.</dd + ></dl +><h3 id="dhcp" +>DHCP</h3 +><dl +><dt + >DHCP_OPTIONS</dt + ><dd + >String. Any extra arguments to pass to the dhcp client, presently dhcpcd.</dd + ><dt + >DHCP_TIMEOUT</dt + ><dd + >Integer. Maximum time to try for a DHCP IP. Default is 10 seconds.</dd + ></dl +><h3 id="x-authentication" +>802.11x Authentication</h3 +><dl +><dt + >AUTH8021X</dt + ><dd + >Use 802.11x authentication. Enable with 'yes'.</dd + ><dt + >WPA_CONF (required for an AUTH8021X of 'yes' only)</dt + ><dd + >Path to wpa_supplicant configuration. Defaults to '/etc/wpa_supplicant.conf'</dd + ><dt + >WPA_OPTS (optional for an AUTH8021X of 'yes')</dt + ><dd + >Extra arguments for wpa_supplicant not specified otherwise. Any option here must specify wpa_supplicant driver. Defaults to '-Dwired'.</dd + ></dl +><h2 id="examples" +>Examples</h2 +><h3 id="using-addr-and-gateway-to-set-static-ip-and-gateway" +>Using ADDR and GATEWAY to set static IP and gateway</h3 +><pre +><code + >IP="static" +ADDR="192.168.1.23" +GATEWAY="192.168.1.1" +</code + ></pre +><h3 id="using-ipcfg-to-set-a-static-ip-and-gateway-with-custom-dns" +>Using IPCFG to set a static IP and gateway with custom DNS</h3 +><pre +><code + >IP="static" +IPCFG=("addr add dev eth0 192.168.1.23/24 brd +" "route add default via 192.168.1.1") +DNS=("208.67.222.222" "208.67.220.220") +</code + ></pre +> diff --git a/doc/ethernet.html b/doc/ethernet.html new file mode 100644 index 0000000..b2ab91a --- /dev/null +++ b/doc/ethernet.html @@ -0,0 +1,87 @@ +<h1 id="ethernet-manual" +>ethernet Manual</h1 +><h2 id="description" +>Description</h2 +><p +>This connection method uses the unmaintained net-tools (ifconfig) and dhcpcd to gain an IP address.</p +><h2 id="options" +>Options</h2 +><dl +><dt + >INTERFACE (required)</dt + ><dd + >The wireless interface to configure</dd + ><dt + >IP (required)</dt + ><dd + >Can be either 'static' or 'dhcp'. Static requires IFOPTS at least.</dd + ><dt + >IFOPTS</dt + ><dd + >Arguments to pass to 'ifconfig'.</dd + ><dt + >GATEWAY (requires IP of 'static')</dt + ><dd + >Set specified gateway</dd + ></dl +><h3 id="dns" +>DNS</h3 +><dl +><dt + >DNS</dt + ><dd + >Array of DNS nameservers. Simply specify the IP's of each of the DNS nameservers.</dd + ><dt + >DNS1, DNS2 (deprecated)</dt + ><dd + >Specify first and second nameservers.</dd + ><dt + >SEARCH</dt + ><dd + >"search" line for /etc/resolv.conf</dd + ><dt + >DOMAIN</dt + ><dd + >"domain" line for /etc/resolv.conf</dd + ><dt + >HOSTNAME</dt + ><dd + >Set the system hostname. Ensure any hostname is correctly referenced in /etc/hosts.</dd + ></dl +><h3 id="dhcp" +>DHCP</h3 +><dl +><dt + >DHCLIENT</dt + ><dd + >yes/no. Use dhclient instead of dhcpcd. Defaults to no.</dd + ><dt + >DHCP_OPTIONS</dt + ><dd + >String. Any extra arguments to pass to the dhcp client, presently dhcpcd.</dd + ><dt + >DHCP_TIMEOUT</dt + ><dd + >Integer. Maximum time to try for a DHCP IP. Default is 10 seconds.</dd + ></dl +><h3 id="x-authentication" +>802.11x Authentication</h3 +><dl +><dt + >AUTH8021X</dt + ><dd + >Use 802.11x authentication. Enable with 'yes'.</dd + ><dt + >WPA_CONF (required for an AUTH8021X of 'yes' only)</dt + ><dd + >Path to wpa_supplicant configuration. Defaults to '/etc/wpa_supplicant.conf'</dd + ><dt + >WPA_OPTS (optional for an AUTH8021X of 'yes')</dt + ><dd + >Extra arguments for wpa_supplicant not specified otherwise. Any option here must specify wpa_supplicant driver. Defaults to '-Dwired'.</dd + ></dl +><h2 id="examples" +>Examples</h2 +><p +>See /etc/network.d/examples</p +> diff --git a/doc/wireless b/doc/wireless index 20ced91..e1f649e 100644 --- a/doc/wireless +++ b/doc/wireless @@ -17,8 +17,10 @@ SECURITY (required for security of 'wep', 'wpa' or 'wpa-config') : One of 'wpa', 'wep', 'none' or 'wpa-config'. Defaults to 'none' KEY (required for SECURITY of 'wpa' or 'wep' only) : Wireless encryption key. -ESSID (required) +ESSID (this or AP is required) : Name of network to connect to. +AP (this or ESSID is required) +: AP of the network to connect to. TIMEOUT (optional) : Time to wait for association. Defaults to 15 seconds. SCAN (optional) @@ -31,3 +33,6 @@ WPA_CONF (for SECURITY of 'wpa-config' only) : Path to wpa_supplicant configuration. Defaults to '/etc/wpa_supplicant.conf' WPA_OPTS : Extra arguments for wpa_supplicant not specified otherwise. Any option here must specify wpa_supplicant driver. Defaults to '-Dwext'. +WPA_GROUP +: Group that has authority for on-the-fly config files created when SECURITY="wpa" + diff --git a/doc/wireless-dbus b/doc/wireless-dbus index 918256d..bddd565 100644 --- a/doc/wireless-dbus +++ b/doc/wireless-dbus @@ -15,8 +15,10 @@ SECURITY (required) : One of 'wpa', 'wep', 'none' or 'wpa-config' KEY (required for SECURITY of 'wpa' or 'wep' only) : Wireless encryption key. -ESSID (required) -: Name of network to connect to. +ESSID (this or AP is required) +: Name of network to connect to. Note that for "wireless-dbus" profiles this is always a Gnu regexp (as interpreted by "expr"). +AP (this or ESSID is required) +: AP of the network to connect to. TIMEOUT : Time to wait for association. Defaults to 15 seconds. diff --git a/doc/wireless-dbus.html b/doc/wireless-dbus.html new file mode 100644 index 0000000..a2d2aea --- /dev/null +++ b/doc/wireless-dbus.html @@ -0,0 +1,53 @@ +<h1 id="wireless-dbus-connection-manual" +>'wireless-dbus' Connection manual</h1 +><h2 id="description" +>Description</h2 +><p +>This connection method uses wpa_supplicant's dbus interface to configure a wireless network connection.</p +><p +>This connection uses the 'ethernet-iproute' connection after successful association and thus supports all of it's options.</p +><h2 id="options" +>Options</h2 +><dl +><dt + >INTERFACE (required)</dt + ><dd + >The wireless interface to configure</dd + ><dt + >SECURITY (required)</dt + ><dd + >One of 'wpa', 'wep', 'none' or 'wpa-config'</dd + ><dt + >KEY (required for SECURITY of 'wpa' or 'wep' only)</dt + ><dd + >Wireless encryption key.</dd + ><dt + >ESSID (this or AP is required)</dt + ><dd + >Name of network to connect to. Note that for "wireless-dbus" profiles this is always a Gnu regexp (as interpreted by "expr").</dd + ><dt + >AP (this or ESSID is required)</dt + ><dd + >AP of the network to connect to.</dd + ><dt + >TIMEOUT</dt + ><dd + >Time to wait for association. Defaults to 15 seconds.</dd + ></dl +><h3 id="wpa-options" +>WPA options</h3 +><dl +><dt + >WPA_CONF (for SECURITY of 'wpa-config' only)</dt + ><dd + >Path to wpa_supplicant configuration. Defaults to '/etc/wpa_supplicant.conf'</dd + ><dt + >WPA_DRIVER</dt + ><dd + >wpa_supplicant driver to be used. Defaults to 'wext'</dd + ><dt + >WPA_OPTS</dt + ><dd + >Extra arguments for wpa_supplicant not specified otherwise.</dd + ></dl +> diff --git a/doc/wireless.html b/doc/wireless.html new file mode 100644 index 0000000..d78d7b6 --- /dev/null +++ b/doc/wireless.html @@ -0,0 +1,63 @@ +<h1 id="wireless-connection-manual" +>'wireless' Connection manual</h1 +><h2 id="description" +>Description</h2 +><p +>This connection method uses wireless_tools and wpa_supplicant to configure a wireless network connection.</p +><p +>The new 'wireless-dbus' connection method is preferred over this</p +><p +>This connection uses the 'ethernet' connection after successful association and thus supports all of it's options.</p +><h2 id="options" +>Options</h2 +><dl +><dt + >INTERFACE (required)</dt + ><dd + >The wireless interface to configure</dd + ><dt + >SECURITY (required for security of 'wep', 'wpa' or 'wpa-config')</dt + ><dd + >One of 'wpa', 'wep', 'none' or 'wpa-config'. Defaults to 'none'</dd + ><dt + >KEY (required for SECURITY of 'wpa' or 'wep' only)</dt + ><dd + >Wireless encryption key.</dd + ><dt + >ESSID (this or AP is required)</dt + ><dd + >Name of network to connect to.</dd + ><dt + >AP (this or ESSID is required)</dt + ><dd + >AP of the network to connect to.</dd + ><dt + >TIMEOUT (optional)</dt + ><dd + >Time to wait for association. Defaults to 15 seconds.</dd + ><dt + >SCAN (optional)</dt + ><dd + >Scan for a wireless network rather than blindly attempting to connect. Hidden SSID networks do not appear in a scan. Enable with 'yes'. Defaults to 'no'.</dd + ><dt + >IWCONFIG (optional)</dt + ><dd + >Arguments to pass to iwconfig before attempting to configure the connection. For example, BSSID.</dd + ></dl +><h3 id="wpa-options" +>WPA options</h3 +><dl +><dt + >WPA_CONF (for SECURITY of 'wpa-config' only)</dt + ><dd + >Path to wpa_supplicant configuration. Defaults to '/etc/wpa_supplicant.conf'</dd + ><dt + >WPA_OPTS</dt + ><dd + >Extra arguments for wpa_supplicant not specified otherwise. Any option here must specify wpa_supplicant driver. Defaults to '-Dwext'.</dd + ><dt + >WPA_GROUP</dt + ><dd + >Group that has authority for on-the-fly config files created when SECURITY="wpa"</dd + ></dl +> diff --git a/examples/ethernet-iproute b/examples/ethernet-iproute index 6ac20e1..6ac20e1 100755..100644 --- a/examples/ethernet-iproute +++ b/examples/ethernet-iproute diff --git a/man/netcfg.8 b/man/netcfg.8 index f07e12a..312366d 100644 --- a/man/netcfg.8 +++ b/man/netcfg.8 @@ -1,4 +1,4 @@ -.TH NETCFG 8 "MAY 2007" "Arch Linux" "Network Scripts" +.TH NETCFG 8 "AUGUST 2009" "Arch Linux" "Network Scripts" .\" groff -man -Tascii foo.1 .SH NAME netcfg \- start/stop/control network profiles @@ -19,10 +19,13 @@ Start the specified profile, only if it's interface is not currently up. .B \-d, down \fIprofile\fP Stop the specified profile .TP +.B\-r, reconnect \fIprofile\fP +Disconnect and reconnect the specified profile +.TP .B \-i, iface-down \fBinterface\fP Stop the profile up on the specified interface. .TP -.B \-a, all-down +.B \-a, all-down Stop all connected profiles .TP .B all-suspend @@ -31,19 +34,28 @@ Suspend and store the name of all active profiles. .B all-resume Reconnect any profiles that have been suspended. .TP +.B current +Report currently running profiles +.TP +.B \-l, list +List all available profiles +.TP .B \-v, --version Display version information and exit .TP .B \-h, --help Display help message and exit -.SH FILES +.SH FILES .TP .I /usr/lib/network/ Currently installed network profile types. .TP -.I /etc/network.d/examples +.I /etc/network.d/examples Example configurations for this script +.TP +.I /etc/network.d/hooks +See /usr/share/doc/netcfg/contrib for examples. .SH BUGS None, hopefully, but if you do find one of these elusive things, please submit at http://bugs.archlinux.org/ or email one of the authors below. @@ -52,6 +64,6 @@ James Rayner <james@archlinux.org> Others listed in AUTHORS file in source. .SH SEE ALSO -.BR ethernet (5), +.BR ethernet (5), .BR iwconfig (8), .BR wireless (5) diff --git a/src-wireless/net-auto b/src-wireless/net-auto index 8b724a5..98bb91d 100755..100644 --- a/src-wireless/net-auto +++ b/src-wireless/net-auto @@ -2,12 +2,16 @@ . /etc/rc.conf . /etc/rc.d/functions +. /usr/lib/network/globals case "$1" in start) + if ! ck_daemon net-auto; then # JP: check if it's already running + exit_stderr "net-auto has already been started: try \"/etc/rc.d/net-auto restart\"" + fi # Ensure any device renaming has occurred as intended for daemon in "${DAEMONS[@]}"; do - if [ "$daemon" = "${daemon#!}" -a "$daemon" = "net-rename" ]; then + if [[ "$daemon" = "${daemon#!}" && "$daemon" = net-rename ]]; then if ck_daemon net-rename; then /etc/rc.d/net-rename start fi @@ -15,28 +19,43 @@ case "$1" in done # TODO: check if any way of using 'stacks' in bash - for iface in ${AUTO_NETWORKS[@]}; do - if [[ "${iface:0:4}" = "auto" ]]; then - auto=$iface - elif [[ "$auto" ]]; then - /usr/bin/netcfg-$auto $iface - [[ $? -eq 0 ]] && echo $iface >> /var/run/daemons/net-auto + + rm -f "$STATE_DIR/net-auto" + for iface in "${AUTO_NETWORKS[@]}"; do + if [[ "${iface:0:4}" = auto ]]; then + auto="$iface" + elif [[ -n "$auto" ]]; then + if /usr/bin/netcfg-"$auto" "$iface"; then + echo "$iface" >> "$STATE_DIR/net-auto" + add_daemon net-auto # JP: was this forgotten? + fi unset auto + else + true # JP: can AUTO_NETWORKS contain elements other than ...auto-CONNECTION INTERFACE...? + # JP: for example, what do we do with AUTO_NETWORKS=(auto-wireless wlan0 eth0)? + # JP: or with AUTO_NETWORKS=(eth0 auto-wireless wlan0)? fi done ;; stop) - [[ ! -e /var/run/daemons/net-auto ]] && exit 0 - for iface in $(cat /var/run/daemons/net-auto); do - netcfg iface-down $iface + if ck_daemon net-auto; then + exit_stderr "net-auto not running" + fi + + for iface in $(cat "$STATE_DIR/net-auto"); do # JP: note that we may have written "wlan0 wlan1" to net-auto + # e.g. if the user did AUTO_NETWORKS=(auto-wireless "wlan0 wlan1") + # and now we're unpacking and handling wlan0, wlan1 one by one + # that's not necessarily a problem...I'm just calling attention to it + /usr/bin/netcfg iface-down "$iface" done + rm -f "$STATE_DIR/net-auto" rm_daemon net-auto ;; restart) - $0 stop; sleep 1; $0 start + "$0" stop; sleep 1; "$0" start ;; *) - echo "usage: $0 {start|stop|restart}" + exit_stderr "Usage: $0 {start|stop|restart}" esac # vim: set ts=4 et sw=4: diff --git a/src-wireless/netcfg-auto-wireless b/src-wireless/netcfg-auto-wireless index e77eb0e..a17232c 100755..100644 --- a/src-wireless/netcfg-auto-wireless +++ b/src-wireless/netcfg-auto-wireless @@ -1,59 +1,128 @@ #! /bin/bash # Originally contributed by Neuro: http://bbs.archlinux.org/viewtopic.php?pid=278148#p278148 -. /usr/lib/network/network -. /usr/lib/network/wireless . /etc/rc.conf . /etc/rc.d/functions +. /usr/lib/network/network +. /usr/lib/network/wireless_utils # wifi_auto # autoconnect wireless interface # $1 - wireless interface wifi_auto() { - interface=$1; - stat_busy "Scanning for networks" + local interface="$1" connection="$2" + + if [[ ! -f "$CONN_DIR/$connection" ]]; then + exit_err "$connection is not a valid connection." + elif ! "$CONN_DIR/$connection" verify "$interface"; then + exit_err "$interface is not a wireless interface." + fi + + report_try "Scanning for networks" - ifconfig $interface up - networks="$(list_networks $interface)" + local status=$(query_iface "$interface" "$connection") # supply $connection as hint + case "$status" in + disabled) + exit_fail "INTERFACE $interface is disabled." + ;; + external) + exit_fail "INTERFACE $interface was configured by another application." + ;; + "") + #ifconfig "$interface" up 2>/dev/null # $? is 255 when radio-switched-off + "$CONN_DIR/$connection" control "$interface" up + if [[ $? -gt 0 ]]; then + # interface is really disabled + "$CONN_DIR/$connection" control "$interface" disable + exit_fail "INTERFACE $interface is disabled." + fi + ;; + *) + # interface already up and controlled by a profile + ;; + esac - if [[ ! "$networks" ]]; then - stat_append "- No networks available." - stat_fail - exit 1 + networks=$(list_networks "$interface") + + if [[ -z "$networks" ]]; then + # disconnect interface if it wasn't already up + [[ -z "$status" ]] && "$CONN_DIR/$connection" control "$interface" forcedown + exit_fail "No local networks." fi + [[ -z "$status" ]] && "$CONN_DIR/$connection" control "$interface" down # take iface down here so that query_iface doesn't perceive it as externally controlled + # unclear what should happen if $status set to an already-connected profile...? + # Loop through all the found essid's, then find a matching profile. - while read essid; do - for network in $(list_profiles); do - load_profile $network - case "$CONNECTION" in - wireless-old|wireless|wireless-dbus) - if [[ "$essid" = "$ESSID" && "$interface" = "$INTERFACE" ]]; then - found=$network - fi - ;; - esac - done - done < $networks + + local found_profile found_essid + + # JP: add ability to use AP instead of ESSID + # JP: also, make ESSIDs in wireless-dbus CONNECTIONS a regexp instead of a literal + while read ap essid; do + while read network; do + ( + unset CONNECTION INTERFACE AP + load_profile "$network" + case "$CONNECTION" in + wireless-old|wireless|wireless-dbus) + if [[ "$interface" = "$INTERFACE" ]]; then + if [[ "$ap" == "$AP" ]]; then + exit 2 + elif [[ -z "$found_profile" ]]; then + if [[ "$CONNECTION" == wireless-dbus ]]; then + if expr match "$essid" "^$ESSID\$" 1>/dev/null; then + exit 1 + fi + elif [[ "$essid" == "$ESSID" ]]; then + exit 1 + fi + fi + fi + ;; + esac + exit 0 + ) + case $? in + 2) found_profile="$network" + found_essid="$essid" + break 2;; + 1) found_profile="$network" + found_essid="$essid" + ;; + esac + done < <(list_profiles) # avoid subshell + done < "$networks" # avoid subshell; list_networks returns name of a tmp file + # JP: now each line of that file is of format: ap essid... - if [[ "$found" ]]; then - netcfg $found - exit $? + rm -f "$networks" # shouldn't we delete the tmp file? + + if [[ -n "$found_profile" ]]; then + report_success + if profile_up "$found_profile" "$found_essid"; then # we pass literal essid to profile_up as $2 + exit 0 + else + + + + "$CONN_DIR/$connection" control "$interface" forcedown # take down interface? + exit_fail "Couldn't connect profile $found_profile." + fi + else + [[ -z "$status" ]] && "$CONN_DIR/$connection" control "$interface" forcedown + exit_fail "No profiles matched the local networks." fi - stat_append "- No profiles matched the found networks" - stat_fail - exit 1 } if [[ $(id -u) -ne 0 ]]; then - err "This script needs to be run with root priviledges" - exit 1 + exit_stderr "This script should be run as root." fi -if [[ -z $1 ]]; then - err "Please supply an interface to connect" - exit 1 +if [[ -z "$1" ]]; then + exit_stderr "Must supply an interface to connect." fi -wifi_auto $1 - + +SELF=$(basename $0) +wifi_auto "$1" "${SELF#netcfg-auto-}" # we assume this script is named netcfg-auto-CONNECTIONTYPE + diff --git a/src-wireless/wireless-dbus b/src-wireless/wireless-dbus index 6f53a6c..f1c1cd8 100755..100644 --- a/src-wireless/wireless-dbus +++ b/src-wireless/wireless-dbus @@ -4,7 +4,7 @@ import dbus import shlex import subprocess from signal import SIGTERM -from os import kill +from os import kill,path from time import sleep # dbus constants. @@ -21,16 +21,20 @@ def read_config(config): cfg = shlex.split(open(config, "r").read()) options = {} for line in cfg: - (var, delim, value) = line.partition('=') - if delim and var.lstrip()[0] != "#": - options[var] = value + (var, delim, value) = line.partition('=') + if delim: + var = var.lstrip() + if var[0] != '#': + value = value.partition("#")[0].rstrip() + if value[0] == value[-1] and value[0] in ('"',"'"): + value=value[1:-1] + options[var] = value return options def wep_hex2dec(key): if len(key) not in [10, 26]: - print "Bad key" - raise SyntaxError + fail("Profile error: invalid KEY.", report_type="err") x=0 new_key=[] @@ -40,14 +44,24 @@ def wep_hex2dec(key): return new_key -def fail(msg=None): +# JP: connect to our hookable reporting functions in /usr/lib/network/globals + +def report(report_type, *args): + report_handler.stdin.write('report_%s %s\n' % (report_type, ' '.join(map(repr,args)))) + +def fail(msg=None, report_type="fail"): if msg: - print " -", msg - kill(int(open("/var/run/wpa_supplicant.pid").read()),SIGTERM) + report(report_type, msg) + try: + pid = open("/var/run/wpa_supplicant.pid").read() + except IOError: + pass + else: + kill(int(pid),SIGTERM) sys.exit(1) -def start(profile): +def start(profile, essid): # TODO: Add check if it's even a wireless interface # Interface up - probably redundant, should be 'ip' instead. #try: @@ -68,22 +82,25 @@ def start(profile): except KeyError: args.append("-Dwext") - if profile['SECURITY'] == "wpa-config": + security = profile.get('SECURITY','none') + if security == "wpa-config": try: args.append("-c" + profile["WPA_CONF"]) except KeyError: args.append("-c/etc/wpa_supplicant.conf") - elif not profile['SECURITY'] in ['wpa', 'wep', 'none']: - fail("Invalid security chosen") + elif not security in ['wpa', 'wep', 'none']: + fail("Profile error: invalid SECURITY.", report_type="err") # Start wpa_supplicant + report('debug', 'wireless_dbus', 'starting wpa_supplicant') supplicant = subprocess.Popen(args,stderr=subprocess.STDOUT,stdout=subprocess.PIPE) output = supplicant.communicate()[0] if supplicant.returncode not in [255,0]: - print output - fail("Could not start wpa_supplicant") + print >>sys.stderr, output # JP: print to stderr + fail("Couldn't start wpa_supplicant.") # Connect to wpa_supplicant + report('debug', 'wireless_dbus', 'connecting to wpa_supplicant') bus = dbus.SystemBus() wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) @@ -104,20 +121,26 @@ def start(profile): iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE); # Add and select the network. Networks already specified for wpa-config - if profile['SECURITY'] in ['wpa','wep','none']: + if security in ['wpa','wep','none']: + report('debug', 'wireless_dbus', 'add and select network') path = iface.addNetwork() net_obj = bus.get_object(WPAS_DBUS_SERVICE, path) rnet = dbus.Interface(net_obj, WPAS_DBUS_NETWORKS_INTERFACE) iface.selectNetwork(rnet) + if not essid: + essid = profile["ESSID"] - if profile['SECURITY'] == "wpa": - opts = dbus.Dictionary({"ssid": dbus.ByteArray(profile['ESSID']), + if security == "wpa": + opts = dbus.Dictionary({"ssid": dbus.ByteArray(essid), "psk": dbus.String(profile['KEY'])}, signature="sv") + report('debug', 'wireless_dbus', 'connect to network with security=wpa') + # next line crashes with + # dbus.exceptions.DBusException: fi.epitest.hostap.WPASupplicant.InvalidOptions: Did not receive correct message arguments rnet.set(opts) - elif profile['SECURITY'] == "wep": + elif security == "wep": key=profile['KEY'] if key[:2] == "s:": # String key prefixed by "s:" keydbus=key[2:] @@ -127,15 +150,17 @@ def start(profile): for l in key: keydbus+=chr(l) - opts = dbus.Dictionary({"ssid": dbus.ByteArray(profile['ESSID']), + opts = dbus.Dictionary({"ssid": dbus.ByteArray(essid), "key_mgmt": dbus.String("NONE"), "wep_tx_keyidx": dbus.Int32(1), "wep_key0": dbus.ByteArray(keydbus)}, signature="sv") + report('debug', 'wireless_dbus', 'connect to network with security=wep') rnet.set(opts) - elif profile['SECURITY'] == "none": - opts = dbus.Dictionary({"ssid": dbus.ByteArray(profile['ESSID'])}, + elif security == "none": + opts = dbus.Dictionary({"ssid": dbus.ByteArray(essid)}, signature="sv") + report('debug', 'wireless_dbus', 'connect to network with security=none') rnet.set(opts) # Determine timeout @@ -154,24 +179,81 @@ def start(profile): break if n == timeout: - fail("Association/Authentication failed:" + state) + # fail("Association/Authentication failed:" + state) + fail("Couldn't associate/authenticate with wireless network.") # Run ethernet and get an ip. try: - subprocess.check_call(["/usr/lib/network/connections/ethernet-iproute", "up", sys.argv[2]]) + subprocess.check_call([ETHERNET_IPROUTE, "up", sys.argv[2]]) except subprocess.CalledProcessError: fail() sys.exit(0) def stop(profile): - subprocess.call(["/usr/lib/network/connections/ethernet", "down", sys.argv[2]]) + ret = subprocess.call([ETHERNET_IPROUTE, "down", sys.argv[2]]) kill(int(open("/var/run/wpa_supplicant.pid").read()),SIGTERM) - sys.exit(0) + sys.exit(ret) + +def query(interface,request): + if request == 'profile' or request=='enabled': + ret = subprocess.call([WIRELESS, "query", interface, request, "-iproute"]) # JP: left some of the new query functionality in the wireless file because it's so bash-heavy + elif request=='associated': + ret = subprocess.call([WIRELESS, "query", interface, request, "-iproute", sys.argv[5], sys.argv[6]]) # JP: left some of the new query functionality in the wireless file because it's so bash-heavy + else: + ret = subprocess.call([ETHERNET_IPROUTE, "query", interface, request]) # JP: for these requests, we can call the ethernet-iproute functions directly and save some process overhead + sys.exit(ret) + +def control(interface,action): + if action == 'enable' or action == 'disable': + ret = subprocess.call([WIRELESS, "control", interface, action]) + else: + ret = subprocess.call([ETHERNET_IPROUTE, "control", interface, action]) # JP: for these actions, we can call the ethernet-iproute functions directly and save some process overhead + sys.exit(ret) + +def verify(interface): + ret = subprocess.call([WIRELESS, "verify", interface]) + sys.exit(ret) if __name__ == "__main__": - profile = read_config("/etc/network.d/"+sys.argv[2]) - - if sys.argv[1] == "up": - start(profile) - elif sys.argv[1] == "down": - stop(profile) + + CONN_DIR = path.abspath(path.dirname(sys.argv[0])) + ETHERNET_IPROUTE = path.join(CONN_DIR,"ethernet-iproute") + WIRELESS = path.join(CONN_DIR,"wireless") + + if sys.argv[1] == "query": + interface,request = sys.argv[2:4] + query(interface,request) + + elif sys.argv[1] == "control": + interface,action = sys.argv[2:4] + control(interface,action) + + elif sys.argv[1] == "verify": + interface = sys.argv[2] + verify(interface) + + else: + # setup bash report_* handler + report_handler_script = 'source /etc/rc.conf; source /etc/rc.d/functions; source %s/../globals; while read cmd args; do eval $cmd "$args"; done' % (CONN_DIR,) + report_handler = subprocess.Popen(report_handler_script, executable='/bin/bash', stdin=subprocess.PIPE, shell=True) + + try: + profile_name = sys.argv[2] + profile = read_config("/etc/network.d/"+profile_name) + + essid = sys.argv[3] if len(sys.argv)>3 else "" # JP: pass literal ESSID as an argument, so that we can have entry in profile be a regexp + + if 'INTERFACE' not in profile: + fail("Profile error: no INTERFACE to configure.", report_type="err") + + if sys.argv[1] == "up": + start(profile, essid) + elif sys.argv[1] == "down": + stop(profile) + + + finally: + report_handler.stdin.close() + report_handler.wait() + +# vim: et ts=4 @@ -1,45 +1,53 @@ +################################## +## +# /usr/lib/network/8021x +## +################################## + # Uses wpa_supplicant to check for association to a network # wpa_check interface [timeout] wpa_check() { - local timeout=0 INTERFACE=$1 TIMEOUT=${2:-15} + local INTERFACE="$1" TIMEOUT="${2:-15}" timeout=0 - while [[ $timeout -lt $TIMEOUT ]]; do + while [[ "$timeout" -lt "$TIMEOUT" ]]; do ( # Sometimes wpa_supplicant isn't ready so silence errors for 2s only to avoid hiding real errors - if [[ $timeout -lt 2 ]]; then - eval `wpa_cli status 2> /dev/null|grep wpa_state` + if [[ "$timeout" -lt 2 ]]; then + eval $(wpa_cli -i "$INTERFACE" status 2> /dev/null | fgrep "wpa_state=") else - eval `wpa_cli status|grep wpa_state` + eval $(wpa_cli -i "$INTERFACE" status | fgrep "wpa_state=") fi - [[ "$wpa_state" = "COMPLETED" ]] + [[ "$wpa_state" = COMPLETED ]] ) && return 0 sleep 1 let timeout++ done wpa_cli terminate >/dev/null 2>&1 - err_append "Authentication/association failed" + report_fail "Couldn't associate/authenticate with wireless network." return 1 } start_wpa() { - INTERFACE="$1"; WPA_CONF="$2"; WPA_OPTS="$3" + local INTERFACE="$1" WPA_CONF="$2" WPA_OPTS="$3" - wpa_supplicant -B -P/var/run/wpa_supplicant_${INTERFACE}.pid -i"${INTERFACE}" -c "$WPA_CONF" $WPA_OPTS + wpa_supplicant -B -P "/var/run/wpa_supplicant_$INTERFACE.pid" -i "$INTERFACE" -c "$WPA_CONF" $WPA_OPTS sleep 1 - if [[ ! -f "/var/run/wpa_supplicant_${INTERFACE}.pid" ]]; then - err_append "wpa_supplicant did not start, possible configuration error" + if [[ ! -f "/var/run/wpa_supplicant_$INTERFACE.pid" ]]; then + report_fail "wpa_supplicant did not start, possible configuration error" return 1 fi } stop_wpa() { - wpa_cli terminate &> /dev/null - if [[ -f /var/run/wpa_supplicant_$1.pid ]]; then - kill $(cat /var/run/wpa_supplicant_$1.pid) &>/dev/null & + wpa_cli -i "$1" terminate &> /dev/null + sleep 1 # JP: need this else the file tends to disappear after [[ -f ... ]] but before cat... + # see <http://bbs.archlinux.org/viewtopic.php?pid=515667#p515667> + if [[ -f "/var/run/wpa_supplicant_$1.pid" ]]; then + kill $(cat "/var/run/wpa_supplicant_$1.pid") &>/dev/null & fi } diff --git a/src/connections/ethernet b/src/connections/ethernet index e24e9f5..933dbd6 100644 --- a/src/connections/ethernet +++ b/src/connections/ethernet @@ -1,86 +1,114 @@ #! /bin/bash + +################################## +## +# /usr/lib/network/connections/ethernet +## +################################## + +. /etc/rc.conf +. /etc/rc.d/functions . /usr/lib/network/network ethernet_up() { - load_profile $1 + load_profile "$1" - if [[ ! -e /sys/class/net/"$INTERFACE" ]]; then - if ! echo "$INTERFACE"|grep ":"; then - err_append "interface $INTERFACE does not exist" + if [[ ! -e "/sys/class/net/$INTERFACE" ]]; then + if ! echo "$INTERFACE" | fgrep -q ":"; then + report_fail "Interface $INTERFACE does not exist." return 1 fi fi - ip link set $INTERFACE up - sleep 1 + report_debug ethernet_up ifup + ethernet_control "$INTERFACE" up + sleep 3 - if ip link show $INTERFACE|grep -q "NO-CARRIER"; then - err_append "No connection" + # don't think it's possible to detect carrier using ifconfig alone (at least, not without ifdown/ifupping the interface) + # if ip link show dev "$INTERFACE" | fgrep -q "NO-CARRIER"; then... + # if ! ethernet_query "$INTERFACE" address; then... + if [[ $(cat /sys/class/net/$INTERFACE/carrier 2>/dev/null) -ne 1 ]]; then # gives err if iface inactive (i.e. ifdown) + # 0 if up but not connected to network, 1 if connected + report_fail "No connection." return 1 fi if checkyesno "${AUTH8021X:-no}"; then - . "${SUBR_DIR}"/8021x + . "$SUBR_DIR/8021x" [[ -z "$WPA_CONF" ]] && WPA_CONF="/etc/wpa_supplicant.conf" [[ -z "$WPA_OPTS" ]] && WPA_OPTS="-Dwired" + report_debug ethernet_up start_wpa "$INTERFACE" "$WPA_CONF" "$WPA_OPTS" start_wpa "$INTERFACE" "$WPA_CONF" "$WPA_OPTS" if ! wpa_check "$INTERFACE"; then - ifconfig "$INTERFACE" down + ethernet_control "$INTERFACE" forcedown # JP: forcedown ignores the nodown quirk, matching + # what was already here in the code...do we in fact want to ignore + # the nodown quirk here? return 1 fi fi - case $IP in + case "$IP" in dhcp) if checkyesno "${DHCLIENT:-no}"; then - rm -r /var/run/dhclient-${INTERFACE}.pid >/dev/null 2>&1 - if ! dhclient -q -e TIMEOUT="${DHCP_TIMEOUT:-10}" -pf /var/run/dhclient-${INTERFACE}.pid $INTERFACE; then - err_append "DHCP IP lease attempt failed." + rm -r "/var/run/dhclient-$INTERFACE.pid" >/dev/null 2>&1 + report_debug ethernet_up dhclient -q -e TIMEOUT="${DHCP_TIMEOUT:-10}" -pf "/var/run/dhclient-$INTERFACE.pid" "$INTERFACE" + if ! dhclient -q -e TIMEOUT="${DHCP_TIMEOUT:-10}" -pf "/var/run/dhclient-$INTERFACE.pid" "$INTERFACE"; then + report_fail "Couldn't get DHCP IP lease." return 1 fi else # Clear remaining pid files. - rm -f /var/run/dhcpcd-${INTERFACE}.{pid,cache} >/dev/null 2>&1 + rm -f "/var/run/dhcpcd-$INTERFACE".{pid,cache} >/dev/null 2>&1 # If using own dns, tell dhcpcd to NOT replace resolv.conf - [[ -n "$DNS1" ]] && DHCP_OPTIONS="-C resolv.conf $DHCP_OPTIONS" - # Start dhcpcd - if ! dhcpcd -qL -t "${DHCP_TIMEOUT:-10}" $DHCP_OPTIONS "$INTERFACE"; then - err_append "DHCP IP lease attempt failed." + [[ -n "$DNS1" || -n "$DNS" ]] && DHCP_OPTIONS="-C resolv.conf $DHCP_OPTIONS" + + #if ! dhcpcd -qL -t "${DHCP_TIMEOUT:-10}" $DHCP_OPTIONS "$INTERFACE" >/dev/null 2>&1; then + report_debug ethernet_up dhcpcd -qL -t "${DHCP_TIMEOUT:-10}" $DHCP_OPTIONS "$INTERFACE" + dhcpcd -qL -t "${DHCP_TIMEOUT:-10}" $DHCP_OPTIONS "$INTERFACE" 2>&1 | report_debug $(cat) + if [[ "$PIPESTATUS" -ne 0 ]]; then + report_fail "Couldn't get DHCP IP lease." return 1 fi fi - [[ -n "$IFOPTS" ]] && ifconfig "$INTERFACE" $IFOPTS + if [[ -n "$IFOPTS" ]]; then + report_debug ethernet_up ifup $IFOPTS + ifconfig "$INTERFACE" $IFOPTS + fi ;; static) + report_debug ethernet_up ifup $IFOPTS if ! ifconfig "$INTERFACE" $IFOPTS up; then - err_append "Bringing interface up failed." + report_fail "Couldn't configure $INTERFACE interface." return 1 fi # bring up the default route (gateway) if [[ -n "$GATEWAY" ]]; then - if ! route add default gw $GATEWAY; then - err_append "Adding gateway failed." + report_debug ethernet_up route add default gw "$GATEWAY" + if ! route add default gw "$GATEWAY"; then + ethernet_control "$INTERFACE" down # JP: don't we want to take the interface down if we failed (as ethernet-iproute does?) + report_fail "Couldn't add gateway $GATEWAY." return 1 fi fi ;; *) - err_append "IP=\"\" must be either 'dhcp' or 'static'." + report_err "Profile error: must be either IP=dhcp or IP=static." return 1 ;; esac # set the hostname if [[ -n "$HOSTNAME" ]]; then + report_debug ethernet_up hostname "$HOSTNAME" if ! hostname "$HOSTNAME"; then - err_append "Setting hostname failed." + report_fail "Couldn't set hostname." return 1 fi fi # Generate a new resolv.conf - if [[ -n "$DNS1" ]] || [[ -n "$DNS" ]]; then + if [[ -n "$DNS1" || -n "$DNS" ]]; then : >/etc/resolv.conf [[ -n "$DOMAIN" ]] && echo "domain $DOMAIN" >>/etc/resolv.conf @@ -94,36 +122,141 @@ ethernet_up() { done fi fi + + # successfully running a new profile; erase any suspended profiles on this interface + local iface="$INTERFACE" + find "$STATE_DIR/suspend/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + # the pipe to "while read" will create a subshell, so sourced variables will already be in a sandbox + # we just need to clear INTERFACE which is all we care about + unset INTERFACE + . "$STATE_DIR/suspend/$prof" + if [[ "$iface" == "$INTERFACE" ]]; then + rm "$STATE_DIR/suspend/$prof" + fi + done + return 0 } ethernet_down() { - load_profile $1 - case $IP in + load_profile "$1" + case "$IP" in dhcp) if checkyesno "${DHCLIENT:-no}"; then - if [[ -f /var/run/dhclient-${INTERFACE}.pid ]]; then - kill `cat /var/run/dhclient-${INTERFACE}.pid` + if [[ -f "/var/run/dhclient-$INTERFACE.pid" ]]; then + report_debug ethernet_down kill dhclient + kill $(cat "/var/run/dhclient-$INTERFACE.pid") fi else - if [[ -f /var/run/dhcpcd-${INTERFACE}.pid ]]; then - dhcpcd -qx "$INTERFACE" + if [[ -f "/var/run/dhcpcd-$INTERFACE.pid" ]]; then + report_debug ethernet_down dhcpcd -qx "$INTERFACE" + dhcpcd -qx "$INTERFACE" >/dev/null 2>&1 fi fi ;; static) - [[ -n "$GATEWAY" ]] && route del default gw $GATEWAY + if [[ -n "$GATEWAY" ]]; then + report_debug ethernet_down route del default gw "$GATEWAY" + route del default gw "$GATEWAY" + fi ;; esac - ifconfig $INTERFACE 0.0.0.0 - - case "$CONNECTION" in # Keep interface up for wireless - ethernet|ethernet-old) - quirk "nodown" || ifconfig $INTERFACE down + + report_debug ethernet_down ifdown + ethernet_control "$INTERFACE" down +} + +# CONNECTION_query $INTERFACE [ profile | enabled (i.e. not rfkill-ed) | active (i.e. ifup'd) | associated | address ] +ethernet_query() { + local INTERFACE="$1" REQUEST="$2" + # report_debug ethernet_query "$@" + case "$REQUEST" in + + # JP: this duplicates code in ethernet-iproute, but doing so was necessary to refactor these functions into the CONNECTION scripts + profile) + # return code = 1 if iface is available to be used + # return code = 0 if unavailable (disabled, or in use by another profile or by external config) + # echo "" | "disabled" | "external" | PROFILE_NAME + if [[ ! -f "$STATE_DIR/interfaces/$INTERFACE" ]]; then + # interface is up but has no registered profile + if ethernet_query "$INTERFACE" address; then + set_iface up "$INTERFACE" external + echo "external" + return 0 + fi + # no ip address, count as inactive + # should we ifconfig iface down? no, may be up for a reason + return 1 + + else # interface is up and thinks it's running a profile + . "$STATE_DIR/interfaces/$INTERFACE" # these files should only contain a PROFILE=... statement + case "$PROFILE" in + external) + echo "external" + return 0 + ;; + disabled) # shouldn't happen for non-wireless interfaces, but leaving this here in case... + # iface should be down if it's disabled + ethernet_control "$INTERFACE" down + echo "disabled" + return 0 + ;; + *) + ( # use subshell to prevent overwriting currently loaded profile + . "$STATE_DIR/profiles/$PROFILE" + if ! ethernet_query "$INTERFACE" address; then + report_warn "INTERFACE $INTERFACE is inactive; no longer being controlled by $PROFILE" + set_profile down "$PROFILE" + exit 1 + else + echo "$PROFILE" # associated with loaded profile + exit 0 + fi + ) + # return $? + ;; + esac + fi + ;; + + active) + ifconfig "$INTERFACE" | fgrep -qw UP + # cat /sys/class/net/wlan0/carrier >/dev/null # gives err if iface inactive, 0 if up but not connected to network, 1 if connected + ;; + + address) + ifconfig "$INTERFACE" | fgrep -q addr: + ;; + + *) return 1 ;; + esac +} + +# CONNECTION_control $INTERFACE [ up | down | forcedown | enable | disable ] +ethernet_control() { + local INTERFACE="$1" ACTION="$2" + #report_debug ethernet_control "$@" + case "$ACTION" in + up) ifconfig "$INTERFACE" up 2>/dev/null + ethernet_query "$INTERFACE" active + ;; + down|forcedown) + ifconfig "$1" 0.0.0.0 + if [[ "$2" = forcedown ]] || ! quirk nodown; then + ifconfig "$INTERFACE" down + fi + ;; + *) return 1 ;; esac } -ethernet_$1 $2 +ethernet_verify() { + local INTERFACE="$1" + # do nothing +} + +ethernet_$1 "$2" "$3" exit $? # vim: set ts=4 et sw=4: diff --git a/src/connections/ethernet-iproute b/src/connections/ethernet-iproute index 1a4a6e4..8ecabe8 100644 --- a/src/connections/ethernet-iproute +++ b/src/connections/ethernet-iproute @@ -1,85 +1,102 @@ #! /bin/bash + +################################## +## +# /usr/lib/network/connections/ethernet-iproute +## +################################## + +. /etc/rc.conf +. /etc/rc.d/functions . /usr/lib/network/network -error() -{ - err_append "$*" - ip addr flush $INTERFACE &>/dev/null - quirk "nodown" || ip link set $INTERFACE down &>/dev/null +report_iproute() { + report_"$@" + ethernet_control "$INTERFACE" down exit 1 } ethernet_up() { - load_profile $1 + load_profile "$1" - if [[ ! -e /sys/class/net/$INTERFACE ]]; then - if ! echo "$INTERFACE"|grep ":"; then - error "Interface $INTERFACE does not exist" + if [[ ! -e "/sys/class/net/$INTERFACE" ]]; then + if ! echo "$INTERFACE" | fgrep -q ":"; then + report_iproute fail "Interface $INTERFACE does not exist." fi fi - ip link set $INTERFACE up - sleep 1 + report_debug ethernet_iproute_up ifup + ethernet_control "$INTERFACE" up + sleep 3 - if ip link show $INTERFACE|grep -q "NO-CARRIER"; then - err_append "No connection" - return 1 + # if ! ethernet_query "$INTERFACE" address; then... + if ip link show dev "$INTERFACE" | fgrep -q "NO-CARRIER"; then # JP: shouldn''t we include the "dev"? + report_iproute fail "No connection." fi - - if checkyesno ${AUTH8021X:-no}; then - . ${SUBR_DIR}/8021x + + if checkyesno "${AUTH8021X:-no}"; then + . "$SUBR_DIR/8021x" [[ -z "$WPA_CONF" ]] && WPA_CONF="/etc/wpa_supplicant.conf" [[ -z "$WPA_OPTS" ]] && WPA_OPTS="-Dwired" - + report_debug ethernet_iproute_up start_wpa "$INTERFACE" "$WPA_CONF" "$WPA_OPTS" start_wpa "$INTERFACE" "$WPA_CONF" "$WPA_OPTS" if ! wpa_check "$INTERFACE"; then - ip link set $INTERFACE down + ethernet_control "$INTERFACE" forcedown # JP: forcedown ignores the nodown quirk, matching + # what was already here in the code...do we in fact want to ignore + # the nodown quirk here? return 1 fi fi - case $IP in + case "$IP" in dhcp) # Clear remaining pid files. - rm -f /var/run/dhcpcd-${INTERFACE}.{pid,cache} >/dev/null 2>&1 - + rm -f "/var/run/dhcpcd-$INTERFACE".{pid,cache} >/dev/null 2>&1 # If using own dns, tell dhcpcd to NOT replace resolv.conf [[ -n "$DNS" ]] && DHCP_OPTIONS="-C resolv.conf $DHCP_OPTIONS" - - if ! dhcpcd -qL -t ${DHCP_TIMEOUT:-10} $DHCP_OPTIONS $INTERFACE; then - error "DHCP IP lease attempt failed" + + #if ! dhcpcd -qL -t "${DHCP_TIMEOUT:-10}" $DHCP_OPTIONS "$INTERFACE" >/dev/null 2>&1; then + report_debug ethernet_iproute_up dhcpcd -qL -t "${DHCP_TIMEOUT:-10}" $DHCP_OPTIONS "$INTERFACE" + dhcpcd -qL -t "${DHCP_TIMEOUT:-10}" $DHCP_OPTIONS "$INTERFACE" 2>&1 | report_iproute debug $(cat) + if [[ "$PIPESTATUS" -ne 0 ]]; then + report_iproute fail "Couldn't get DHCP IP lease." fi ;; static) if [[ -n "$ADDR" ]]; then - if ! ip addr add ${ADDR}/24 brd + dev $INTERFACE; then - error "Could not configure interface" + report_debug ethernet_iproute_up ip addr add "$ADDR/24" brd + dev "$INTERFACE" + if ! ip addr add "$ADDR/24" brd + dev "$INTERFACE"; then + report_iproute fail "Couldn't configure $INTERFACE interface." fi fi if [[ -n "$GATEWAY" ]]; then - if ! ip route add default via $GATEWAY; then - error "Adding gateway failed" + report_debug ethernet_iproute_up ip route add default via "$GATEWAY" + if ! ip route add default via "$GATEWAY"; then + + report_iproute fail "Couldn't add gateway $GATEWAY." fi fi ;; *) - error "Profile error: IP must be either 'dhcp' or 'static'" + report_iproute err "Profile error: must be either IP=dhcp or IP=static." ;; esac if [[ -n "$IPCFG" ]]; then for line in "${IPCFG[@]}"; do - if ! ip $line; then - error "Could not configure interface" + report_debug ethernet_iproute_up ip "$line" + if ! ip "$line"; then + report_iproute fail "Couldn't configure $INTERFACE interface ($line)." fi done fi - # Set hostname + # set the hostname if [[ -n "$HOSTNAME" ]]; then - if ! hostname $HOSTNAME; then - error "Cannot set hostname" + report_debug ethernet_iproute_up hostname "$HOSTNAME" + if ! hostname "$HOSTNAME"; then + report_iproute fail "Couldn't set hostname." fi fi @@ -89,29 +106,132 @@ ethernet_up() { [[ -n "$DOMAIN" ]] && echo "domain $DOMAIN" >>/etc/resolv.conf [[ -n "$SEARCH" ]] && echo "search $SEARCH" >>/etc/resolv.conf - if [[ -n "$DNS" ]]; then - for dns in ${DNS[@]}; do - echo "nameserver $dns" >>/etc/resolv.conf - done - fi + for dns in "${DNS[@]}"; do + echo "nameserver $dns" >>/etc/resolv.conf + done fi + + # successfully running a new profile; erase any suspended profiles on this interface + local iface="$INTERFACE" + find "$STATE_DIR/suspend/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + # the pipe to "while read" will create a subshell, so sourced variables will already be in a sandbox + # we just need to clear INTERFACE which is all we care about + unset INTERFACE + . "$STATE_DIR/suspend/$prof" + if [[ "$iface" == "$INTERFACE" ]]; then + rm "$STATE_DIR/suspend/$prof" + fi + done + return 0 } ethernet_down() { - load_profile $1 + load_profile "$1" - if [[ "$IP" == "dhcp" ]]; then - if [[ -f /var/run/dhcpcd-${INTERFACE}.pid ]]; then - dhcpcd -qx $INTERFACE + if [[ "$IP" == dhcp ]]; then + if [[ -f "/var/run/dhcpcd-$INTERFACE.pid" ]]; then + report_debug ethernet_iproute_down dhcpcd -qx "$INTERFACE" + dhcpcd -qx "$INTERFACE" >/dev/null 2>&1 fi fi - ip addr flush $INTERFACE - quirk "nodown" || ip link set $INTERFACE down + report_debug ethernet_iproute_down if_down + ethernet_control "$INTERFACE" down +} + +# CONNECTION_query $INTERFACE [ profile | enabled (i.e. not rfkill-ed) | active (i.e. ifup'd) | associated | address ] +ethernet_query() { + local INTERFACE="$1" REQUEST="$2" + # report_debug ethernet_iproute_query "$@" + case "$REQUEST" in + + profile) + # return code = 1 if iface is available to be used + # return code = 0 if unavailable (disabled, or in use by another profile or by external config) + # echo "" | "disabled" | "external" | PROFILE_NAME + if [[ ! -f "$STATE_DIR/interfaces/$INTERFACE" ]]; then + # interface is up but has no registered profile + if ethernet_query "$INTERFACE" address; then + set_iface up "$INTERFACE" external + echo "external" + return 0 + fi + # no ip address, count as inactive + # should we ifconfig iface down? no, may be up for a reason + return 1 + + else # interface is up and thinks it's running a profile + . "$STATE_DIR/interfaces/$INTERFACE" # these files should only contain a PROFILE=... statement + case "$PROFILE" in + external) + echo "external" + return 0 + ;; + disabled) # shouldn't happen for non-wireless interfaces, but leaving this here in case... + # iface should be down if it's disabled + ethernet_control "$INTERFACE" down + echo "disabled" + return 0 + ;; + *) + ( # use subshell to prevent overwriting currently loaded profile + . "$STATE_DIR/profiles/$PROFILE" + if ! ethernet_query "$INTERFACE" address; then + report_warn "INTERFACE $INTERFACE is inactive; no longer being controlled by $PROFILE" + set_profile down "$PROFILE" + exit 1 + else + echo "$PROFILE" # associated with loaded profile + exit 0 + fi + ) + # return $? + ;; + esac + fi + ;; + + active) + ip -o link show dev "$INTERFACE" | egrep -qw '^[^>]*,UP' # man ip is inconsistent about whether to use "dev" + # cat /sys/class/net/wlan0/carrier >/dev/null # gives err if iface inactive, 0 if up but not connected to network, 1 if connected + ;; + + address) + test -n "$(ip -o addr show dev "$INTERFACE" | sed -n '2p')" + # ip link show dev "$INTERFACE" | fgrep -q "NO-CARRIER" + ;; + + *) return 1 ;; + esac +} + +# CONNECTION_control $INTERFACE [ up | down | forcedown | enable | disable ] +ethernet_control() { + local INTERFACE="$1" ACTION="$2" + #report_debug ethernet_iproute_control "$@" + case "$ACTION" in + up) + ip link set dev "$INTERFACE" up 2>/dev/null # man ip is inconsistent about whether to use "dev" + ethernet_query "$INTERFACE" active + ;; + down|forcedown) + ip addr flush dev "$INTERFACE" &>/dev/null + if [[ "$2" = forcedown ]] || ! quirk nodown; then + ip link set dev "$INTERFACE" down &>/dev/null # man ip is inconsistent about whether to use "dev" + fi + ;; + *) return 1 + ;; + esac +} +ethernet_verify() { + local INTERFACE="$1" + # do nothing } -ethernet_$1 $2 +ethernet_$1 "$2" "$3" exit $? # vim: set ts=4 et sw=4: diff --git a/src/connections/ppp b/src/connections/ppp index 9432323..ce3c8ab 100644 --- a/src/connections/ppp +++ b/src/connections/ppp @@ -1,25 +1,58 @@ #! /bin/bash +################################## +## +# /usr/lib/network/connections/ppp +## +################################## + +. /etc/rc.conf +. /etc/rc.d/functions . /usr/lib/network/network ppp_up() { - load_profile $1 - [[ -z "$PEER" ]] && PEER="provider" + load_profile "$1" + [[ -z "$PEER" ]] && PEER=provider [[ -z "$PPP_TIMEOUT" ]] && PPP_TIMEOUT=30 - /usr/sbin/pppd call $PEER updetach child-timeout $PPP_TIMEOUT linkname $PEER - - if [[ $? -ne 0 ]]; then - err_append "pppd connection failed" - exit 1 + if ! /usr/sbin/pppd call "$PEER" updetach child-timeout "$PPP_TIMEOUT" linkname "$PEER"; then + report_fail "Couldn't make pppd connection." + return 1 fi + + # wireless and wireless-dbus connections run this block when they call "ethernet up"; but ppp needs to do it itself + # successfully running a new profile; erase any suspended profiles on this interface + local interface="$INTERFACE" + find "$STATE_DIR/suspend/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + # the pipe to "while read" will create a subshell, so sourced variables will already be in a sandbox + # we just need to clear INTERFACE which is all we care about + unset INTERFACE + . "$STATE_DIR/suspend/$prof" + if [[ "$interface" == "$INTERFACE" ]]; then + rm "$STATE_DIR/suspend/$prof" + fi + done } ppp_down() { - load_profile $1 - kill $(head -1 /var/run/ppp-$(basename $PEER).pid) + load_profile "$1" + kill $(head -1 "/var/run/ppp-$(basename "$PEER").pid") +} + +ppp_query() { + "$CONN_DIR/ethernet" query "$@" +} + +ppp_control() { + "$CONN_DIR/wireless" control "$@" +} + +ppp_verify() { + local INTERFACE="$1" + # do nothing } -ppp_$1 $2 +ppp_$1 "$2" exit $? # vim: set ts=4 et sw=4: diff --git a/src/connections/wireless b/src/connections/wireless index 8b43ed1..f316cb7 100644 --- a/src/connections/wireless +++ b/src/connections/wireless @@ -1,124 +1,378 @@ #! /bin/bash + +################################## +## +# /usr/lib/network/connections/wireless +## +################################## + +. /etc/rc.conf +. /etc/rc.d/functions . /usr/lib/network/network wireless_up() { - load_profile $1 - . ${SUBR_DIR}/8021x - . ${SUBR_DIR}/wireless + load_profile "$1" + [[ -n "$2" ]] && ESSID="$2" # JP: use the literal ESSID (though currently we only interpret wireless-dbus ESSIDs as regexps) + + . "$SUBR_DIR/8021x" + . "$SUBR_DIR/wireless_utils" # Check if interface exists - if [[ ! -e /sys/class/net/"$INTERFACE" ]]; then - if ! echo "$INTERFACE"|grep ":"; then - err_append "interface $INTERFACE does not exist" + if [[ ! -e "/sys/class/net/$INTERFACE" ]]; then + if ! echo "$INTERFACE" | fgrep -q ":"; then + report_fail "Interface $INTERFACE does not exist." return 1 fi fi # Kill any lingering wpa_supplicants. - stop_wpa $INTERFACE + report_debug wireless_up stop_wpa "$INTERFACE" + stop_wpa "$INTERFACE" # Most drivers (mac80211) need mode set before device is brought up # Drivers generally default to managed, but set this to be sure. - if [[ "$(iwgetid -sm $INTERFACE)" -ne "Managed" ]]; then - iwconfig $INTERFACE mode managed - fi + if [[ $(iwgetid -sm "$INTERFACE") -ne Managed ]]; then + report_debug wireless_up iwconfig "$INTERFACE" mode managed + iwconfig "$INTERFACE" mode managed + fi - ifconfig $INTERFACE up + report_debug wireless_up ifup + wireless_control "$INTERFACE" up || return 1 + + quirk prescan && iwlist "$INTERFACE" scan &> /dev/null # bcm43xx + + + if quirk preessid; then # ipw3945 + if [[ -n "$AP" ]]; then # JP: enable use of AP + # JP: Since I don't undertand why the else block below is an eval, I'm not sure + # if this command also needs to be eval'd + iwconfig "$INTERFACE" mode managed ap "$AP" + else + # JP: I don't understand why this needs to be an eval. What's wrong with just: + # iwconfig "$INTERFACE" mode managed essid "$ESSID" + eval "iwconfig \"$INTERFACE\" mode managed essid \"$ESSID\"" + fi - quirk "prescan" && iwlist $INTERFACE scan &> /dev/null # bcm43xx - quirk "preessid" && eval "iwconfig $INTERFACE mode managed essid \"$ESSID\"" # ipw3945 + fi - if checkyesno ${SCAN:-no}; then - if ! find_essid $INTERFACE "$ESSID"; then - err_append "Network not present." + if checkyesno "${SCAN:-no}"; then + report_debug wireless_up scanning + local OLDESSID="$ESSID" + if [[ -n "$AP" ]]; then + ESSID=$(find_ap "$INTERFACE" "$AP") + else + ESSID=$(find_essid "$INTERFACE" "$ESSID" "$CONNECTION") # JP: we could have left $3 null for default of treating ESSID as literal + # but instead we explicitly pass $CONNECTION + fi + if [[ $? -gt 0 ]]; then + report_fail "Wireless network \"$OLDESSID\" not present." return 1 fi fi # Manually set iwconfig options - [[ "$IWCONFIG" ]] && iwconfig $INTERFACE $IWCONFIG + if [[ -n "$IWCONFIG" ]]; then + report_debug wireless_up iwconfig "$INTERFACE" $IWCONFIG + iwconfig "$INTERFACE" $IWCONFIG + fi # Set to 'none' if not set - [[ -z "$SECURITY" ]] && SECURITY="none" + [[ -z "$SECURITY" ]] && SECURITY=none - case $SECURITY in + case "$SECURITY" in wep|none) # 'none' uses iwconfig like wep. Use sane default if WEP_OPTS="" - if [[ "$SECURITY" = "wep" ]]; then - WEP_OPTS="essid \"$ESSID\" key $KEY" - elif [[ "$SECURITY" = "none" ]]; then - WEP_OPTS="essid \"$ESSID\"" - fi + if [[ -z "$WEP_OPTS" ]]; then + if [[ "$SECURITY" = wep ]]; then + if [[ -n "$AP" ]]; then + WEP_OPTS="ap \"$AP\" key $KEY" # JP: formerly I had "...key open $KEY"; is it correct to omit the 'open'? + else + WEP_OPTS="essid \"$ESSID\" key $KEY" # JP: formerly I had "...key open $KEY"; is it correct to omit the 'open'? + fi + else # SECURITY=none + if [[ -n "$AP" ]]; then + WEP_OPTS="ap \"$AP\"" + else + WEP_OPTS="essid \"$ESSID\"" + fi + fi + fi - quirk "predown" && ifconfig $INTERFACE down # madwifi FS#10585 + quirk predown && wireless_control "$INTERFACE" forcedown # madwifi FS#10585 - if ! eval iwconfig $INTERFACE $WEP_OPTS; then - err_append "Could not set wireless configuration." + report_debug wireless_up iwconfig "$INTERFACE" $WEP_OPTS + # JP: I don't understand why this needs to be an eval. What's wrong with just: + # iwconfig "$INTERFACE" $WEP_OPTS + if ! eval "iwconfig \"$INTERFACE\" $WEP_OPTS"; then + report_fail "Couldn't configure wireless." return 1 fi - quirk "predown" && ifconfig $INTERFACE up # madwifi FS#10585 + + + quirk predown && wireless_control "$INTERFACE" up # madwifi FS#10585 - wep_check $INTERFACE $TIMEOUT||return 1 + report_debug ethernet_up wep_check + wep_check "$INTERFACE" "$TIMEOUT" || return 1 ;; + wpa) # Quirk for broken drivers... http://bbs.archlinux.org/viewtopic.php?id=36384 - quirk "wpaessid" && eval iwconfig $INTERFACE essid "\"$ESSID\"" + if quirk wpaessid; then + if [[ -n "$AP" ]]; then + # JP: Since I don't undertand why the else block below is an eval, I'm not sure + # if this command also needs to be eval'd + iwconfig "$INTERFACE" ap "$AP" + else + # JP: I don't understand why this needs to be an eval. What's wrong with just: + # iwconfig "$INTERFACE" essid "$ESSID" + eval "iwconfig \"$INTERFACE\" essid \"$ESSID\"" + fi + fi - local WPA_CONF="/tmp/wpa.${1// /}" # substitute spaces out - echo "ctrl_interface=/var/run/wpa_supplicant" >> $WPA_CONF - echo "ctrl_interface_group=${WPA_GROUP:-wheel}" >> $WPA_CONF - chmod 600 $WPA_CONF + local WPA_CONF="${TMPDIR:-/tmp}/wpa.${1// /}" # substitute spaces out + # make empty tmp dir with correct permissions, rename it + rm -rf "$WPA_CONF" + mv -f $(mktemp -d) "$WPA_CONF" || return 1 + echo "ctrl_interface=/var/run/wpa_supplicant" >> "$WPA_CONF/wpa.conf" # we know $WPA_CONF now has no spaces, but it may have other nasty chars, so still needs to be quoted + echo "ctrl_interface_group=${WPA_GROUP:-wheel}" >> "$WPA_CONF/wpa.conf" # Generate configuration - if [[ "${#KEY}" == "64" ]]; then - echo -e "network={ \nssid=\"$ESSID\" \npsk=$KEY \n}">> $WPA_CONF - elif ! echo "$KEY" | wpa_passphrase "$ESSID" >> $WPA_CONF; then - err_append "Configuration generation failed. $(cat $WPA_CONF)" + if [[ "${#KEY}" -eq 64 ]]; then + echo -e "network={ \nssid=\"$ESSID\" \npsk=$KEY \n}">> "$WPA_CONF/wpa.conf" + # JP: formerly I had { \nssid=\"$ESSID\" \nproto=WPA \nkey_mgmt=WPA-PSK \npsk=$KEY \n} + # JP: is what's above better? + elif ! echo "$KEY" | wpa_passphrase "$ESSID" >> "$WPA_CONF/wpa.conf"; then + report_fail "Couldn't generate WPA configuration." + cat "$WPA_CONF/wpa.conf" >&2 return 1 fi # Connect! [[ -z "$WPA_OPTS" ]] && WPA_OPTS="-Dwext" - start_wpa $INTERFACE $WPA_CONF $WPA_OPTS || return 1 - if ! wpa_check $INTERFACE $TIMEOUT; then - stop_wpa $INTERFACE + report_debug wireless_up start_wpa "$INTERFACE" "$WPA_CONF/wpa.conf" "$WPA_OPTS" + start_wpa "$INTERFACE" "$WPA_CONF/wpa.conf" "$WPA_OPTS" || return 1 + report_debug wireless_up wpa_check + if ! wpa_check "$INTERFACE" "$TIMEOUT"; then + stop_wpa "$INTERFACE" return 1 fi ;; wpa-config) - . ${SUBR_DIR}/8021x + . "$SUBR_DIR/8021x" [[ -z "$WPA_CONF" ]] && WPA_CONF="/etc/wpa_supplicant.conf" # defaults [[ -z "$WPA_OPTS" ]] && WPA_OPTS="-Dwext" - start_wpa $INTERFACE $WPA_CONF $WPA_OPTS || return 1 - if ! wpa_check $INTERFACE $TIMEOUT; then - stop_wpa $INTERFACE + report_debug wireless_up start_wpa "$INTERFACE" "$WPA_CONF" "$WPA_OPTS" + start_wpa "$INTERFACE" "$WPA_CONF" "$WPA_OPTS" || return 1 + report_debug wireless_up wpa_check + if ! wpa_check "$INTERFACE" "$TIMEOUT"; then + stop_wpa "$INTERFACE" return 1 fi ;; esac - if ! ${CONN_DIR}/ethernet up $1; then - wireless_down $1 YES + if ! "$CONN_DIR/ethernet" up "$1"; then + wireless_down "$1" YES return 1 fi } +# wireless_down PROFILE [ LEAVE ifconfig up? default no ] wireless_down() { - load_profile $1 - . ${SUBR_DIR}/8021x - PROFILE=$1 NOETHERNETDOWN=$2 - if ! checkyesno $2; then - ${CONN_DIR}/ethernet down $1 + load_profile "$1" + . "$SUBR_DIR/8021x" + local PROFILE="$1" NOETHERNETDOWN="$2" # JP: made declarations local?? also, why introduce these vars if you're not going to use them? + if ! checkyesno "$NOETHERNETDOWN"; then + "$CONN_DIR/ethernet" down "$PROFILE" fi - stop_wpa $INTERFACE - [[ "$SECURITY" == "wpa" ]] && rm -f "/tmp/wpa.${1// /}" # remove wpa config - iwconfig $INTERFACE essid off key off &> /dev/null - ifconfig $INTERFACE down + report_debug wireless_down stop_wpa "$INTERFACE" + stop_wpa "$INTERFACE" + [[ "$SECURITY" == wpa ]] && rm -rf "/tmp/wpa.${PROFILE// /}" # remove tmp wpa config + report_debug wireless_down iwconfig "$INTERFACE" essid off key off + iwconfig "$INTERFACE" essid off key off &> /dev/null + #wireless_control "$INTERFACE" forcedown # this is now handled in ethernet down, isn't it? } -wireless_$1 $2 +# CONNECTION_query $INTERFACE [ profile | enabled (i.e. not rfkill-ed) | active (i.e. ifup'd) | associated | address ] [ "" | "-iproute" ] +# "query associated" also uses $4=ESSID $5=AP if known +wireless_query() { + local INTERFACE="$1" REQUEST="$2" NEW="$3" + #report_debug wireless_query "$@" + case "$REQUEST" in + + profile) + # return code = 1 if iface is available to be used + # return code = 0 if unavailable (disabled, or in use by another profile or by external config) + # echo "" | "disabled" | "external" | PROFILE_NAME + if [[ ! -f "$STATE_DIR/interfaces/$INTERFACE" ]]; then + # wireless interface is up but has no registered profile + if ! wireless_query "$INTERFACE" enabled "$NEW"; then + wireless_control disable "$INTERFACE" # STATE_DIR/interface should say "disabled" instead + # iface should be down if it''s disabled + wireless_control "$INTERFACE" down "$NEW" + echo "disabled" + return 0 + fi + if wireless_query "$INTERFACE" address "$NEW"; then + set_iface up "$INTERFACE" external + echo "external" + return 0 + fi + # no ip address, count as inactive (even if associated) + # should we tell iface to de-associate if still associated? + # should we ifconfig iface down? no, may be up for a reason + return 1 + + else # interface is up and thinks it's running a profile + . "$STATE_DIR/interfaces/$INTERFACE" # these files should only contain a PROFILE=... statement + case "$PROFILE" in + external) + echo "external" + return 0 + ;; + disabled) + # iface should be down if it's disabled + wireless_control "$INTERFACE" down "$NEW" + echo "disabled" + return 0 + ;; + *) + ( # use subshell to prevent overwriting currently loaded profile + . "$STATE_DIR/profiles/$PROFILE" + if ! "$CONN_DIR/$CONNECTION" query "$INTERFACE" enabled; then + report_warn "INTERFACE $INTERFACE is disabled; no longer being controlled by $PROFILE" + set_profile down "$PROFILE" + wireless_control disable "$INTERFACE" + wireless_control "$INTERFACE" down "$NEW" + echo "disabled" + exit 0 + elif ! wireless_query "$INTERFACE" address "$NEW"; then + report_warn "INTERFACE $INTERFACE is inactive; no longer being controlled by $PROFILE" + set_profile down "$PROFILE" + exit 1 + elif "$CONN_DIR/$CONNECTION" query "$INTERFACE" associated "$NEW" "$ESSID" "$AP"; then + echo "$PROFILE" # associated with loaded profile + exit 0 + else + report_warn "INTERFACE $INTERFACE no longer being controlled by $PROFILE" + set_profile down "$PROFILE" + set_iface up "$INTERFACE" external + echo "external" + exit 0 + fi + ) + # return $? + ;; + esac + fi + ;; + + enabled) + local rfkill iftype + for rfkill in /sys/class/rfkill/*; do + if [[ -e "$rfkill/type" && -e "$rfkill/state" ]]; then + iftype=$(cat "$rfkill/type") + if [[ "$iftype" == wlan || "$iftype" == "${INTERFACE:0:${#INTERFACE}-1}" ]]; then # JP: don't really know what the range of values for this is + # on my machine, the INTERFACE is wlan0 and the type=wlan + test $(cat "$rfkill/state") -eq 1 # state is 1 when enabled, 2 when killed + return $? + fi + fi + done +# # JP: this is easier, but I don't know if it can be relied on to stay in place... +# if [[ -e /sys/class/net/$INTERFACE/device/rfkill/rfkill?/state ]]; then +# test $(cat /sys/class/net/$INTERFACE/device/rfkill/rfkill?/state) -eq 1 +# return $? +# fi + return 0 # default to enabled + ;; + + associated) + # assumed to have ip address + # return 0: associated with loaded profile + # 1: associated with other + # 2: still has ip address but unassociated: can this happen? + local ESSID="$4" AP="$5" + local essid=$(iwgetid "$INTERFACE" -r) ap=$(iwgetid "$INTERFACE" -ra) + [[ -z "$ap" ]] && return 2 + if [[ -n "$AP" ]]; then + [[ $(echo "$AP" | tr 'abcdef' 'ABCDEF') == "$ap" ]] + return $? + fi + case "$ESSID" in + # [Rr][Oo][Aa][Mm]|[Oo][Pp][Ee][Nn]) + # return 0 + # ;; + *) + if [[ "$NEW" == -iproute ]]; then + # JP: ESSID is a regexp; we handle this here instead of in the wireless-dbus script for now + expr match "$essid" "^$ESSID\$" >/dev/null + return $? # returns true if matched more than 0 chars + else + [[ "$ESSID" == "$essid" ]] + return $? + fi + ;; + esac + ;; + + active|address) + "$CONN_DIR/ethernet$NEW" query "$INTERFACE" "$REQUEST" + ;; + + *) return 1;; + esac +} + +# CONNECTION_control $INTERFACE [ up | down | forcedown | enable | disable ] [ "" | "-iproute" ] +wireless_control() { + local INTERFACE="$1" ACTION="$2" NEW="$3" + #report_debug wireless_control "$@" + case "$ACTION" in + enable) + [[ $(query_iface "$INTERFACE") == disabled ]] && set_iface "$INTERFACE" down + ;; + disable) + set_iface up "$INTERFACE" disabled + ;; + up) + if [[ "$NEW" = -iproute ]]; then + "$CONN_DIR/ethernet-iproute" control "$INTERFACE" "$ACTION" + else + # JP: same as ethernet control ... up + #ifconfig "$INTERFACE" up 2>/dev/null + ifconfig "$INTERFACE" up + wireless_query "$INTERFACE" active + fi + ;; + down|forcedown) + if [[ "$NEW" = -iproute ]]; then + "$CONN_DIR/ethernet-iproute" control "$INTERFACE" "$ACTION" + else + # JP: differs slightly from ethernet control ... down (but I don't know whether the difference is really appropriate, I'm just preserving it from older codebase) + ifconfig "$INTERFACE" 0.0.0.0 + if [[ "$2" = forcedown ]]; then + ifconfig "$INTERFACE" down + fi + fi + ;; + *) return 1;; + esac +} + +wireless_verify() { + local INTERFACE="$1" + if [[ -d "/sys/class/net/$INTERFACE/phy80211" || -d "/sys/class/net/$INTERFACE/wireless" ]]; then + return 0 + else + return 1 + fi +} + +wireless_$1 "$2" "$3" "$4" "$5" "$6" exit $? # vim: set ts=4 et sw=4: + diff --git a/src/globals b/src/globals new file mode 100644 index 0000000..e227d51 --- /dev/null +++ b/src/globals @@ -0,0 +1,118 @@ +################################## +## +# /usr/lib/network/globals +## +################################## + +# JP: rather than declare these in several library files, we just declare them +# once here, so they only need to be changed at a single point + +# JP: added the /etc/network.d/hooks directory +# any +x files in that directory will be sourced when this file is sourced +# they can override any of the utility functions defined here for custom behavior +# (such as logging error messages to syslog, as I like to do) +# this lets us keep netcfg simple but gives it the flexibility for users +# to make modular use of it to do more complex things + + +### Globals +PROFILE_DIR="/etc/network.d/" +HOOKS_DIR="$PROFILE_DIR/hooks/" +SUBR_DIR="/usr/lib/network/" +CONN_DIR="${SUBR_DIR}/connections/" +STATE_DIR="/var/run/network/" + + +### Logging/Error reporting +# + +function report_err { + printhl "$*" +} + +function report_warn { + printhl "$*" +} + +function report_notify { + true +} + +function report_debug { + true +} + +function report_try { + stat_busy "$*" + REPORT_TRYING=1 +} + +function report_fail { + if [[ -n "$*" ]]; then + if [[ -n "$REPORT_TRYING" ]]; then + stat_append "- $*" + REPORT_TRYING= + stat_fail + else + printhl "$*" + fi + elif [[ -n "$REPORT_TRYING" ]]; then + REPORT_TRYING= + stat_fail + fi +} + +function report_success { + if [[ -n "$*" ]]; then + stat_append "- $*" + fi + stat_done +} + +### For calling scripts only; don't use in library functions + +function exit_stderr { echo "$*" >&2; exit 1; } +function exit_err { report_err "$*"; exit 1; } +function exit_fail { report_fail "$*"; exit 1; } + + + +function line_input { + # saves $IFS and noglob shell option (to restore them, eval the output of this function) + if [[ $(shopt -o noglob) =~ off ]]; then + printf "set +f; IFS=%q" "$IFS" + else + printf "set -f; IFS=%q" "$IFS" + fi + # now set $IFS and noglob to handle line-at-a-time input + set -f + IFS=$'\n' +} + +function load_hooks() { + ### Load any +x files in $HOOKS_DIR + + # JP: we need to process line-at-a-time input (don't want to assume path to HOOKS_DIR and its content are all single bash tokens) + # find ... | while read ... is no good because anything we do inside the while read ... subshell will be invisible to the outside calling context + +# # JP: this technique works in general (and is used elsewhere in the code) +# # <(cmd) creates a file descriptor whose content is the stdout of cmd, which we then use as stdin by saying "< <(cmd)" +# +# # JP: HOWEVER, for reasons I don't understand, this construct can't be sourced from the Python wireless-dbus script +# # Why bash can source the rest of the file but not that construct, just in that context, eludes me +# if [[ -d "$HOOKS_DIR" ]]; then +# while read h; do +# source "$h" +# done < <(find -L "$HOOKS_DIR/" -maxdepth 1 -type f -executable) +# fi + + # JP: this might be a more elegant way to process input line-at-a-time without a pipe, anyway + local h OLDIFS=$(line_input) + for h in $(find -L "$HOOKS_DIR/" -maxdepth 1 -type f -executable | sort); do + source "$h" + done + eval $OLDIFS +} + +load_hooks + diff --git a/src/net-profiles b/src/net-profiles index 67ab5e8..a4b964b 100755..100644 --- a/src/net-profiles +++ b/src/net-profiles @@ -2,17 +2,17 @@ . /etc/rc.conf . /etc/rc.d/functions +. /usr/lib/network/globals case "$1" in start) if ! ck_daemon net-profiles; then - echo "net-profiles has already been started. Try '/etc/rc.d/net-profiles restart'" - exit + exit_stderr "net-profiles is already running: try \"/etc/rc.d/net-profiles restart\"" fi # Ensure any device renaming has occurred as intended for daemon in "${DAEMONS[@]}"; do - if [ "$daemon" = "${daemon#!}" -a "$daemon" = "net-rename" ]; then + if [[ "$daemon" = "${daemon#!}" && "$daemon" = net-rename ]]; then if ck_daemon net-rename; then /etc/rc.d/net-rename start fi @@ -20,40 +20,65 @@ case "$1" in done # $NET env var is passed from the kernel boot line - [ ! "$NETWORKS_MENU_TIMEOUT" ] && NETWORKS_MENU_TIMEOUT=5 + # JP: is that true? or does $NET come from /etc/rc.conf? + [[ -z "$NETWORKS_MENU_TIMEOUT" ]] && NETWORKS_MENU_TIMEOUT=5 - if [[ "$NET" = "menu" ]]; then - /usr/bin/netcfg-menu $NETWORKS_MENU_TIMEOUT - elif [[ "$NET" ]]; then - /usr/bin/netcfg2 -c $NET - else + if [[ "$NET" = menu ]]; then + if /usr/bin/netcfg-menu "$NETWORKS_MENU_TIMEOUT"; then + mv "$STATE_DIR"/{menu,net-profiles} # JP: user may want to disconnect profile by calling net-profiles stop + add_daemon net-profiles + exit 0 + fi + elif [[ -n "$NET" ]]; then + if /usr/bin/netcfg check-iface "$NET"; then + echo "$NET" > "$STATE_DIR/net-profiles" # JP: user may want to disconnect profile by calling net-profiles stop + add_daemon net-profiles + exit 0 + fi + else # No NET= passed at boot, go to NETWORKS=() - for network in ${NETWORKS[@]}; do - case $network in + for network in "${NETWORKS[@]}"; do + case "$network" in menu) # Pull up menu of networks - /usr/bin/netcfg-menu $NETWORKS_MENU_TIMEOUT + if /usr/bin/netcfg-menu "$NETWORKS_MENU_TIMEOUT"; then + mv "$STATE_DIR"/{menu,net-profiles} # JP: user may want to disconnect profile by calling net-profiles stop + add_daemon net-profiles + exit 0 + fi + break # if netcfg-menu was called but failed: exit for loop ;; *) # Either interface or profile - if [ "$network" = "${network#!}" ]; then # otherwise profile - /usr/bin/netcfg2 -c $network + if [[ "$network" = "${network#!}" ]]; then # otherwise profile + if /usr/bin/netcfg check-iface "$network"; then + echo "$network" > "$STATE_DIR/net-profiles" # JP: user may want to disconnect profile by calling net-profiles stop + add_daemon net-profiles + exit 0 + fi + break # found an enabled profile but failed to bring it up: exit for loop fi ;; esac done - fi + fi - add_daemon net-profiles + exit_err "No profile started." # JP: don't add_daemon unless we were successful (above) ;; stop) + if ck_daemon net-profiles; then + exit_stderr "net-profiles not running" + fi + # shutdown any profiles started by netcfg (or from NET_PROFILES in rc.conf) - /usr/bin/netcfg2 -a + # JP: only attempt to disconnect the profiles we're controlling + cat "${state_DIR}/net-profiles" 2>/dev/null | xargs -d'\n' /usr/bin/netcfg down # JP: use xargs in case any of the profile names contain spaces etc + rm -f "$STATE_DIR/net-profiles" rm_daemon net-profiles ;; restart) - $0 stop; sleep 1; $0 start + "$0" stop; sleep 1; "$0" start ;; *) - echo "usage: $0 {start|stop|restart}" + exit_stderr "Usage: $0 {start|stop|restart}" esac # vim: set ts=4 et sw=4: diff --git a/src/net-rename b/src/net-rename index db2c461..1cff1c1 100755..100644 --- a/src/net-rename +++ b/src/net-rename @@ -2,12 +2,13 @@ . /etc/rc.conf . /etc/rc.d/functions +. /usr/lib/network/globals case "$1" in start) - stat_busy "Renaming network devices" + report_try "Renaming network devices" ifrename -p -t - stat_done + report_success add_daemon net-rename ;; @@ -17,9 +18,10 @@ case "$1" in /bin/true ;; restart) - $0 start + "$0" start ;; *) - echo "usage: $0 {start|stop|restart}" + exit_stderr "Usage: $0 {start|stop|restart}" + ;; esac # vim: set ts=4 et sw=4: @@ -4,7 +4,7 @@ . /etc/rc.d/functions . /usr/lib/network/network -NETCFG_VER=2.2.1 +NETCFG_VER=2.3.0b1 version() { @@ -18,45 +18,69 @@ usage() echo " Start specified profile: netcfg profile " echo " Other functions: netcfg argument profile" echo "Arguments:" + echo " current Report currently running profiles" echo "-a, all-down Take all active profiles down" echo "-c, check-iface Do not start profile if interface is already up" echo "-d, down Take specified profile down" echo "-h, help This help message" echo "-i, iface-down Take down profile active on specified interface" + echo "-l, list List all available profiles" echo "-r, reconnect Disconnect and reconnect specified profile" echo "-u, up Start specified profile" echo "-v, version Output version information and exit" echo " all-resume Resume previously suspended profiles and reconnect them" echo " all-suspend Store a list of current running profiles and suspend them" + exit 1 } -# TODO: Re-add ROOT check and rewrite with getopts from BashFAQ +# TODO: rewrite with getopts from BashFAQ -case $1 in +case "$1" in --version|-v|version) - version;; + version + exit 0;; --help|-h|help) usage;; + list|-l) + echo "Available Profiles" + echo "------------------" + list_profiles + exit 0;; + current) + if [[ -d "$STATE_DIR/profiles/" ]]; then + ls "$STATE_DIR/profiles/" + exit 0 + else + exit_err "No active profiles." + fi;; +esac + +if [[ $(id -u) -gt 0 ]]; then + exit_stderr "This script should be run as root." +fi + +case "$1" in + -c|check-iface|-u|up) - CHECK="YES"; - profile_up $2;; + CHECK=YES + profile_up "$2";; clean) - rm /var/run/network/interfaces/* 2> /dev/null - rm /var/run/network/profiles/* 2> /dev/null - rm /var/run/network/suspend/* 2> /dev/null - rm /var/run/network/last_profile 2> /dev/null + rm "$STATE_DIR/interfaces"/* 2> /dev/null + rm "$STATE_DIR/profiles"/* 2> /dev/null + rm "$STATE_DIR/suspend"/* 2> /dev/null + rm "$STATE_DIR/last_profile" 2> /dev/null killall wpa_supplicant 2> /dev/null killall dhcpcd 2> /dev/null ;; -d|down) - profile_down $2;; + profile_down "$2";; -i|iface-down) - interface_down $2;; + interface_down "$2";; -a|all-down) all_down;; -r|reconnect) - profile_down $2 - profile_up $2;; + profile_down "$2" + profile_up "$2";; all-resume) all_resume;; all-suspend) @@ -64,8 +88,8 @@ case $1 in -*|--*) usage;; *) - if [ -n "$1" ]; then - profile_up $1 + if [[ -n "$1" ]]; then + profile_up "$1" else usage fi diff --git a/src/netcfg-menu b/src/netcfg-menu index 8c58edc..70acf5a 100755..100644 --- a/src/netcfg-menu +++ b/src/netcfg-menu @@ -4,57 +4,58 @@ . /etc/rc.d/functions . /usr/lib/network/network +# JP: we'll use $STATE_DIR/menu to record what profile is being connected in this way +rm -f "$STATE_DIR/menu" # Scan all profiles i=0 -for prof in `find -L $PROFILE_DIR -maxdepth 1 -type f -printf "%f\n"|sort`; do +while read prof; do # if there is a profile called "main", Use as default - [ "$prof" = "main" ] && DEFAULT=$prof - unset DESCRIPTION - . $PROFILE_DIR/$prof - profiles[$i]=$prof - i=$((i+1)) - profiles[$i]=$DESCRIPTION - i=$((i+1)) -done - -if [ ${#profiles} -eq 0 ]; then - echo "No profiles were found in $PROFILE_DIR" - return 1 + [[ "$prof" = main ]] && DEFAULT=main + unset DESCRIPTION # JP: we cant sandbox the sourced profiles, because we need to expose the profiles array + . "$PROFILE_DIR/$prof" + profiles[$i]="$prof" + let i++ + profiles[$i]="$DESCRIPTION" + let i++ +done < <(list_profiles | sort) # JP: re-use list_profiles instead of duplicating it; avoid subshell we'd get by piping it to the while read... + +if [[ ${#profiles} -eq 0 ]]; then + exit_err "No profiles were found in $PROFILE_DIR" fi +[[ -n "$NETWORKS_MENU_DEFAULT" ]] && DEFAULT="$NETWORKS_MENU_DEFAULT" # if no default yet, use the first entry -[ "$NETWORKS_MENU_DEFAULT" ] && DEFAULT="$NETWORKS_MENU_DEFAULT" - -[ "$DEFAULT" = "" ] && DEFAULT=${profiles[0]} -ANSWER=$(mktemp) || exit 1 +[[ -z "$DEFAULT" ]] && DEFAULT="${profiles[0]}" +ANSWER=$(mktemp /tmp/menu.XXXXXXXX) || exit 1 # Set timeout -if [ "$1" = "" ]; then - TIMEOUT="0" -else - TIMEOUT="$1" -fi +TIMEOUT="${1:-0}" # JP: equivalent to the block that was here before # Display Dialog -dialog --timeout $TIMEOUT --default-item $DEFAULT \ +dialog --timeout "$TIMEOUT" --default-item "$DEFAULT" \ --menu "Select the network profile you wish to use" \ - 13 50 6 "${profiles[@]}" 2>$ANSWER + 13 50 6 "${profiles[@]}" 2> $ANSWER ret=$? case $ret in 1) ;; # Cancel - do nothing 255) # timeout - use default - netcfg2 $DEFAULT + profile_up "$DEFAULT" # JP: use profile_up and catch $? + ret=$? + if [[ $ret -eq 0 ]]; then echo "$DEFAULT" > "$STATE_DIR/menu"; fi ;; 0) # User selection - netcfg2 $(cat $ANSWER) + profile_up "$(cat $ANSWER)" + ret=$? + if [[ $ret -eq 0 ]]; then mv $ANSWER "$STATE_DIR/menu"; fi ;; - *) # Shouldnt happen - echo "Abnormal ret code from dialog: $ret" + *) # Shouldn't happen + echo "Abnormal ret code from dialog: $ret" >&2 ;; esac -rm $ANSWER +rm -f $ANSWER # JP: add -f +exit $ret # JP: exit with caught $? # vim: set ts=4 et sw=4: diff --git a/src/network b/src/network index b45caaa..2720caa 100644 --- a/src/network +++ b/src/network @@ -1,272 +1,391 @@ -### Globals -PROFILE_DIR="/etc/network.d/" -SUBR_DIR="/usr/lib/network/" -CONN_DIR="${SUBR_DIR}/connections/" -STATE_DIR="/var/run/network/" - -### Messages +################################## ## -# err msg -# output specified message -err_append() { - echo " - $*" -} +# /usr/lib/network/network +## +################################## + +. /usr/lib/network/globals -err() { - printhl "$*" -} -### Profile loading +### Profile listing and loading ## +# list_profiles +# Outputs a list of all profiles +list_profiles() { + # JP: follow aliases with -L, also skip profiles that start with '.' or end with '~' or '.conf' (so profile.conf can be the wpa.conf file for profile) + find -L "$PROFILE_DIR/" -maxdepth 1 -type f -not -name '*~' -not -name '*.conf' -not -name '.*' -printf "%f\n" +} # load_profile profile # source the profile load_profile() { - validate_profile $1 || return 1 - . $PROFILE_DIR/$1 + validate_profile "$1" || return 1 + . "$PROFILE_DIR/$1" } # validate_profile profile # check whether profile exists and is usable -validate_profile() -{ +validate_profile() { [[ -z "$1" ]] && return 1 - if [[ ! -f $PROFILE_DIR/$1 ]]; then - err "Profile \"$1\" does not exist" + if [[ ! -f "$PROFILE_DIR/$1" ]]; then + report_err "Profile \"$1\" does not exist." return 1 fi - . $PROFILE_DIR/$1 + . "$PROFILE_DIR/$1" if [[ -z "$INTERFACE" ]]; then - err "Profile missing an interface to configure" + report_err "Profile error: no INTERFACE to configure." return 1 fi - if [[ ! -f $CONN_DIR/$CONNECTION ]]; then - err "$CONNECTION is not a valid connection, check spelling or look at examples" + if [[ ! -f "$CONN_DIR/$CONNECTION" ]]; then + report_err "Profile error: $CONNECTION is not a valid connection, check spelling or look at examples." return 1 fi } -### Profile up/down +### Profile suspend/resume ## -# all_down -# take all profiles down -# -all_down() -{ - for prof in $(find ${STATE_DIR}/profiles/ -maxdepth 1 -type f -printf '%f\n'); do - profile_down $prof +# interface_suspend interface/all [call_profile_down? default=yes] +# store a list of running profiles and take them down (unless $2 is no) +interface_suspend() { + report_debug interface_suspend "$@" + + [[ ! -d "$STATE_DIR" ]] && mkdir -p "$STATE_DIR"/{interfaces,profiles} + [[ ! -d "$STATE_DIR/suspend" ]] && mkdir "$STATE_DIR/suspend" + + find "$STATE_DIR/profiles/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + # the pipe to "while read" will create a subshell, so sourced variables will already be in a sandbox + # we just need to clear INTERFACE which is all we care about + unset INTERFACE + . "$STATE_DIR/profiles/$prof" + if [[ "$1" == all || "$1" == "$INTERFACE" ]]; then + report_notify "suspending interface $INTERFACE with profile $prof" + cp "$STATE_DIR/profiles/$prof" "$STATE_DIR/suspend/" + if checkyesno "${2:-yes}"; then + profile_down "$prof" + fi + fi done } - # all_suspend # store a list of running profiles and take them down -# -all_suspend() -{ - [[ ! -d $STATE_DIR ]] && mkdir -p $STATE_DIR/{interfaces,profiles} - [[ ! -d $STATE_DIR/suspend ]] && mkdir $STATE_DIR/suspend - - for prof in $(find $STATE_DIR/profiles/ -maxdepth 1 -type f); do - cp $prof $STATE_DIR/suspend/ - profile_down $(basename $prof) - done +all_suspend() { + interface_suspend all } +# all_resume +# resume suspended interfaces +# optional arguments: interfaces not to resume (e.g., because they're disabled) +all_resume() { + report_debug all_resume "$@" -# all_suspend -# store a list of running profiles and take them down -# -all_resume() -{ - for prof in $(find $STATE_DIR/suspend/ -maxdepth 1 -type f); do - profile_up $(basename $prof) - rm $prof + find "$STATE_DIR/suspend/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + # the pipe to "while read" will create a subshell, so sourced variables will already be in a sandbox + # we just need to clear INTERFACE which is all we care about + unset INTERFACE + . "$STATE_DIR/suspend/$prof" + if [[ $# -eq 0 || ! " $* " =~ " $INTERFACE " ]]; then + report_notify "resuming interface $INTERFACE with profile $prof" + profile_up "$prof" + rm -f "$STATE_DIR/suspend/$prof" # if profile_up succeeds, it will have already removed this + fi done } -# profile_up profile +### Profile up/down +## +# profile_up profile [literal essid] # put all profiles up # -profile_up() -{ +profile_up() { ( # Keep inside subshell so that options from one profile don't cross to others - # exit 1 used as a subshell is effectively a new process - [[ ! -d $STATE_DIR ]] && mkdir -p $STATE_DIR/{interfaces,profiles} + # exit 1 used in a subshell is effectively exiting a new process + [[ ! -d "$STATE_DIR" ]] && mkdir -p "$STATE_DIR"/{interfaces,profiles,suspend} + + local status PROFILE="$1" # save PROFILE in a variable so that it's available to PREUP/POSTDOWN etc hooks - load_profile $1 || exit 1 + load_profile "$PROFILE" || exit 1 - check_profile $1 && err "$1 already connected" && exit 1 + if check_profile "$PROFILE"; then + report_err "$PROFILE already connected." + exit 1 + fi # NETWORKS_EXCLUSIVE, rc.conf: Profiles are globally mutually exclusive # EXCLUSIVE, network.d/profile: Individual profile is mutually exclusive - if checkyesno $NETWORKS_EXCLUSIVE || checkyesno $EXCLUSIVE; then + if checkyesno "$NETWORKS_EXCLUSIVE" || checkyesno "$EXCLUSIVE"; then all_down fi - stat_busy "$1 up" + report_try "$PROFILE up" - if check_iface $INTERFACE; then - if checkyesno $CHECK; then - err_append "Interface $INTERFACE already in use" - stat_fail && exit 1 - else - interface_down $INTERFACE || exit 1 - load_profile $1 - fi - fi + status=$(query_iface "$INTERFACE" "$CONNECTION") + report_debug "status reported to profile_up as: $status" + + if [[ -n "$status" && "$status" != "$PROFILE" ]]; then + case "$status" in + disabled) + # interface radio-switch disabled + report_fail "INTERFACE $INTERFACE is disabled." + exit 1 + ;; + external) + # interface being controlled externally + report_fail "INTERFACE $INTERFACE was configured by another application." + exit 1 + ;; + *) + # report_fail if iface in use by another profile? (default no) + if checkyesno "$CHECK"; then + report_fail "INTERFACE $INTERFACE already in use by another profile." + exit 1 + elif ! ( # use subshell so interface_down -> profile_down doesn't overwrite loaded profile + interface_down "$INTERFACE" "$CONNECTION" # supply current CONNECTION-type as hint + ); then + report_fail + exit 1 + fi + ;; + esac + fi - eval $PRE_UP || exit 1 + + if ! ( eval $PRE_UP ); then # JP: sandbox the eval so variables don't bleed into current function + report_debug profile_up "PRE_UP failed" + report_fail + exit 1 + fi - if ! ${CONN_DIR}/${CONNECTION} up $1; then - stat_fail + if ! "$CONN_DIR/$CONNECTION" up "$PROFILE" "$2"; then + report_debug profile_up "connect failed" + report_fail + # "$CONN_DIR/$CONNECTION" down "$PROFILE" "$2" # JP: should we do this to make sure? exit 1 fi - eval $POST_UP || exit 1 + if ! ( eval $POST_UP ); then # JP: sandbox the eval + report_debug profile_up "POST_UP failed" + report_fail + # failing POST_UP will take interface down + "$CONN_DIR/$CONNECTION" down "$PROFILE" "$2" + exit 1 + fi - set_profile up $1 - unset EXCLUSIVE - - stat_done + set_profile up "$PROFILE" + # unset EXCLUSIVE + report_success ); return $? } - # profile_down profile # take profile down # -profile_down() -{ +profile_down() { ( - [[ ! -d $STATE_DIR ]] && mkdir -p $STATE_DIR/{interfaces,profiles} + [[ ! -d "$STATE_DIR" ]] && mkdir -p "$STATE_DIR"/{interfaces,profiles,suspend} - load_profile $1 || exit 1 + local status PROFILE="$1" # save PROFILE in a variable so that it's available to PREUP/POSTDOWN etc hooks + + load_profile "$PROFILE" || exit 1 + + status=$(query_iface "$INTERFACE" "$CONNECTION") + report_debug "status reported to profile_down as: $status" - if ! check_profile $1; then - err "Profile not connected" - exit 1 - fi + if [[ "$status" != "$PROFILE" ]]; then + # if interface not available to be controlled by netcfg, then + # any profiles will have been removed by check_iface + # else another profile is running + report_err "Profile wasn't connected." + exit 1 + fi - stat_busy "$1 down" - if [[ "$(get_iface_prof $INTERFACE)" == "external" ]]; then - err_append "$interface was connected by another application" - stat_fail - exit 1 - fi + report_try "$PROFILE down" +# JP: the following will have already been caught by the block above... +# if [[ "$status" == external ]]; then +# report_fail "Interface $INTERFACE was configured by another application." +# exit 1 +# fi - eval $PRE_DOWN || exit 1 + if ! ( eval $PRE_DOWN ); then # JP: sandbox the eval + report_debug profile_down "PRE_DOWN failed" + true # JP: did we want failing PRE_DOWN to leave the profile active? + # report_fail + # exit 1 + fi - if ! ${CONN_DIR}/${CONNECTION} down $1; then - stat_fail + if ! "$CONN_DIR/$CONNECTION" down "$PROFILE" "$2"; then + report_debug profile_up "disconnect failed" + report_fail exit 1 fi - eval $POST_DOWN || exit 1 + if ! ( eval $POST_DOWN ); then # JP: sandbox the eval + report_debug profile_down "POST_DOWN failed" + report_fail + exit 1 + fi - set_profile down $1 - stat_done + set_profile down "$PROFILE" + report_success ); return $? } - -# Check if variable is a member of an array -inarray() -{ -search=$1 -shift -for item in $*; do - if [[ "$item" == "$search" ]]; then - return 0 - fi -done -return 1 +# all_down +# take all registered profiles down +all_down() { + find "$STATE_DIR/profiles/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + profile_down "$prof" + done +} +# interface_down interface +# take interface down +# +interface_down() { + local status=$(query_iface "$@") # we may not be hinting what CONNECTION type to use, e.g. when called from /usr/bin/netcfg + case "$status" in + none|disabled) return 0;; + external) return 1;; + *) profile_down "$status";; + esac } -quirk() { -inarray $1 ${QUIRKS[@]} -return $? + +### Status setting functions +## +# set_profile up/down profile +# Set profile state, either up or down +# +set_profile() { + if [[ "$1" == up ]]; then + ( # subshell creates sandbox for sourced variables + . "$PROFILE_DIR/$2" + cp "$PROFILE_DIR/$2" "$STATE_DIR/profiles/" + echo "$2" > "$STATE_DIR/last_profile" + set_iface up "$INTERFACE" "$2" + ) + elif [[ "$1" == down && -f "$STATE_DIR/profiles/$2" ]]; then + ( # subshell + . "$STATE_DIR/profiles/$2" + rm "$STATE_DIR/profiles/$2" + set_iface down "$INTERFACE" "$2" + ) + fi } -# interface_down interface -# take interface down +# set_iface up/down interface [profile/disabled/external] +# Set interface status to up/down (really up means "unavailable" (may be down & disabled) and down means "available" +# optionally link it to a profile. # -interface_down() -{ - local prof=$(get_iface_prof $1) - profile_down $prof - return $? +set_iface() { + local PROFILE="${3:-external}" # JP: this is equivalent to the two-line block that was here before + if [[ "$1" == up ]]; then + echo "PROFILE=$PROFILE" > "$STATE_DIR/interfaces/$2" + elif [[ "$1" == down ]]; then + rm -f "$STATE_DIR/interfaces/$2" # JP: add -f so we don't complain if the interface isn't up + fi } -### Query functions + + +### Checking/querying functions ## # check_iface interface # Return 0 if interface up # Return 1 if interface down # check_iface() { - [[ -f $STATE_DIR/interfaces/$1 ]] && return 0 - return 1 + [[ -f "$STATE_DIR/interfaces/$1" ]] && return 0 + return 1 } +############################################################################ +# Note: get_iface_prof left in for compatibility with any external code that calls it +# but in this codebase, it's superceded by query_iface # get_iface_prof interface # Echo interface profile and return 0 if up # Return 1 if down. # get_iface_prof() { - if check_iface $1; then - . $STATE_DIR/interfaces/$1 + if check_iface "$1"; then + . "$STATE_DIR/interfaces/$1" echo $PROFILE else return 1 fi } - -# list_profiles -# Outputs a list of all profiles -list_profiles() { - find $PROFILE_DIR/ -maxdepth 1 -type f -printf "%f\n" -} +############################################################################ # check_profile profile -# Return 0 if profile up -# Return 1 if profile down +# Return 0 if profile registered as being up +# Return 1 if profile not registered # check_profile() { - [[ -f $STATE_DIR/profiles/$1 ]] && return 0 + [[ -f "$STATE_DIR/profiles/$1" && ! -f "$STATE_DIR/suspend/$1" ]] && return 0 return 1 -} +} + + +# query_iface interface [connection] +# Return 0 if interface up or otherwise unavailable for connecting +# Return 1 if interface down (and so free to be connected) +# echoes which profile controls the interface (or "external" or "disabled") + +query_iface() { + + local INTERFACE="$1" PROFILE connection="$2" + + if [[ -z "$connection" ]]; then # JP: the difficulty with factoring this code into the CONNECTION routines + # is that we sometimes want to query/control the interface without knowing what CONNECTION type it instantiates + # so we supply the CONNECTION type as a hint ($2) when possible + # and when CONNECTION wasn't hinted, we default to wireless-dbus if iface verifies as wireless, else ethernet-iproute + if "$CONN_DIR/wireless-dbus" verify "$INTERFACE"; then + connection=wireless-dbus + else + connection=ethernet-iproute + fi + fi + + if ! "$CONN_DIR/$connection" query "$INTERFACE" active; then + # interface is not up + if [[ -f "$STATE_DIR/interfaces/$INTERFACE" ]]; then # but the interface thinks it's running a profile... + . "$STATE_DIR/interfaces/$INTERFACE" # these files should only contain a PROFILE=... statement + if [[ "$PROFILE" == disabled ]]; then + echo "disabled" + return 0 + elif [[ "$PROFILE" != external ]]; then + report_warn "INTERFACE $INTERFACE no longer being controlled by $PROFILE" + set_profile down "$PROFILE" + PROFILE= + fi + set_iface down "$INTERFACE" # update iface status: no longer profile/external + fi + echo "$PROFILE" + return 1 + else + "$CONN_DIR/$connection" query "$INTERFACE" profile + return $? + fi -### Status setting functions -## -# set_profile up/down profile -# Set profile state, either up or down -# -set_profile() { - if [[ "$1" == "up" ]]; then - . $PROFILE_DIR/$2 - cp $PROFILE_DIR/$2 $STATE_DIR/profiles/ - echo $2 > $STATE_DIR/last_profile - set_iface up $INTERFACE $2 - - elif [[ "$1" == "down" ]]; then - . $STATE_DIR/profiles/$2 - rm $STATE_DIR/profiles/$2 - set_iface down $INTERFACE $2 - fi } -# set_iface up/down interface [profile] -# Set interface status to up/down -# optionally link it to a profile. -# -set_iface() { - PROFILE=$3 - [[ -z "$PROFILE" ]] && PROFILE=external - if [[ "$1" == "up" ]]; then - echo "PROFILE=$PROFILE" > $STATE_DIR/interfaces/$2 - elif [[ "$1" == "down" ]]; then - rm $STATE_DIR/interfaces/$2 - fi + +### Utility functions +## +# Check if variable is a member of an array +inarray() { + local item search="$1" + shift + for item; do + if [[ "$item" == "$search" ]]; then + return 0 + fi + done + return 1 } +quirk() { + inarray "$1" "${QUIRKS[@]}" +} ### From FreeBSD's /etc/rc.subr ## @@ -274,11 +393,10 @@ set_iface() { # Test $1 variable, and warn if not set to YES or NO. # Return 0 if it's "yes" (et al), nonzero otherwise. # -checkyesno() -{ - _value=${1} +checkyesno() { + local _value="$1" #debug "checkyesno: $1 is set to $_value." - case $_value in + case "$_value" in # "yes", "true", "on", or "1" [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) @@ -295,3 +413,5 @@ checkyesno() ;; esac } + + diff --git a/src/wireless b/src/wireless index 62454e4..d27c43b 100644 --- a/src/wireless +++ b/src/wireless @@ -1,62 +1,141 @@ +################################## +## +# /usr/lib/network/wireless_utils +## +################################## + # Uses wireless_tools, to check for association to a network. # wep_check interface [timeout] -wep_check() -{ - INTERFACE=$1; TIMEOUT=$2 +wep_check() { + local INTERFACE="$1" TIMEOUT="${2:-15}" timeout=0 bssid - [[ -z "$TIMEOUT" ]] && TIMEOUT=15 - let timeout=0 - while [[ $timeout -ne $TIMEOUT ]]; do - bssid=`iwgetid $INTERFACE -ra` - [[ ! "$bssid" = "00:00:00:00:00:00" ]] && return 0 + while [[ "$timeout" -lt "$TIMEOUT" ]]; do + bssid=$(iwgetid "$INTERFACE" -ra) + [[ -n "$bssid" && "$bssid" != "00:00:00:00:00:00" ]] && return 0 sleep 1 let timeout++ done - err_append "Wireless association failed" + report_fail "Couldn't associate with wireless network." return 1 } # Check if a particular network is within range -# find_essid interface essid -find_essid() -{ - INTERFACE=$1; ESSID=$2; RETRIES=5 - try=0; - while [[ $try -ne $RETRIES ]]; do - if iwlist $INTERFACE scan|sed "s/ESSID://g"|grep -q "\"$ESSID\""; then +# find_essid interface essid connection (we treat ESSID as regexp when CONNECTION=wireless-dbus) +find_essid() { + local INTERFACE="$1" ESSID="$2" CONNECTION="$3" RETRIES=20 try=0 res scanned + while [[ "$try" -lt "$RETRIES" ]]; do + sleep 0.5 + let try++ + if [[ "$CONNECTION" == wireless-dbus ]]; then + # JP: ESSID is a regexp + found=$( + res=$(iwlist "$INTERFACE" scan 2>/dev/null) + [[ -z "$res" ]] && exit 1 + # if results were non-null, process them and exit 0 + echo "$res" | sed -nr 's/^\s+ESSID:"([^"]*)"$/\1/p' | egrep -xm1 "$ESSID" + ) + else + found=$( + res=$(iwlist "$INTERFACE" scan 2>/dev/null) + [[ -z "$res" ]] && exit 1 + # if results were non-null, process them and exit 0 + echo "$res" | sed -nr 's/^\s+ESSID:"([^"]*)"$/\1/p' | fgrep -xm1 "$ESSID" + ) + fi && { + scanned=1 + report_debug find_essid "\"$found\"" + # we only bother with at most 5 successful scans + if (( try < RETRIES-4 )); then try=$((RETRIES-4)); fi + } + if [[ -n "$found" ]]; then + echo "$found" # JP: echo literal ESSID return 0 # network found fi - sleep 1 + done + if [[ "$scanned" -ne 1 ]]; then + report_debug find_essid "unable to scan" + fi + return 1 +} + +# Check if a particular network is within range +# find_ap interface ap +find_ap() { + local INTERFACE="$1" ap=$(echo "$2" | tr 'abcdef' 'ABCDEF') RETRIES=20 try=0 res scanned + while [[ "$try" -lt "$RETRIES" ]]; do + sleep 0.5 let try++ + found=$( + res=$(iwlist "$INTERFACE" scan 2> /dev/null) + [[ -z "$res" ]] && exit 1 + # if results were non-null, process them and exit 0 + echo "$res" | sed -nr '/^\s+Cell .. - Address: ([[:xdigit:]:]+)$/ { s//\1/; N; s/(.*)\n\s+ESSID:"([^"]*)"$/\1\t\2/p }' \ + | egrep -m1 "^$ap\t" + ) && { + scanned=1 + report_debug find_ap "\"$found\"" + # we only bother with at most 5 successful scans + if (( try < RETRIES-4 )); then try=$((RETRIES-4)); fi + } + if [[ -n "$found" ]]; then + echo "$found" | cut -f2 # JP: echo literal ESSID + return 0 + fi done + if [[ "$scanned" -ne 1 ]]; then + report_debug find_ap "unable to scan" + fi return 1 } -# Return a filename containing a list of network ESSID's found. +# Return a filename containing a list of network APs and ESSIDs found (sorted by decreasing signal strength) # list_networks interface -list_networks() -{ +list_networks() { + local INTERFACE="$1" essids try=0 RETRIES=20 res scanned # temp file used, as keeping ESSID's with spaces in their name in arrays # is hard, obscure and kinda nasty. This is simpler and clearer. - [[ -z "$1" ]] && return 1 - essids=$(mktemp /tmp/essid.XXXXX) - - let try=0; - RETRIES=6; - while [[ $try -ne $RETRIES ]]; do - iwlist $1 scan 2> /dev/null|grep ESSID|sed 's/.*ESSID:"\([^"]\+\)".*/\1/' > $essids - sleep 0.5; let try++ - done - sort -u $essids -o $essids - + [[ -z "$INTERFACE" ]] && return 1 + essids=$(mktemp /tmp/essid.XXXXXXXX) + +# # James suggested using this, but it requires wpa_supplicant to be running +# wpa_cli -i "$INTERFACE" scan 2>/dev/null || { rm $essids; return 1; } +# sleep 0.5 +# wpa_cli -i "$INTERFACE" scan_results > $essids 2>/dev/null || { rm $essids; return 1; } + + { + while [[ "$try" -lt "$RETRIES" ]]; do + sleep 0.5 + let try++ + # iwlist "$INTERFACE" scan 2> /dev/null | fgrep "ESSID" | sed 's/.*ESSID:"\([^"]\+\)".*/\1/' > $essids + res=$(iwlist "$INTERFACE" scan 2> /dev/null) + [[ -z "$res" ]] && continue + scanned=1 + # we only bother with at most 5 successful scans + if (( try < RETRIES-4 )); then try=$((RETRIES-4)); fi + echo "$res" | sed -r '1d; $ { H; ba }; 2 { h; d }; /^\s+Cell /! { H; d }; :a; x; s/\n/ /g' + done + } \ + | sed -rne 's/.*Address: ([[:xdigit:]:]+).*ESSID:"([^"]*)".*Quality=([0-9]+).*/\1\t\3\t\1\t\2/p' \ + -e 's/.*Address: ([[:xdigit:]:]+).*Quality=([0-9]+).*ESSID:"([^"]*)".*/\1\t\2\t\1\t\3/p' \ + | sort -k1 -k2nr | uniq -w17 \ + | sort -k2nr \ + | cut -f3- > "$essids" + # 1. make tab-separated fields: ap, signal-strength, ap, essid (easiest way to use uniq and cut here requires ap twice) + # 2. eliminate duplicate aps (keeping strongest signal) + # 3. sort entire list by decreasing signal + # 4. then return tab-separated fields: ap, essid (ap needs to come first so that read can assume format is <word> <rest of line>) +# # File of 0 length, ie. no ssid's. if [[ ! -s $essids ]]; then + rm $essids return 1 fi echo $essids return 0 } + + # vim: set ts=4 et sw=4 ft=sh: |