aboutsummaryrefslogtreecommitdiff
path: root/nixpkgs/nixos/modules/services/networking/znc/default.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services/networking/znc/default.nix')
-rw-r--r--nixpkgs/nixos/modules/services/networking/znc/default.nix308
1 files changed, 308 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/networking/znc/default.nix b/nixpkgs/nixos/modules/services/networking/znc/default.nix
new file mode 100644
index 00000000000..a7315896c50
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/znc/default.nix
@@ -0,0 +1,308 @@
+{ config, lib, pkgs, ...}:
+
+with lib;
+
+let
+
+ cfg = config.services.znc;
+
+ defaultUser = "znc";
+
+ modules = pkgs.buildEnv {
+ name = "znc-modules";
+ paths = cfg.modulePackages;
+ };
+
+ listenerPorts = concatMap (l: optional (l ? Port) l.Port)
+ (attrValues (cfg.config.Listener or {}));
+
+ # Converts the config option to a string
+ semanticString = let
+
+ sortedAttrs = set: sort (l: r:
+ if l == "extraConfig" then false # Always put extraConfig last
+ else if isAttrs set.${l} == isAttrs set.${r} then l < r
+ else isAttrs set.${r} # Attrsets should be last, makes for a nice config
+ # This last case occurs when any side (but not both) is an attrset
+ # The order of these is correct when the attrset is on the right
+ # which we're just returning
+ ) (attrNames set);
+
+ # Specifies an attrset that encodes the value according to its type
+ encode = name: value: {
+ null = [];
+ bool = [ "${name} = ${boolToString value}" ];
+ int = [ "${name} = ${toString value}" ];
+
+ # extraConfig should be inserted verbatim
+ string = [ (if name == "extraConfig" then value else "${name} = ${value}") ];
+
+ # Values like `Foo = [ "bar" "baz" ];` should be transformed into
+ # Foo=bar
+ # Foo=baz
+ list = concatMap (encode name) value;
+
+ # Values like `Foo = { bar = { Baz = "baz"; Qux = "qux"; Florps = null; }; };` should be transmed into
+ # <Foo bar>
+ # Baz=baz
+ # Qux=qux
+ # </Foo>
+ set = concatMap (subname: optionals (value.${subname} != null) ([
+ "<${name} ${subname}>"
+ ] ++ map (line: "\t${line}") (toLines value.${subname}) ++ [
+ "</${name}>"
+ ])) (filter (v: v != null) (attrNames value));
+
+ }.${builtins.typeOf value};
+
+ # One level "above" encode, acts upon a set and uses encode on each name,value pair
+ toLines = set: concatMap (name: encode name set.${name}) (sortedAttrs set);
+
+ in
+ concatStringsSep "\n" (toLines cfg.config);
+
+ semanticTypes = with types; rec {
+ zncAtom = nullOr (oneOf [ int bool str ]);
+ zncAttr = attrsOf (nullOr zncConf);
+ zncAll = oneOf [ zncAtom (listOf zncAtom) zncAttr ];
+ zncConf = attrsOf (zncAll // {
+ # Since this is a recursive type and the description by default contains
+ # the description of its subtypes, infinite recursion would occur without
+ # explicitly breaking this cycle
+ description = "znc values (null, atoms (str, int, bool), list of atoms, or attrsets of znc values)";
+ });
+ };
+
+in
+
+{
+
+ imports = [ ./options.nix ];
+
+ options = {
+ services.znc = {
+ enable = mkEnableOption "ZNC";
+
+ user = mkOption {
+ default = "znc";
+ example = "john";
+ type = types.str;
+ description = ''
+ The name of an existing user account to use to own the ZNC server
+ process. If not specified, a default user will be created.
+ '';
+ };
+
+ group = mkOption {
+ default = defaultUser;
+ example = "users";
+ type = types.str;
+ description = ''
+ Group to own the ZNC process.
+ '';
+ };
+
+ dataDir = mkOption {
+ default = "/var/lib/znc/";
+ example = "/home/john/.znc/";
+ type = types.path;
+ description = ''
+ The state directory for ZNC. The config and the modules will be linked
+ to from this directory as well.
+ '';
+ };
+
+ openFirewall = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to open ports in the firewall for ZNC. Does work with
+ ports for listeners specified in
+ <option>services.znc.config.Listener</option>.
+ '';
+ };
+
+ config = mkOption {
+ type = semanticTypes.zncConf;
+ default = {};
+ example = literalExample ''
+ {
+ LoadModule = [ "webadmin" "adminlog" ];
+ User.paul = {
+ Admin = true;
+ Nick = "paul";
+ AltNick = "paul1";
+ LoadModule = [ "chansaver" "controlpanel" ];
+ Network.freenode = {
+ Server = "chat.freenode.net +6697";
+ LoadModule = [ "simple_away" ];
+ Chan = {
+ "#nixos" = { Detached = false; };
+ "##linux" = { Disabled = true; };
+ };
+ };
+ Pass.password = {
+ Method = "sha256";
+ Hash = "e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93";
+ Salt = "l5Xryew4g*!oa(ECfX2o";
+ };
+ };
+ }
+ '';
+ description = ''
+ Configuration for ZNC, see
+ <link xlink:href="https://wiki.znc.in/Configuration"/> for details. The
+ Nix value declared here will be translated directly to the xml-like
+ format ZNC expects. This is much more flexible than the legacy options
+ under <option>services.znc.confOptions.*</option>, but also can't do
+ any type checking.
+ </para>
+ <para>
+ You can use <command>nix-instantiate --eval --strict '&lt;nixpkgs/nixos&gt;' -A config.services.znc.config</command>
+ to view the current value. By default it contains a listener for port
+ 5000 with SSL enabled.
+ </para>
+ <para>
+ Nix attributes called <literal>extraConfig</literal> will be inserted
+ verbatim into the resulting config file.
+ </para>
+ <para>
+ If <option>services.znc.useLegacyConfig</option> is turned on, the
+ option values in <option>services.znc.confOptions.*</option> will be
+ gracefully be applied to this option.
+ </para>
+ <para>
+ If you intend to update the configuration through this option, be sure
+ to enable <option>services.znc.mutable</option>, otherwise none of the
+ changes here will be applied after the initial deploy.
+ '';
+ };
+
+ configFile = mkOption {
+ type = types.path;
+ example = "~/.znc/configs/znc.conf";
+ description = ''
+ Configuration file for ZNC. It is recommended to use the
+ <option>config</option> option instead.
+ </para>
+ <para>
+ Setting this option will override any auto-generated config file
+ through the <option>confOptions</option> or <option>config</option>
+ options.
+ '';
+ };
+
+ modulePackages = mkOption {
+ type = types.listOf types.package;
+ default = [ ];
+ example = literalExample "[ pkgs.zncModules.fish pkgs.zncModules.push ]";
+ description = ''
+ A list of global znc module packages to add to znc.
+ '';
+ };
+
+ mutable = mkOption {
+ default = true; # TODO: Default to true when config is set, make sure to not delete the old config if present
+ type = types.bool;
+ description = ''
+ Indicates whether to allow the contents of the
+ <literal>dataDir</literal> directory to be changed by the user at
+ run-time.
+ </para>
+ <para>
+ If enabled, modifications to the ZNC configuration after its initial
+ creation are not overwritten by a NixOS rebuild. If disabled, the
+ ZNC configuration is rebuilt on every NixOS rebuild.
+ </para>
+ <para>
+ If the user wants to manage the ZNC service using the web admin
+ interface, this option should be enabled.
+ '';
+ };
+
+ extraFlags = mkOption {
+ default = [ ];
+ example = [ "--debug" ];
+ type = types.listOf types.str;
+ description = ''
+ Extra arguments to use for executing znc.
+ '';
+ };
+ };
+ };
+
+
+ ###### Implementation
+
+ config = mkIf cfg.enable {
+
+ services.znc = {
+ configFile = mkDefault (pkgs.writeText "znc-generated.conf" semanticString);
+ config = {
+ Version = lib.getVersion pkgs.znc;
+ Listener.l.Port = mkDefault 5000;
+ Listener.l.SSL = mkDefault true;
+ };
+ };
+
+ networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall listenerPorts;
+
+ systemd.services.znc = {
+ description = "ZNC Server";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network-online.target" ];
+ serviceConfig = {
+ User = cfg.user;
+ Group = cfg.group;
+ Restart = "always";
+ ExecStart = "${pkgs.znc}/bin/znc --foreground --datadir ${cfg.dataDir} ${escapeShellArgs cfg.extraFlags}";
+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
+ };
+ preStart = ''
+ mkdir -p ${cfg.dataDir}/configs
+
+ # If mutable, regenerate conf file every time.
+ ${optionalString (!cfg.mutable) ''
+ echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated."
+ rm -f ${cfg.dataDir}/configs/znc.conf
+ ''}
+
+ # Ensure essential files exist.
+ if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then
+ echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
+ cp --no-clobber ${cfg.configFile} ${cfg.dataDir}/configs/znc.conf
+ chmod u+rw ${cfg.dataDir}/configs/znc.conf
+ chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf
+ fi
+
+ if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then
+ echo "No znc.pem file found in ${cfg.dataDir}. Creating one now."
+ ${pkgs.znc}/bin/znc --makepem --datadir ${cfg.dataDir}
+ fi
+
+ # Symlink modules
+ rm ${cfg.dataDir}/modules || true
+ ln -fs ${modules}/lib/znc ${cfg.dataDir}/modules
+ '';
+ };
+
+ users.users = optionalAttrs (cfg.user == defaultUser) {
+ ${defaultUser} =
+ { description = "ZNC server daemon owner";
+ group = defaultUser;
+ uid = config.ids.uids.znc;
+ home = cfg.dataDir;
+ createHome = true;
+ };
+ };
+
+ users.groups = optionalAttrs (cfg.user == defaultUser) {
+ ${defaultUser} =
+ { gid = config.ids.gids.znc;
+ members = [ defaultUser ];
+ };
+ };
+
+ };
+}