diff options
author | rnhmjoj <rnhmjoj@inventati.org> | 2021-06-20 21:20:37 +0200 |
---|---|---|
committer | rnhmjoj <rnhmjoj@inventati.org> | 2021-08-12 00:31:06 +0200 |
commit | 99e8af51b28f9e978e8137a6c4ca16d817c64d40 (patch) | |
tree | 963ce486e7ab72f855813c98174af383f3c1001d | |
parent | dc54724bd66bacbc0b050b316e9aed6bcec2e43d (diff) | |
download | nixpkgs-99e8af51b28f9e978e8137a6c4ca16d817c64d40.tar nixpkgs-99e8af51b28f9e978e8137a6c4ca16d817c64d40.tar.gz nixpkgs-99e8af51b28f9e978e8137a6c4ca16d817c64d40.tar.bz2 nixpkgs-99e8af51b28f9e978e8137a6c4ca16d817c64d40.tar.lz nixpkgs-99e8af51b28f9e978e8137a6c4ca16d817c64d40.tar.xz nixpkgs-99e8af51b28f9e978e8137a6c4ca16d817c64d40.tar.zst nixpkgs-99e8af51b28f9e978e8137a6c4ca16d817c64d40.zip |
nixos/wireless: use udev to wait for interfaces
I may have finally found a clean solution to the issues[1][2][3] with the automatic discovery of wireless network interfaces. [1]: https://github.com/NixOS/nixpkgs/issues/101963 [2]: https://github.com/NixOS/nixpkgs/issues/23196 [3]: https://github.com/NixOS/nixpkgs/pull/125917#issuecomment-856000426 Currently the start script fails right away if no interface is available by the time it's running, possibly leaving the system without network. This happens when running a little early in the boot. A solution is to instead wait for at least one interface to appear before scanning the /sys/class/net/ directory. This is done here by listening for the right udev events (from the net/wlan subsystem) using the `udevadm monitor` command and grep to match its output. This methods guarantees the availability of at least one interface to wpa_supplicant, but won't add additional interfaces once it has started. However, if the current interface is lost, say unplugged, the service is automatically stopped and will be restarted as soon as a one (not necessarily the same) is detected. It would be possible make this fully dynamic by running another service that continously listen for udev events and manages the main wpa_supplicant daemon, but this is probably overkill. I tested the following cases: - one interface, starting at boot, w/o predictable naming scheme - two interfaces, starting at boot (intel wireless and a usb adapter), w/o predictable naming scheme - one interface after the system booted, w/o predictable naming scheme - two interfaces after the system booted, w/o predictable naming scheme - unplugging and plugging back the current interface
-rw-r--r-- | nixos/modules/services/networking/wpa_supplicant.nix | 51 |
1 files changed, 26 insertions, 25 deletions
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix index c0a4ce40760..49790da0e3c 100644 --- a/nixos/modules/services/networking/wpa_supplicant.nix +++ b/nixos/modules/services/networking/wpa_supplicant.nix @@ -42,11 +42,6 @@ in { description = '' The interfaces <command>wpa_supplicant</command> will use. If empty, it will automatically use all wireless interfaces. - <warning><para> - The automatic discovery of interfaces does not work reliably on boot: - it may fail and leave the system without network. When possible, specify - a known interface name. - </para></warning> ''; }; @@ -230,14 +225,6 @@ in { message = ''options networking.wireless."${name}".{psk,pskRaw,auth} are mutually exclusive''; }); - warnings = - optional (cfg.interfaces == [] && config.systemd.services.wpa_supplicant.wantedBy != []) - '' - No network interfaces for wpa_supplicant have been configured: the service - may randomly fail to start at boot. You should specify at least one using the option - networking.wireless.interfaces. - ''; - environment.systemPackages = [ package ]; services.dbus.packages = [ package ]; @@ -257,31 +244,45 @@ in { wantedBy = [ "multi-user.target" ]; stopIfChanged = false; - path = [ package ]; + path = [ package pkgs.udev ]; script = let configStr = if cfg.allowAuxiliaryImperativeNetworks then "-c /etc/wpa_supplicant.conf -I ${configFile}" else "-c ${configFile}"; in '' - if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ] - then echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead." + if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]; then + echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead." fi + iface_args="-s -u -D${cfg.driver} ${configStr}" + ${if ifaces == [] then '' - for i in $(cd /sys/class/net && echo *); do - DEVTYPE= - UEVENT_PATH=/sys/class/net/$i/uevent - if [ -e "$UEVENT_PATH" ]; then - source "$UEVENT_PATH" - if [ "$DEVTYPE" = "wlan" -o -e /sys/class/net/$i/wireless ]; then - args+="''${args:+ -N} -i$i $iface_args" - fi - fi + # detect interfaces automatically + + # check if there are no wireless interface + if ! find -H /sys/class/net/* -name wireless | grep -q .; then + # if so, wait until one appears + echo "Waiting for wireless interfaces" + grep -q '^ACTION=add' < <(stdbuf -oL -- udevadm monitor -s net/wlan -pu) + # Note: the above line has been carefully written: + # 1. The process substitution avoids udevadm hanging (after grep has quit) + # until it tries to write to the pipe again. Not even pipefail works here. + # 2. stdbuf is needed because udevadm output is buffered by default and grep + # may hang until more udev events enter the pipe. + fi + + # add any interface found to the daemon arguments + for name in $(find -H /sys/class/net/* -name wireless | cut -d/ -f 5); do + echo "Adding interface $name" + args+="''${args:+ -N} -i$name $iface_args" done '' else '' + # add known interfaces to the daemon arguments args="${concatMapStringsSep " -N " (i: "-i${i} $iface_args") ifaces}" ''} + + # finally start daemon exec wpa_supplicant $args ''; }; |