summaryrefslogtreecommitdiffstats
path: root/tv/3modules
diff options
context:
space:
mode:
Diffstat (limited to 'tv/3modules')
-rw-r--r--tv/3modules/default.nix20
-rw-r--r--tv/3modules/iptables.nix33
-rw-r--r--tv/3modules/lidControl.nix45
-rw-r--r--tv/3modules/systemd.nix47
-rw-r--r--tv/3modules/wwan.nix181
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;
+ };
+}