{ config, lib, pkgs, ... }: let cfg = config.services.bro; in { options.services.bro = { enable = lib.mkEnableOption ""; package = lib.mkPackageOption pkgs "bro" { }; instances = lib.mkOption { default = { }; type = lib.types.attrsOf (lib.types.submodule ({ name, config, ... }: { options = { enable = lib.mkEnableOption ""; client = { logLevel = lib.mkOption { type = lib.types.nullOr (lib.types.enum [ "trace" "debug" "info" "warn" "error" ]); default = null; }; settings = lib.mkOption { default = { }; type = lib.types.submodule { freeformType = with lib.types; oneOf [ str bool ]; options = { BRO_SOCKET_PATH = lib.mkOption { type = lib.types.path; default = "/run/bro/${name}.sock"; defaultText = lib.literalExpression "\"/run/bro/${name}.sock\""; }; BRO_FORWARD_ENV = lib.mkOption { type = lib.types.listOf lib.types.str; default = config.server.settings.allowed-env; example = [ "LS_COLORS" "TIME_STYLE" "QUOTING_STYLE" ]; }; BRO_FILE_FLAGS = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; example = [ "-f" "--file" ]; }; BRO_FILE_ARGS = lib.mkOption { type = lib.types.bool; default = false; example = true; }; BRO_CAPTURE_TTY_STDIN = lib.mkOption { type = lib.types.bool; default = false; example = true; }; }; }; }; programName = lib.mkOption { type = lib.types.str; default = lib.baseNameOf config.server.settings.executable; defaultText = lib.literalExpression "lib.baseNameOf config.services.bro.instances..server.settings.executable"; }; package = lib.mkOption { type = lib.types.package; readOnly = true; default = ( lib.warnIf (!cfg.enable || !config.enable) "Bro wrapper for instance '${name}' should not be used unless it is enabled." (pkgs.writeShellApplication { name = config.client.programName; runtimeEnv = lib.pipe config.client.settings [ (lib.mapAttrs (_: v: if lib.isBool v then (if v == true then "1" else null) else v)) (lib.mapAttrs (_: v: if lib.isList v then (if v == [ ] then null else lib.concatStringsSep "," v) else v)) (env: env // { RUST_LOG = config.client.logLevel; }) (lib.filterAttrs (_: v: v != null)) ]; text = ''exec ${lib.getExe' cfg.package "bro-client"} "$@"''; })).overrideAttrs { name = "bro-${name}-wrapper"; }; }; }; server = { listenStreams = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "/run/bro/${name}.sock" ]; }; logLevel = lib.mkOption { type = lib.types.nullOr (lib.types.enum [ "trace" "debug" "info" "warn" "error" ]); default = "info"; }; settings = lib.mkOption { default = { }; type = lib.types.submodule { freeformType = lib.types.str; options = { executable = lib.mkOption { type = lib.types.str; }; fd-passing = lib.mkEnableOption "" // { default = true; }; allowed-env = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; example = [ "LS_COLORS" "TIME_STYLE" "QUOTING_STYLE" ]; }; inherit-env = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; example = [ "LS_COLORS" "TIME_STYLE" "QUOTING_STYLE" ]; }; }; }; }; }; }; })); }; }; config = lib.mkIf cfg.enable { systemd = { sockets = lib.mapAttrs' (name: instance: { name = "bro-${name}"; value = { wantedBy = [ "sockets.target" ]; listenStreams = instance.server.listenStreams; socketConfig = { AcceptFileDescriptors = lib.mkIf instance.server.settings.fd-passing true; }; }; }) (lib.filterAttrs (name: instance: instance.enable) cfg.instances); services = lib.mapAttrs' (name: instance: { name = "bro-${name}"; value = { environment.RUST_LOG = lib.mkIf (instance.server.logLevel != null) instance.server.logLevel; serviceConfig = { Type = "notify-reload"; ExecStart = let args = lib.pipe instance.server.settings [ (lib.filterAttrs (_: v: v != [ ])) (lib.mapAttrs (_: v: if lib.isList v then lib.concatStringsSep "," v else v)) (settings: settings // { systemd-socket = true; }) (lib.cli.toCommandLineShellGNU { }) ]; in "${lib.getExe' cfg.package "bro-server"} ${args}"; Restart = "on-failure"; RestartSec = "5s"; }; }; }) (lib.filterAttrs (name: instance: instance.enable) cfg.instances); }; }; }