summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorVladimír Čunát <v@cunat.cz>2019-06-05 11:12:34 +0200
committerVladimír Čunát <v@cunat.cz>2019-06-05 11:12:34 +0200
commitc0ccf42c69845b10c1acd98e93f2d427d429250a (patch)
tree05571464ace23e44ea411ad150cdf2d926b3f14a /nixos
parenteb7c14fad15e4762a0f54a83decc964b3ae77409 (diff)
parent576af1718704f9eca5db0983a94edcf8d06caddd (diff)
downloadnixpkgs-c0ccf42c69845b10c1acd98e93f2d427d429250a.tar
nixpkgs-c0ccf42c69845b10c1acd98e93f2d427d429250a.tar.gz
nixpkgs-c0ccf42c69845b10c1acd98e93f2d427d429250a.tar.bz2
nixpkgs-c0ccf42c69845b10c1acd98e93f2d427d429250a.tar.lz
nixpkgs-c0ccf42c69845b10c1acd98e93f2d427d429250a.tar.xz
nixpkgs-c0ccf42c69845b10c1acd98e93f2d427d429250a.tar.zst
nixpkgs-c0ccf42c69845b10c1acd98e93f2d427d429250a.zip
Merge branch 'staging-next' into staging
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-1909.xml12
-rw-r--r--nixos/modules/misc/ids.nix4
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/services/audio/jack.nix275
-rw-r--r--nixos/modules/services/desktops/geoclue2.nix13
-rw-r--r--nixos/modules/services/networking/btsync.nix324
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix50
-rw-r--r--nixos/modules/services/system/localtime.nix8
-rw-r--r--nixos/modules/services/torrent/deluge.nix152
-rw-r--r--nixos/modules/services/x11/compton.nix36
-rw-r--r--nixos/modules/services/x11/redshift.nix8
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix1
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py2
-rw-r--r--nixos/tests/deluge.nix49
14 files changed, 575 insertions, 361 deletions
diff --git a/nixos/doc/manual/release-notes/rl-1909.xml b/nixos/doc/manual/release-notes/rl-1909.xml
index 665345d6f35..922239998ad 100644
--- a/nixos/doc/manual/release-notes/rl-1909.xml
+++ b/nixos/doc/manual/release-notes/rl-1909.xml
@@ -114,6 +114,18 @@
    </listitem>
    <listitem>
     <para>
+     Since Bittorrent Sync was superseded by Resilio Sync in 2016, the
+     <literal>bittorrentSync</literal>, <literal>bittorrentSync14</literal>,
+     and <literal>bittorrentSync16</literal> packages have been removed in
+     favor of <literal>resilio-sync</literal>.
+    </para>
+    <para>
+     The corresponding module, <option>services.btsync</option> has been
+     replaced by the <option>services.resilio</option> module.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
       The limesurvey apache subservice was replaced with a full NixOS module.
       One can configure it using the <option>services.limesurvey.enable</option>
       and <option>services.limesurvey.virtualHost</option> options.
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 5b7fa5d2b98..f1118f472e4 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -145,7 +145,7 @@
       #notbit = 111; # unused
       aerospike = 111;
       ngircd = 112;
-      btsync = 113;
+      #btsync = 113; # unused
       minecraft = 114;
       vault = 115;
       rippled = 116;
@@ -457,7 +457,7 @@
       #notbit = 111; # unused
       aerospike = 111;
       #ngircd = 112; # unused
-      btsync = 113;
+      #btsync = 113; # unused
       #minecraft = 114; # unused
       vault = 115;
       #ripped = 116; # unused
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 5042470802f..1017d25bf28 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -184,6 +184,7 @@
   ./services/amqp/activemq/default.nix
   ./services/amqp/rabbitmq.nix
   ./services/audio/alsa.nix
+  ./services/audio/jack.nix
   ./services/audio/icecast.nix
   ./services/audio/liquidsoap.nix
   ./services/audio/mpd.nix
@@ -542,7 +543,6 @@
   ./services/networking/autossh.nix
   ./services/networking/bird.nix
   ./services/networking/bitlbee.nix
-  ./services/networking/btsync.nix
   ./services/networking/charybdis.nix
   ./services/networking/chrony.nix
   ./services/networking/cjdns.nix
diff --git a/nixos/modules/services/audio/jack.nix b/nixos/modules/services/audio/jack.nix
new file mode 100644
index 00000000000..1364abd4044
--- /dev/null
+++ b/nixos/modules/services/audio/jack.nix
@@ -0,0 +1,275 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.jack;
+
+  pcmPlugin = cfg.jackd.enable && cfg.alsa.enable;
+  loopback = cfg.jackd.enable && cfg.loopback.enable;
+
+  enable32BitAlsaPlugins = cfg.alsa.support32Bit && pkgs.stdenv.isx86_64 && pkgs.pkgsi686Linux.alsaLib != null;
+
+  umaskNeeded = versionOlder cfg.jackd.package.version "1.9.12";
+  bridgeNeeded = versionAtLeast cfg.jackd.package.version "1.9.12";
+in {
+  options = {
+    services.jack = {
+      jackd = {
+        enable = mkEnableOption ''
+          JACK Audio Connection Kit. You need to add yourself to the "jackaudio" group
+        '';
+
+        package = mkOption {
+          # until jack1 promiscuous mode is fixed
+          internal = true;
+          type = types.package;
+          default = pkgs.jack2;
+          defaultText = "pkgs.jack2";
+          example = literalExample "pkgs.jack1";
+          description = ''
+            The JACK package to use.
+          '';
+        };
+
+        extraOptions = mkOption {
+          type = types.listOf types.str;
+          default = [
+            "-dalsa"
+          ];
+          example = literalExample ''
+            [ "-dalsa" "--device" "hw:1" ];
+          '';
+          description = ''
+            Specifies startup command line arguments to pass to JACK server.
+          '';
+        };
+
+        session = mkOption {
+          type = types.lines;
+          description = ''
+            Commands to run after JACK is started.
+          '';
+        };
+
+      };
+
+      alsa = {
+        enable = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Route audio to/from generic ALSA-using applications using ALSA JACK PCM plugin.
+          '';
+        };
+
+        support32Bit = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to support sound for 32-bit ALSA applications on 64-bit system.
+          '';
+        };
+      };
+
+      loopback = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Create ALSA loopback device, instead of using PCM plugin. Has broader
+            application support (things like Steam will work), but may need fine-tuning
+            for concrete hardware.
+          '';
+        };
+
+        index = mkOption {
+          type = types.int;
+          default = 10;
+          description = ''
+            Index of an ALSA loopback device.
+          '';
+        };
+
+        config = mkOption {
+          type = types.lines;
+          description = ''
+            ALSA config for loopback device.
+          '';
+        };
+
+        session = mkOption {
+          type = types.lines;
+          description = ''
+            Additional commands to run to setup loopback device.
+          '';
+        };
+      };
+
+    };
+
+  };
+
+  config = mkMerge [
+
+    (mkIf pcmPlugin {
+      sound.extraConfig = ''
+        pcm_type.jack {
+          libs.native = ${pkgs.alsaPlugins}/lib/alsa-lib/libasound_module_pcm_jack.so ;
+          ${lib.optionalString enable32BitAlsaPlugins
+          "libs.32Bit = ${pkgs.pkgsi686Linux.alsaPlugins}/lib/alsa-lib/libasound_module_pcm_jack.so ;"}
+        }
+        pcm.!default {
+          @func getenv
+          vars [ PCM ]
+          default "plug:jack"
+        }
+      '';
+    })
+
+    (mkIf loopback {
+      boot.kernelModules = [ "snd-aloop" ];
+      boot.kernelParams = [ "snd-aloop.index=${toString cfg.loopback.index}" ];
+      sound.extraConfig = cfg.loopback.config;
+    })
+
+    (mkIf cfg.jackd.enable {
+      services.jack.jackd.session = ''
+        ${lib.optionalString bridgeNeeded "${pkgs.a2jmidid}/bin/a2jmidid -e &"}
+      '';
+      # https://alsa.opensrc.org/Jack_and_Loopback_device_as_Alsa-to-Jack_bridge#id06
+      services.jack.loopback.config = ''
+        pcm.loophw00 {
+          type hw
+          card ${toString cfg.loopback.index}
+          device 0
+          subdevice 0
+        }
+        pcm.amix {
+          type dmix
+          ipc_key 219345
+          slave {
+            pcm loophw00
+          }
+        }
+        pcm.asoftvol {
+          type softvol
+          slave.pcm "amix"
+          control { name Master }
+        }
+        pcm.cloop {
+          type hw
+          card ${toString cfg.loopback.index}
+          device 1
+          subdevice 0
+          format S32_LE
+        }
+        pcm.loophw01 {
+          type hw
+          card ${toString cfg.loopback.index}
+          device 0
+          subdevice 1
+        }
+        pcm.ploop {
+          type hw
+          card ${toString cfg.loopback.index}
+          device 1
+          subdevice 1
+          format S32_LE
+        }
+        pcm.aduplex {
+          type asym
+          playback.pcm "asoftvol"
+          capture.pcm "loophw01"
+        }
+        pcm.!default {
+          type plug
+          slave.pcm aduplex
+        }
+      '';
+      services.jack.loopback.session = ''
+        alsa_in -j cloop -dcloop &
+        alsa_out -j ploop -dploop &
+        while [ "$(jack_lsp cloop)" == "" ] || [ "$(jack_lsp ploop)" == "" ]; do sleep 1; done
+        jack_connect cloop:capture_1 system:playback_1
+        jack_connect cloop:capture_2 system:playback_2
+        jack_connect system:capture_1 ploop:playback_1
+        jack_connect system:capture_2 ploop:playback_2
+      '';
+
+      assertions = [
+        {
+          assertion = !(cfg.alsa.enable && cfg.loopback.enable);
+          message = "For JACK both alsa and loopback options shouldn't be used at the same time.";
+        }
+      ];
+
+      users.users.jackaudio = {
+        group = "jackaudio";
+        extraGroups = [ "audio" ];
+        description = "JACK Audio system service user";
+      };
+      # http://jackaudio.org/faq/linux_rt_config.html
+      security.pam.loginLimits = [
+        { domain = "@jackaudio"; type = "-"; item = "rtprio"; value = "99"; }
+        { domain = "@jackaudio"; type = "-"; item = "memlock"; value = "unlimited"; }
+      ];
+      users.groups.jackaudio = {};
+
+      environment = {
+        systemPackages = [ cfg.jackd.package ];
+        etc."alsa/conf.d/50-jack.conf".source = "${pkgs.alsaPlugins}/etc/alsa/conf.d/50-jack.conf";
+        variables.JACK_PROMISCUOUS_SERVER = "jackaudio";
+      };
+
+      services.udev.extraRules = ''
+        ACTION=="add", SUBSYSTEM=="sound", ATTRS{id}!="Loopback", TAG+="systemd", ENV{SYSTEMD_WANTS}="jack.service"
+      '';
+
+      systemd.services.jack = {
+        description = "JACK Audio Connection Kit";
+        serviceConfig = {
+          User = "jackaudio";
+          ExecStart = "${cfg.jackd.package}/bin/jackd ${lib.escapeShellArgs cfg.jackd.extraOptions}";
+          LimitRTPRIO = 99;
+          LimitMEMLOCK = "infinity";
+        } // optionalAttrs umaskNeeded {
+          UMask = "007";
+        };
+        path = [ cfg.jackd.package ];
+        environment = {
+          JACK_PROMISCUOUS_SERVER = "jackaudio";
+          JACK_NO_AUDIO_RESERVATION = "1";
+        };
+        restartIfChanged = false;
+      };
+      systemd.services.jack-session = {
+        description = "JACK session";
+        script = ''
+          jack_wait -w
+          ${cfg.jackd.session}
+          ${lib.optionalString cfg.loopback.enable cfg.loopback.session}
+        '';
+        serviceConfig = {
+          RemainAfterExit = true;
+          User = "jackaudio";
+          StateDirectory = "jack";
+          LimitRTPRIO = 99;
+          LimitMEMLOCK = "infinity";
+        };
+        path = [ cfg.jackd.package ];
+        environment = {
+          JACK_PROMISCUOUS_SERVER = "jackaudio";
+          HOME = "/var/lib/jack";
+        };
+        wantedBy = [ "jack.service" ];
+        partOf = [ "jack.service" ];
+        after = [ "jack.service" ];
+        restartIfChanged = false;
+      };
+    })
+
+  ];
+
+  meta.maintainers = [ maintainers.gnidorah ];
+}
diff --git a/nixos/modules/services/desktops/geoclue2.nix b/nixos/modules/services/desktops/geoclue2.nix
index a16dbc04a5f..040fe157d52 100644
--- a/nixos/modules/services/desktops/geoclue2.nix
+++ b/nixos/modules/services/desktops/geoclue2.nix
@@ -188,6 +188,19 @@ in
 
     systemd.packages = [ package ];
 
+    users.users.geoclue = {
+      isSystemUser = true;
+      home = "/var/lib/geoclue";
+      group = "geoclue";
+      description = "Geoinformation service";
+    };
+
+    users.groups.geoclue = {};
+
+    systemd.tmpfiles.rules = [
+      "d /var/lib/geoclue 0755 geoclue geoclue"
+    ];
+
     # restart geoclue service when the configuration changes
     systemd.services."geoclue".restartTriggers = [
       config.environment.etc."geoclue/geoclue.conf".source
diff --git a/nixos/modules/services/networking/btsync.nix b/nixos/modules/services/networking/btsync.nix
deleted file mode 100644
index 33e85ef58e6..00000000000
--- a/nixos/modules/services/networking/btsync.nix
+++ /dev/null
@@ -1,324 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.btsync;
-
-  bittorrentSync = cfg.package;
-
-  listenAddr = cfg.httpListenAddr + ":" + (toString cfg.httpListenPort);
-
-  optionalEmptyStr = b: v: optionalString (b != "") v;
-
-  webUIConfig = optionalString cfg.enableWebUI
-    ''
-      "webui":
-      {
-        ${optionalEmptyStr cfg.httpLogin     "\"login\":          \"${cfg.httpLogin}\","}
-        ${optionalEmptyStr cfg.httpPass      "\"password\":       \"${cfg.httpPass}\","}
-        ${optionalEmptyStr cfg.apiKey        "\"api_key\":        \"${cfg.apiKey}\","}
-        ${optionalEmptyStr cfg.directoryRoot "\"directory_root\": \"${cfg.directoryRoot}\","}
-        "listen": "${listenAddr}"
-      }
-    '';
-
-  knownHosts = e:
-    optionalString (e ? "knownHosts")
-      (concatStringsSep "," (map (v: "\"${v}\"") e."knownHosts"));
-
-  sharedFoldersRecord =
-    concatStringsSep "," (map (entry:
-      let helper = attr: v:
-        if (entry ? attr) then boolToString entry.attr else boolToString v;
-      in
-      ''
-        {
-          "secret": "${entry.secret}",
-          "dir":    "${entry.directory}",
-
-          "use_relay_server": ${helper "useRelayServer" true},
-          "use_tracker":      ${helper "useTracker"     true},
-          "use_dht":          ${helper "useDHT"        false},
-
-          "search_lan":       ${helper "searchLAN"      true},
-          "use_sync_trash":   ${helper "useSyncTrash"   true},
-
-          "known_hosts": [${knownHosts entry}]
-        }
-      '') cfg.sharedFolders);
-
-  sharedFoldersConfig = optionalString (cfg.sharedFolders != [])
-    ''
-      "shared_folders":
-        [
-        ${sharedFoldersRecord}
-        ]
-    '';
-
-  configFile = pkgs.writeText "btsync.config"
-    ''
-      {
-        "device_name":     "${cfg.deviceName}",
-        "storage_path":    "${cfg.storagePath}",
-        "listening_port":  ${toString cfg.listeningPort},
-        "use_gui":         false,
-
-        "check_for_updates": ${boolToString cfg.checkForUpdates},
-        "use_upnp":          ${boolToString cfg.useUpnp},
-        "download_limit":    ${toString cfg.downloadLimit},
-        "upload_limit":      ${toString cfg.uploadLimit},
-        "lan_encrypt_data":  ${boolToString cfg.encryptLAN},
-
-        ${webUIConfig}
-        ${sharedFoldersConfig}
-      }
-    '';
-in
-{
-  options = {
-    services.btsync = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          If enabled, start the Bittorrent Sync daemon. Once enabled, you can
-          interact with the service through the Web UI, or configure it in your
-          NixOS configuration. Enabling the <literal>btsync</literal> service
-          also installs a systemd user unit which can be used to start
-          user-specific copies of the daemon. Once installed, you can use
-          <literal>systemctl --user start btsync</literal> as your user to start
-          the daemon using the configuration file located at
-          <literal>$HOME/.config/btsync.conf</literal>.
-        '';
-      };
-
-      deviceName = mkOption {
-        type = types.str;
-        example = "Voltron";
-        description = ''
-          Name of the Bittorrent Sync device.
-        '';
-      };
-
-      listeningPort = mkOption {
-        type = types.int;
-        default = 0;
-        example = 44444;
-        description = ''
-          Listening port. Defaults to 0 which randomizes the port.
-        '';
-      };
-
-      checkForUpdates = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          Determines whether to check for updates and alert the user
-          about them in the UI.
-        '';
-      };
-
-      useUpnp = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          Use Universal Plug-n-Play (UPnP)
-        '';
-      };
-
-      downloadLimit = mkOption {
-        type = types.int;
-        default = 0;
-        example = 1024;
-        description = ''
-          Download speed limit. 0 is unlimited (default).
-        '';
-      };
-
-      uploadLimit = mkOption {
-        type = types.int;
-        default = 0;
-        example = 1024;
-        description = ''
-          Upload speed limit. 0 is unlimited (default).
-        '';
-      };
-
-      httpListenAddr = mkOption {
-        type = types.str;
-        default = "0.0.0.0";
-        example = "1.2.3.4";
-        description = ''
-          HTTP address to bind to.
-        '';
-      };
-
-      httpListenPort = mkOption {
-        type = types.int;
-        default = 9000;
-        description = ''
-          HTTP port to bind on.
-        '';
-      };
-
-      httpLogin = mkOption {
-        type = types.str;
-        example = "allyourbase";
-        default = "";
-        description = ''
-          HTTP web login username.
-        '';
-      };
-
-      httpPass = mkOption {
-        type = types.str;
-        example = "arebelongtous";
-        default = "";
-        description = ''
-          HTTP web login password.
-        '';
-      };
-
-      encryptLAN = mkOption {
-        type = types.bool;
-        default = true;
-        description = "Encrypt LAN data.";
-      };
-
-      enableWebUI = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enable Web UI for administration. Bound to the specified
-          <literal>httpListenAddress</literal> and
-          <literal>httpListenPort</literal>.
-          '';
-      };
-
-      package = mkOption {
-        type = types.package;
-        example = literalExample "pkgs.bittorrentSync20";
-        description = ''
-          Branch of bittorrent sync to use.
-        '';
-      };
-
-      storagePath = mkOption {
-        type = types.path;
-        default = "/var/lib/btsync/";
-        description = ''
-          Where BitTorrent Sync will store it's database files (containing
-          things like username info and licenses). Generally, you should not
-          need to ever change this.
-        '';
-      };
-
-      apiKey = mkOption {
-        type = types.str;
-        default = "";
-        description = "API key, which enables the developer API.";
-      };
-
-      directoryRoot = mkOption {
-        type = types.str;
-        default = "";
-        example = "/media";
-        description = "Default directory to add folders in the web UI.";
-      };
-
-      sharedFolders = mkOption {
-        default = [];
-        example =
-          [ { secret         = "AHMYFPCQAHBM7LQPFXQ7WV6Y42IGUXJ5Y";
-              directory      = "/home/user/sync_test";
-              useRelayServer = true;
-              useTracker     = true;
-              useDHT         = false;
-              searchLAN      = true;
-              useSyncTrash   = true;
-              knownHosts     =
-                [ "192.168.1.2:4444"
-                  "192.168.1.3:4444"
-                ];
-            }
-          ];
-        description = ''
-          Shared folder list. If enabled, web UI must be
-          disabled. Secrets can be generated using <literal>btsync
-          --generate-secret</literal>. Note that this secret will be
-          put inside the Nix store, so it is realistically not very
-          secret.
-
-          If you would like to be able to modify the contents of this
-          directories, it is recommended that you make your user a
-          member of the <literal>btsync</literal> group.
-
-          Directories in this list should be in the
-          <literal>btsync</literal> group, and that group must have
-          write access to the directory. It is also recommended that
-          <literal>chmod g+s</literal> is applied to the directory
-          so that any sub directories created will also belong to
-          the <literal>btsync</literal> group. Also,
-          <literal>setfacl -d -m group:btsync:rwx</literal> and
-          <literal>setfacl -m group:btsync:rwx</literal> should also
-          be applied so that the sub directories are writable by
-          the group.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    assertions =
-      [ { assertion = cfg.deviceName != "";
-          message   = "Device name cannot be empty.";
-        }
-        { assertion = cfg.enableWebUI -> cfg.sharedFolders == [];
-          message   = "If using shared folders, the web UI cannot be enabled.";
-        }
-        { assertion = cfg.apiKey != "" -> cfg.enableWebUI;
-          message   = "If you're using an API key, you must enable the web server.";
-        }
-      ];
-
-    services.btsync.package = mkOptionDefault pkgs.bittorrentSync14;
-
-    users.users.btsync = {
-      description     = "Bittorrent Sync Service user";
-      home            = cfg.storagePath;
-      createHome      = true;
-      uid             = config.ids.uids.btsync;
-      group           = "btsync";
-    };
-
-    users.groups = [
-      { name = "btsync";
-      }];
-
-    systemd.services.btsync = with pkgs; {
-      description = "Bittorrent Sync Service";
-      wantedBy    = [ "multi-user.target" ];
-      after       = [ "network.target" "local-fs.target" ];
-      serviceConfig = {
-        Restart   = "on-abort";
-        UMask     = "0002";
-        User      = "btsync";
-        ExecStart =
-          "${bittorrentSync}/bin/btsync --nodaemon --config ${configFile}";
-      };
-    };
-
-    systemd.user.services.btsync = with pkgs; {
-      description = "Bittorrent Sync user service";
-      after       = [ "network.target" "local-fs.target" ];
-      serviceConfig = {
-        Restart   = "on-abort";
-        ExecStart =
-          "${bittorrentSync}/bin/btsync --nodaemon --config %h/.config/btsync.conf";
-      };
-    };
-
-    environment.systemPackages = [ cfg.package ];
-  };
-}
diff --git a/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix b/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix
index d4f7e95f859..808cb863a9c 100644
--- a/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix
+++ b/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix
@@ -6,7 +6,7 @@
 #
 #   git clone https://github.com/strongswan/strongswan.git
 #   cd strongswan
-#   git diff 5.5.3..5.6.0 src/swanctl/swanctl.opt
+#   git diff 5.7.2..5.8.0 src/swanctl/swanctl.opt
 
 lib: with (import ./param-constructors.nix lib);
 
@@ -227,6 +227,22 @@ in {
       irrespective of the value of this option (even when set to no).
     '';
 
+    childless = mkEnumParam [ "allow" "force" "never" ] "allow" ''
+      Use childless IKE_SA initiation (RFC 6023) for IKEv2.  Acceptable values
+      are <literal>allow</literal> (the default), <literal>force</literal> and
+      <literal>never</literal>. If set to <literal>allow</literal>, responders
+      will accept childless IKE_SAs (as indicated via notify in the IKE_SA_INIT
+      response) while initiators continue to create regular IKE_SAs with the
+      first CHILD_SA created during IKE_AUTH, unless the IKE_SA is initiated
+      explicitly without any children (which will fail if the responder does not
+      support or has disabled this extension).  If set to
+      <literal>force</literal>, only childless initiation is accepted and the
+      first CHILD_SA is created with a separate CREATE_CHILD_SA exchange
+      (e.g. to use an independent DH exchange for all CHILD_SAs). Finally,
+      setting the option to <literal>never</literal> disables support for
+      childless IKE_SAs as responder.
+    '';
+
     send_certreq = mkYesNoParam yes ''
       Send certificate request payloads to offer trusted root CA certificates to
       the peer. Certificate requests help the peer to choose an appropriate
@@ -350,6 +366,16 @@ in {
       name from either the pools section or an external pool.
     '';
 
+    if_id_in = mkStrParam "0" ''
+      XFRM interface ID set on inbound policies/SA, can be overridden by child
+      config, see there for details.
+    '';
+
+    if_id_out = mkStrParam "0" ''
+      XFRM interface ID set on outbound policies/SA, can be overridden by child
+      config, see there for details.
+    '';
+
     mediation = mkYesNoParam no ''
       Whether this connection is a mediation connection, that is, whether this
       connection is used to mediate other connections using the IKEv2 Mediation
@@ -799,7 +825,7 @@ in {
         Updown script to invoke on CHILD_SA up and down events.
       '';
 
-      hostaccess = mkYesNoParam yes ''
+      hostaccess = mkYesNoParam no ''
         Hostaccess variable to pass to <literal>updown</literal> script.
       '';
 
@@ -960,6 +986,26 @@ in {
         mask requires at least Linux 4.19.
       '';
 
+      if_id_in = mkStrParam "0" ''
+        XFRM interface ID set on inbound policies/SA. This allows installing
+        duplicate policies/SAs and associates them with an interface with the
+        same ID. The special value <literal>%unique</literal> sets a unique
+        interface ID on each CHILD_SA instance, beyond that the value
+        <literal>%unique-dir</literal> assigns a different unique interface ID
+        for each CHILD_SA direction (in/out).
+      '';
+
+      if_id_out = mkStrParam "0" ''
+        XFRM interface ID set on outbound policies/SA. This allows installing
+        duplicate policies/SAs and associates them with an interface with the
+        same ID. The special value <literal>%unique</literal> sets a unique
+        interface ID on each CHILD_SA instance, beyond that the value
+        <literal>%unique-dir</literal> assigns a different unique interface ID
+        for each CHILD_SA direction (in/out).
+
+        The daemon will not install routes for CHILD_SAs that have this option set.
+     '';
+
       tfc_padding = mkParamOfType (with lib.types; either int (enum ["mtu"])) 0 ''
         Pads ESP packets with additional data to have a consistent ESP packet
         size for improved Traffic Flow Confidentiality. The padding defines the
diff --git a/nixos/modules/services/system/localtime.nix b/nixos/modules/services/system/localtime.nix
index c7e897c9644..8e9286b9407 100644
--- a/nixos/modules/services/system/localtime.nix
+++ b/nixos/modules/services/system/localtime.nix
@@ -20,7 +20,13 @@ in {
   };
 
   config = mkIf cfg.enable {
-    services.geoclue2.enable = true;
+    services.geoclue2 = {
+      enable = true;
+      appConfig."localtime" = {
+        isAllowed = true;
+        isSystem = true;
+      };
+    };
 
     # so polkit will pick up the rules
     environment.systemPackages = [ pkgs.localtime ];
diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix
index 84f0437b941..01a5890a784 100644
--- a/nixos/modules/services/torrent/deluge.nix
+++ b/nixos/modules/services/torrent/deluge.nix
@@ -5,8 +5,33 @@ with lib;
 let
   cfg = config.services.deluge;
   cfg_web = config.services.deluge.web;
+
   openFilesLimit = 4096;
+  listenPortsDefault = [ 6881 6889 ];
+
+  listToRange = x: { from = elemAt x 0; to = elemAt x 1; };
+
+  configDir = "${cfg.dataDir}/.config/deluge";
+  configFile = pkgs.writeText "core.conf" (builtins.toJSON cfg.config);
+  declarativeLockFile = "${configDir}/.declarative";
 
+  preStart = if cfg.declarative then ''
+    if [ -e ${declarativeLockFile} ]; then
+      # Was declarative before, no need to back up anything
+      ln -sf ${configFile} ${configDir}/core.conf
+      ln -sf ${cfg.authFile} ${configDir}/auth
+    else
+      # Declarative for the first time, backup stateful files
+      ln -sb --suffix=.stateful ${configFile} ${configDir}/core.conf
+      ln -sb --suffix=.stateful ${cfg.authFile} ${configDir}/auth
+      echo "Autogenerated file that signifies that this server configuration is managed declaratively by NixOS" \
+        > ${declarativeLockFile}
+    fi
+  '' else ''
+    if [ -e ${declarativeLockFile} ]; then
+      rm ${declarativeLockFile}
+    fi
+  '';
 in {
   options = {
     services = {
@@ -15,42 +40,151 @@ in {
 
         openFilesLimit = mkOption {
           default = openFilesLimit;
-          example = 8192;
           description = ''
             Number of files to allow deluged to open.
           '';
         };
+
+        config = mkOption {
+          type = types.attrs;
+          default = {};
+          example = literalExample ''
+            {
+              download_location = "/srv/torrents/";
+              max_upload_speed = "1000.0";
+              share_ratio_limit = "2.0";
+              allow_remote = true;
+              daemon_port = 58846;
+              listen_ports = [ ${toString listenPortsDefault} ];
+            }
+          '';
+          description = ''
+            Deluge core configuration for the core.conf file. Only has an effect
+            when <option>services.deluge.declarative</option> is set to
+            <literal>true</literal>. String values must be quoted, integer and
+            boolean values must not. See
+            <link xlink:href="https://git.deluge-torrent.org/deluge/tree/deluge/core/preferencesmanager.py#n41"/>
+            for the availaible options.
+          '';
+        };
+
+        declarative = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to use a declarative deluge configuration.
+            Only if set to <literal>true</literal>, the options
+            <option>services.deluge.config</option>,
+            <option>services.deluge.openFirewall</option> and
+            <option>services.deluge.authFile</option> will be
+            applied.
+          '';
+        };
+
+        openFirewall = mkOption {
+          default = false;
+          type = types.bool;
+          description = ''
+            Whether to open the firewall for the ports in
+            <option>services.deluge.config.listen_ports</option>. It only takes effet if
+            <option>services.deluge.declarative</option> is set to
+            <literal>true</literal>.
+
+            It does NOT apply to the daemon port nor the web UI port. To access those
+            ports secuerly check the documentation
+            <link xlink:href="https://dev.deluge-torrent.org/wiki/UserGuide/ThinClient#CreateSSHTunnel"/>
+            or use a VPN or configure certificates for deluge.
+          '';
+        };
+
+        dataDir = mkOption {
+          type = types.path;
+          default = "/var/lib/deluge";
+          description = ''
+            The directory where deluge will create files.
+          '';
+        };
+
+        authFile = mkOption {
+          type = types.path;
+          example = "/run/keys/deluge-auth";
+          description = ''
+            The file managing the authentication for deluge, the format of this
+            file is straightforward, each line contains a
+            username:password:level tuple in plaintext. It only has an effect
+            when <option>services.deluge.declarative</option> is set to
+            <literal>true</literal>.
+            See <link xlink:href="https://dev.deluge-torrent.org/wiki/UserGuide/Authentication"/> for
+            more informations.
+          '';
+        };
       };
 
-      deluge.web.enable = mkEnableOption "Deluge Web daemon";
+      deluge.web = {
+        enable = mkEnableOption "Deluge Web daemon";
+        port = mkOption {
+        type = types.port;
+          default = 8112;
+          description = ''
+            Deluge web UI port.
+          '';
+        };
+      };
     };
   };
 
   config = mkIf cfg.enable {
 
+    systemd.tmpfiles.rules = [ "d '${configDir}' 0770 deluge deluge" ]
+    ++ optional (cfg.config ? "download_location")
+      "d '${cfg.config.download_location}' 0770 deluge deluge"
+    ++ optional (cfg.config ? "torrentfiles_location")
+      "d '${cfg.config.torrentfiles_location}' 0770 deluge deluge"
+    ++ optional (cfg.config ? "move_completed_path")
+      "d '${cfg.config.move_completed_path}' 0770 deluge deluge";
+
     systemd.services.deluged = {
       after = [ "network.target" ];
       description = "Deluge BitTorrent Daemon";
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.deluge ];
       serviceConfig = {
-        ExecStart = "${pkgs.deluge}/bin/deluged -d";
-        # To prevent "Quit & shutdown daemon" from working; we want systemd to manage it!
+        ExecStart = ''
+          ${pkgs.deluge}/bin/deluged \
+            --do-not-daemonize \
+            --config ${configDir}
+        '';
+        # To prevent "Quit & shutdown daemon" from working; we want systemd to
+        # manage it!
         Restart = "on-success";
         User = "deluge";
         Group = "deluge";
+        UMask = "0002";
         LimitNOFILE = cfg.openFilesLimit;
       };
+      preStart = preStart;
     };
 
     systemd.services.delugeweb = mkIf cfg_web.enable {
-      after = [ "network.target" ];
+      after = [ "network.target" "deluged.service"];
+      requires = [ "deluged.service" ];
       description = "Deluge BitTorrent WebUI";
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.deluge ];
-      serviceConfig.ExecStart = "${pkgs.deluge}/bin/deluge --ui web";
-      serviceConfig.User = "deluge";
-      serviceConfig.Group = "deluge";
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.deluge}/bin/deluge-web \
+            --config ${configDir} \
+            --port ${toString cfg.web.port}
+        '';
+        User = "deluge";
+        Group = "deluge";
+      };
+    };
+
+    networking.firewall = mkIf (cfg.declarative && cfg.openFirewall && !(cfg.config.random_port or true)) {
+      allowedTCPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
+      allowedUDPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
     };
 
     environment.systemPackages = [ pkgs.deluge ];
@@ -58,7 +192,7 @@ in {
     users.users.deluge = {
       group = "deluge";
       uid = config.ids.uids.deluge;
-      home = "/var/lib/deluge/";
+      home = cfg.dataDir;
       createHome = true;
       description = "Deluge Daemon user";
     };
diff --git a/nixos/modules/services/x11/compton.nix b/nixos/modules/services/x11/compton.nix
index 11db0a133d6..d4357324c87 100644
--- a/nixos/modules/services/x11/compton.nix
+++ b/nixos/modules/services/x11/compton.nix
@@ -7,10 +7,19 @@ let
 
   cfg = config.services.compton;
 
+  literalAttrs = v:
+    if isString v then toString v
+    else if isAttrs v then "{\n"
+      + concatStringsSep "\n" (mapAttrsToList
+        (name: value: "${literalAttrs name} = ${literalAttrs value};")
+        v)
+      + "\n}"
+    else generators.toPretty {} v;
+
   floatBetween = a: b: with lib; with types;
     addCheck str (x: versionAtLeast x a && versionOlder x b);
 
-  pairOf = x: with types; addCheck (listOf x) (y: lib.length y == 2);
+  pairOf = x: with types; addCheck (listOf x) (y: length y == 2);
 
   opacityRules = optionalString (length cfg.opacityRules != 0)
     (concatMapStringsSep ",\n" (rule: ''"${rule}"'') cfg.opacityRules);
@@ -23,8 +32,7 @@ let
       fade-in-step  = ${elemAt cfg.fadeSteps 0};
       fade-out-step = ${elemAt cfg.fadeSteps 1};
       fade-exclude  = ${toJSON cfg.fadeExclude};
-    '' + 
-    optionalString cfg.shadow ''
+    '' + optionalString cfg.shadow ''
 
       # shadows
       shadow = true;
@@ -39,10 +47,7 @@ let
       inactive-opacity = ${cfg.inactiveOpacity};
 
       wintypes:
-      {
-        popup_menu = { opacity = ${cfg.menuOpacity}; }
-        dropdown_menu = { opacity = ${cfg.menuOpacity}; }
-      };
+      ${literalAttrs cfg.wintypes};
 
       opacity-rule = [
         ${opacityRules}
@@ -50,7 +55,7 @@ let
 
       # other options
       backend = ${toJSON cfg.backend};
-      vsync = ${lib.boolToString cfg.vSync};
+      vsync = ${boolToString cfg.vSync};
       refresh-rate = ${toString cfg.refreshRate};
     '' + cfg.extraOptions);
 
@@ -98,7 +103,7 @@ in {
       example = [
         "window_type *= 'menu'"
         "name ~= 'Firefox$'"
-        "focused = 1" 
+        "focused = 1"
       ];
       description = ''
         List of conditions of windows that should not be faded.
@@ -138,7 +143,7 @@ in {
       example = [
         "window_type *= 'menu'"
         "name ~= 'Firefox$'"
-        "focused = 1" 
+        "focused = 1"
       ];
       description = ''
         List of conditions of windows that should have no shadow.
@@ -173,6 +178,15 @@ in {
       '';
     };
 
+    wintypes = mkOption {
+      type = types.attrs;
+      default = { popup_menu = { opacity = cfg.menuOpacity; }; dropdown_menu = { opacity = cfg.menuOpacity; }; };
+      example = {};
+      description = ''
+        Rules for specific window types.
+      '';
+    };
+
     opacityRules = mkOption {
       type = types.listOf types.str;
       default = [];
@@ -201,7 +215,7 @@ in {
         let
           res = x != "none";
           msg = "The type of services.compton.vSync has changed to bool:"
-                + " interpreting ${x} as ${lib.boolToString res}";
+                + " interpreting ${x} as ${boolToString res}";
         in
           if isBool x then x
           else warn msg res;
diff --git a/nixos/modules/services/x11/redshift.nix b/nixos/modules/services/x11/redshift.nix
index b7dd7debcb6..4345a334808 100644
--- a/nixos/modules/services/x11/redshift.nix
+++ b/nixos/modules/services/x11/redshift.nix
@@ -119,7 +119,13 @@ in {
     # needed so that .desktop files are installed, which geoclue cares about
     environment.systemPackages = [ cfg.package ];
 
-    services.geoclue2.enable = mkIf (cfg.provider == "geoclue2") true;
+    services.geoclue2 = mkIf (cfg.provider == "geoclue2") {
+      enable = true;
+      appConfig."redshift" = {
+        isAllowed = true;
+        isSystem = true;
+      };
+    };
 
     systemd.user.services.redshift = 
     let
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 99aa7759c95..4e4d14985b0 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -360,6 +360,7 @@ in
       font = mkOption {
         type = types.nullOr types.path;
         default = "${realGrub}/share/grub/unicode.pf2";
+        defaultText = ''"''${pkgs.grub2}/share/grub/unicode.pf2"'';
         description = ''
           Path to a TrueType, OpenType, or pf2 font to be used by Grub.
         '';
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index 940d4c0eb97..ebe37ca10a2 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -154,7 +154,7 @@ def remove_old_entries(gens):
         except ValueError:
             pass
     for path in glob.iglob("@efiSysMountPoint@/efi/nixos/*"):
-        if not path in known_paths:
+        if not path in known_paths and not os.path.isdir(path):
             os.unlink(path)
 
 def get_profiles():
diff --git a/nixos/tests/deluge.nix b/nixos/tests/deluge.nix
index b4be5e465cc..22ad84e7bff 100644
--- a/nixos/tests/deluge.nix
+++ b/nixos/tests/deluge.nix
@@ -5,25 +5,56 @@ import ./make-test.nix ({ pkgs, ...} : {
   };
 
   nodes = {
-    server =
+    simple = {
+      services.deluge = {
+        enable = true;
+        web.enable = true;
+      };
+      networking.firewall.allowedTCPPorts = [ 8112 ];
+    };
+
+    declarative =
       { ... }:
 
-      { services.deluge = {
+      {
+        services.deluge = {
           enable = true;
-          web.enable = true;
+          openFirewall = true;
+          declarative = true;
+          config = {
+            allow_remote = true;
+            download_location = "/var/lib/deluge/my-download";
+            daemon_port = 58846;
+            listen_ports = [ 6881 6889 ];
+          };
+          web = {
+            enable = true;
+            port =  3142;
+          };
+          authFile = pkgs.writeText "deluge-auth" ''
+            localclient:a7bef72a890:10
+            andrew:password:10
+            user3:anotherpass:5
+          '';
         };
-        networking.firewall.allowedTCPPorts = [ 8112 ];
+        environment.systemPackages = [ pkgs.deluge ];
       };
 
-    client = { };
   };
 
   testScript = ''
     startAll;
 
-    $server->waitForUnit("deluged");
-    $server->waitForUnit("delugeweb");
-    $client->waitForUnit("network.target");
-    $client->waitUntilSucceeds("curl --fail http://server:8112");
+    $simple->waitForUnit("deluged");
+    $simple->waitForUnit("delugeweb");
+    $simple->waitForOpenPort("8112");
+    $declarative->waitForUnit("network.target");
+    $declarative->waitUntilSucceeds("curl --fail http://simple:8112");
+
+    $declarative->waitForUnit("deluged");
+    $declarative->waitForUnit("delugeweb");
+    $declarative->waitUntilSucceeds("curl --fail http://declarative:3142");
+    $declarative->succeed("deluge-console 'help' | grep -q 'rm - Remove a torrent'");
+    $declarative->succeed("deluge-console 'connect 127.0.0.1:58846 andrew password; help' | grep -q 'rm - Remove a torrent'");
   '';
 })