{ config, lib, pkgs, utils, ... }:
# All hope abandon ye who enter here. hostapd's configuration
# format is ... special, and you won't be able to infer any
# of their assumptions from just reading the "documentation"
# (i.e. the example config). Assume footguns at all points -
# to make informed decisions you will probably need to look
# at hostapd's code. You have been warned, proceed with care.
let
inherit
(lib)
attrNames
attrValues
concatLists
concatMap
concatMapStrings
concatStringsSep
count
escapeShellArg
filter
flip
generators
getAttr
hasPrefix
imap0
isInt
isString
length
literalExpression
maintainers
mapAttrsToList
mdDoc
mkDefault
mkEnableOption
mkIf
mkOption
mkPackageOption
mkRemovedOptionModule
optional
optionalAttrs
optionalString
optionals
singleton
stringLength
toLower
types
unique
;
cfg = config.services.hostapd;
extraSettingsFormat = {
type = let
singleAtom = types.oneOf [ types.bool types.int types.str ];
atom = types.either singleAtom (types.listOf singleAtom) // {
description = "atom (bool, int or string) or a list of them for duplicate keys";
};
in types.attrsOf atom;
generate = name: value: pkgs.writeText name (generators.toKeyValue {
listsAsDuplicateKeys = true;
mkKeyValue = generators.mkKeyValueDefault {
mkValueString = v:
if isInt v then toString v
else if isString v then v
else if true == v then "1"
else if false == v then "0"
else throw "unsupported type ${builtins.typeOf v}: ${(generators.toPretty {}) v}";
} "=";
} value);
};
# Generates the header for a single BSS (i.e. WiFi network)
writeBssHeader = radio: bss: bssIdx: pkgs.writeText "hostapd-radio-${radio}-bss-${bss}.conf" ''
''\n''\n# BSS ${toString bssIdx}: ${bss}
################################
${if bssIdx == 0 then "interface" else "bss"}=${bss}
'';
makeRadioRuntimeFiles = radio: radioCfg:
pkgs.writeShellScript "make-hostapd-${radio}-files" (''
set -euo pipefail
hostapd_config_file=/run/hostapd/${escapeShellArg radio}.hostapd.conf
rm -f "$hostapd_config_file"
cat > "$hostapd_config_file" <<EOF
# Radio base configuration: ${radio}
################################
EOF
cat ${escapeShellArg (extraSettingsFormat.generate "hostapd-radio-${radio}-extra.conf" radioCfg.settings)} >> "$hostapd_config_file"
${concatMapStrings (script: "${script} \"$hostapd_config_file\"\n") (attrValues radioCfg.dynamicConfigScripts)}
''
+ concatMapStrings (x: "${x}\n") (imap0 (i: f: f i)
(mapAttrsToList (bss: bssCfg: bssIdx: ''
''\n# BSS configuration: ${bss}
mac_allow_file=/run/hostapd/${escapeShellArg bss}.mac.allow
rm -f "$mac_allow_file"
touch "$mac_allow_file"
mac_deny_file=/run/hostapd/${escapeShellArg bss}.mac.deny
rm -f "$mac_deny_file"
touch "$mac_deny_file"
cat