diff options
Diffstat (limited to 'tv/3modules')
-rw-r--r-- | tv/3modules/default.nix | 20 | ||||
-rw-r--r-- | tv/3modules/iptables.nix | 33 | ||||
-rw-r--r-- | tv/3modules/lidControl.nix | 45 | ||||
-rw-r--r-- | tv/3modules/systemd.nix | 47 | ||||
-rw-r--r-- | tv/3modules/wwan.nix | 181 |
5 files changed, 312 insertions, 14 deletions
diff --git a/tv/3modules/default.nix b/tv/3modules/default.nix index b6b4faa51..1a0971ec6 100644 --- a/tv/3modules/default.nix +++ b/tv/3modules/default.nix @@ -1,16 +1,8 @@ +with import ./lib; { - imports = [ - ./charybdis - ./dnsmasq.nix - ./ejabberd - ./focus.nix - ./hosts.nix - ./hw.nix - ./im.nix - ./iptables.nix - ./org.freedesktop.machine1.host-shell.nix - ./slock.nix - ./x0vncserver.nix - ./Xresources.nix - ]; + imports = + map + (name: ./. + "/${name}") + (attrNames + (filterAttrs isNixDirEntry (readDir ./.))); } diff --git a/tv/3modules/iptables.nix b/tv/3modules/iptables.nix index c4bf4644d..5b36c5acb 100644 --- a/tv/3modules/iptables.nix +++ b/tv/3modules/iptables.nix @@ -34,6 +34,10 @@ with import ./lib; type = with types; listOf str; default = []; }; + filter.Wiregrill = mkOption { + type = with types; listOf str; + default = []; + }; }; }; }; @@ -66,6 +70,16 @@ with import ./lib; default = []; }; + input-wiregrill-accept-tcp = mkOption { + type = with types; listOf (either int str); + default = []; + }; + + input-wiregrill-accept-udp = mkOption { + type = with types; listOf (either int str); + default = []; + }; + extra = mkOption { default = {}; type = extraTypes.rules; @@ -141,6 +155,7 @@ with import ./lib; :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] :Retiolum - [0:0] + :Wiregrill - [0:0] ${concatMapStringsSep "\n" (rule: "-A INPUT ${rule}") ([] ++ [ "-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT" @@ -150,6 +165,7 @@ with import ./lib; ++ map accept-tcp (unique (map toString cfg.input-internet-accept-tcp)) ++ map accept-udp (unique (map toString cfg.input-internet-accept-udp)) ++ ["-i retiolum -j Retiolum"] + ++ ["-i wiregrill -j Wiregrill"] )} ${formatTable cfg.extra.filter} ${formatTable cfg."extra${toString iptables-version}".filter} @@ -170,6 +186,23 @@ with import ./lib; ]; }."ip${toString iptables-version}tables" )} + ${concatMapStringsSep "\n" (rule: "-A Wiregrill ${rule}") ([] + ++ optional (cfg.accept-echo-request == "wiregrill") accept-echo-request + ++ map accept-tcp (unique (map toString cfg.input-wiregrill-accept-tcp)) + ++ map accept-udp (unique (map toString cfg.input-wiregrill-accept-udp)) + ++ { + ip4tables = [ + "-p tcp -j REJECT --reject-with tcp-reset" + "-p udp -j REJECT --reject-with icmp-port-unreachable" + "-j REJECT --reject-with icmp-proto-unreachable" + ]; + ip6tables = [ + "-p tcp -j REJECT --reject-with tcp-reset" + "-p udp -j REJECT --reject-with icmp6-port-unreachable" + "-j REJECT" + ]; + }."ip${toString iptables-version}tables" + )} COMMIT ''; } diff --git a/tv/3modules/lidControl.nix b/tv/3modules/lidControl.nix new file mode 100644 index 000000000..6a48da18d --- /dev/null +++ b/tv/3modules/lidControl.nix @@ -0,0 +1,45 @@ +with import ./lib; +{ config, pkgs, ... }: { + options = { + tv.lidControl.enable = mkEnableOption "tv.lidControl"; + }; + config = let + cfg = config.tv.lidControl; + in mkIf cfg.enable { + services.acpid.enable = true; + services.acpid.lidEventCommands = /* sh */ '' + set -- $1 + + # usage: vt_is_xserver NUMBER + vt_is_xserver() { + ${pkgs.iproute}/bin/ss -lp src unix:/tmp/.X11-unix/X* | + ${pkgs.gnused}/bin/sed -n 's|.*/tmp/.X11-unix/X\([0-9]\+\)\>.*|\1|p' | + ${pkgs.gnugrep}/bin/grep -Fqx "$1" + } + + console=$(${pkgs.kbd}/bin/fgconsole) + + if vt_is_xserver "$console"; then + # usage: run_on_display COMMAND [ARG...] + run_on_display() { + owner=$(${pkgs.coreutils}/bin/stat -c %u /tmp/.X11-unix/X$console) + ${pkgs.systemd}/bin/systemd-run -GPq \ + -E DISPLAY=:$console \ + --uid=$owner \ + "$@" + } + case $3 in + open) + run_on_display ${pkgs.xorg.xset}/bin/xset dpms force on + ;; + close) + run_on_display ${pkgs.xorg.xset}/bin/xset dpms force off + ;; + esac + fi + ''; + services.logind.lidSwitch = "ignore"; + services.logind.lidSwitchDocked = "ignore"; + services.logind.lidSwitchExternalPower = "ignore"; + }; +} diff --git a/tv/3modules/systemd.nix b/tv/3modules/systemd.nix new file mode 100644 index 000000000..db8a51994 --- /dev/null +++ b/tv/3modules/systemd.nix @@ -0,0 +1,47 @@ +with import ./lib; +{ config, ... }: let + normalUsers = filterAttrs (_: getAttr "isNormalUser") config.users.users; +in { + options = { + tv.systemd.services = mkOption { + type = types.attrsOf (types.submodule (self: { + options = { + operators = mkOption { + type = with types; listOf (enum (attrNames normalUsers)); + default = []; + }; + }; + })); + default = {}; + }; + }; + config = { + security.polkit.extraConfig = let + access = + mapAttrs' + (name: cfg: + nameValuePair "${name}.service" + (genAttrs cfg.operators (const true)) + ) + config.tv.systemd.services; + in optionalString (access != {}) /* js */ '' + polkit.addRule(function () { + const access = ${lib.toJSON access}; + return function (action, subject) { + if (action.id === "org.freedesktop.systemd1.manage-units") { + const unit = action.lookup("unit"); + if ( + (access[unit]||{})[subject.user] || + ( + unit.includes("@") && + (access[unit.replace(/@[^.]+/, "@")]||{})[subject.user] + ) + ) { + return polkit.Result.YES; + } + } + } + }()); + ''; + }; +} diff --git a/tv/3modules/wwan.nix b/tv/3modules/wwan.nix new file mode 100644 index 000000000..03cd512e4 --- /dev/null +++ b/tv/3modules/wwan.nix @@ -0,0 +1,181 @@ +with import ./lib; +{ config, pkgs, ... }: { + options = { + tv.wwan.enable = mkEnableOption "tv.wwan"; + tv.wwan.apn = mkOption { + type = with types; filename; + }; + tv.wwan.device = mkOption { + type = with types; pathname; + default = "/dev/cdc-wdm0"; + }; + tv.wwan.interface = mkOption { + type = with types; nullOr filename; + default = null; + }; + tv.wwan.operators = mkOption { + type = with types; listOf username; + default = []; + }; + tv.wwan.secrets = mkOption { + type = with types; pathname; + default = toString <secrets/wwan.json>; + # format: {"pin1":number} + }; + }; + config = let + cfg = config.tv.wwan; + in mkIf cfg.enable { + nixpkgs.overlays = singleton (self: super: { + uqmi-wrapper = pkgs.symlinkJoin { + name = "uqmi-wrapper"; + paths = [ + (pkgs.writeDashBin "uqmi" '' + exec ${pkgs.uqmi}/bin/uqmi --device=${cfg.device} "$@" + '') + (pkgs.writeTextDir "share/bash-completion/completions/uqmi" /* sh */'' + _uqmi_complete() { + case ''${#COMP_WORDS[@]} in + 2) + COMPREPLY=($(compgen -W "$( + ${pkgs.uqmi}/bin/uqmi --help 2>&1 | + ${pkgs.coreutils}/bin/tr , \\n | + ${pkgs.gnused}/bin/sed -nr 's/^ *(-[a-z-]+).*/\1/p' + )" -- "''${COMP_WORDS[1]}")) + ;; + esac + } + complete -F _uqmi_complete uqmi + '') + pkgs.uqmi + ]; + }; + }); + systemd.services.wwan = { + environment = { + SECRETS = "%d/secrets"; + }; + path = [ + pkgs.busybox + pkgs.coreutils + pkgs.iproute2 + pkgs.jq + pkgs.uqmi-wrapper + (pkgs.writeDashBin "get-interface" ( + if cfg.interface != null then /* sh */ '' + echo ${cfg.interface} + '' else /* sh */ '' + exec ${pkgs.libqmi}/bin/qmicli -d ${cfg.device} -p --get-wwan-iface + '' + )) + ]; + serviceConfig = { + LoadCredential = [ + "secrets:${cfg.secrets}" + ]; + Type = "oneshot"; + RemainAfterExit = true; + SyslogIdentifier = "wwan"; + ExecStart = pkgs.writeDash "tv.wwan.start.sh" '' + set -efu + + interface=$(get-interface) + + pin1_status=$( + uqmi --uim-get-sim-state | + jq -r '"\(.pin1_status)/\(.pin1_verify_tries)"' + ) + case $pin1_status in + verified/*) + : + ;; + not_verified/3) + pin1=$(jq .pin1 "$SECRETS") + echo "verifying PIN1" >&2 + if ! uqmi --uim-verify-pin1 "$pin1"; then + echo "error: failed to verify PIN1" >&2 + exit 1 + fi + ;; + not_verified/*) + echo "error: not trying to verify PIN1: not enough tries left" >&2 + echo \ + "please check your configuration in ${cfg.secrets}" \ + " and verify if manually using:" \ + " ${pkgs.uqmi}/bin/uqmi -d $device --uim-veriy-pin1 XXXX" \ + >&2 + exit 1 + esac + + raw_ip_path=/sys/class/net/$interface/qmi/raw_ip + raw_ip=$(cat "$raw_ip_path") + if [ "$raw_ip" != Y ]; then + echo "enabling raw-ip" >&2 + if ! echo Y > "$raw_ip_path"; then + echo "error: failed to enable raw-ip" >&2 + exit 1 + fi + fi + + operating_mode=$(uqmi --get-device-operating-mode | tr -d \") + case $operating_mode in + online) + : + ;; + persistent_low_power|low_power) + echo "settings device operating mode to online" >&2 + uqmi --set-device-operating-mode online + operating_mode=$(uqmi --get-device-operating-mode | tr -d \") + if test "$operating_mode" != online; then + echo "error: failed to set device operating mode to online" >&2 + exit 1 + fi + ;; + *) + echo "error: don't know how to change device operating mode to online: $operating_mode" >&2 + exit 1 + esac + + ip link set dev "$interface" up + + data_status=$(uqmi --get-data-status | tr -d \") + case $data_status in + connected) + : + ;; + disconnected) + echo "starting network (APN=${cfg.apn})" >&2 + sleep 1 + uqmi \ + --start-network \ + --autoconnect \ + --apn ${cfg.apn} \ + --ip-family ipv4 + sleep 1 + ;; + *) + echo "error: unsupported data status: $data_status" >&2 + exit 1 + esac + + udhcpc -q -f -n -i "$interface" + ''; + Restart = "on-failure"; + ExecStop = pkgs.writeDash "tv.wwan.stop.sh" '' + set -efu + + interface=$(get-interface) + + ip link set dev "$interface" down + uqmi --stop-network 0xFFFFFFFF --autoconnect + uqmi --sync + uqmi --set-device-operating-mode persistent_low_power + ''; + }; + }; + users.users.root.packages = [ + pkgs.uqmi-wrapper + ]; + tv.systemd.services.wwan.operators = cfg.operators; + }; +} |