···1111# This also holds true for GitHub teams. Since almost none of our teams have write
1212# permissions, you need to list all members of the team with commit access individually.
13131414-# This file
1515-/.github/CODEOWNERS @edolstra
1616-1714# GitHub actions
1815/.github/workflows @NixOS/Security @Mic92 @zowoq
1916/.github/workflows/merge-staging @FRidh
···2219/.editorconfig @Mic92 @zowoq
23202421# Libraries
2525-/lib @edolstra @infinisil
2222+/lib @infinisil
2623/lib/systems @alyssais @ericson2314 @amjoseph-nixpkgs
2727-/lib/generators.nix @infinisil @edolstra @Profpatsch
2828-/lib/cli.nix @infinisil @edolstra @Profpatsch
2929-/lib/debug.nix @infinisil @edolstra @Profpatsch
3030-/lib/asserts.nix @infinisil @edolstra @Profpatsch
2424+/lib/generators.nix @infinisil @Profpatsch
2525+/lib/cli.nix @infinisil @Profpatsch
2626+/lib/debug.nix @infinisil @Profpatsch
2727+/lib/asserts.nix @infinisil @Profpatsch
3128/lib/path.* @infinisil @fricklerhandwerk
3229/lib/fileset @infinisil
3330/doc/functions/fileset.section.md @infinisil
+15
lib/fileset/README.md
···238238 And it would be unclear how the library should behave if the one file wouldn't be added to the store:
239239 `toSource { root = ./file.nix; fileset = <empty>; }` has no reasonable result because returing an empty store path wouldn't match the file type, and there's no way to have an empty file store path, whatever that would mean.
240240241241+### `fileFilter` takes a path
242242+243243+The `fileFilter` function takes a path, and not a file set, as its second argument.
244244+245245+- (-) Makes it harder to compose functions, since the file set type, the return value, can't be passed to the function itself like `fileFilter predicate fileset`
246246+ - (+) It's still possible to use `intersection` to filter on file sets: `intersection fileset (fileFilter predicate ./.)`
247247+ - (-) This does need an extra `./.` argument that's not obvious
248248+ - (+) This could always be `/.` or the project directory, `intersection` will make it lazy
249249+- (+) In the future this will allow `fileFilter` to support a predicate property like `subpath` and/or `components` in a reproducible way.
250250+ This wouldn't be possible if it took a file set, because file sets don't have a predictable absolute path.
251251+ - (-) What about the base path?
252252+ - (+) That can change depending on which files are included, so if it's used for `fileFilter`
253253+ it would change the `subpath`/`components` value depending on which files are included.
254254+- (+) If necessary, this restriction can be relaxed later, the opposite wouldn't be possible
255255+241256## To update in the future
242257243258Here's a list of places in the library that need to be updated in the future:
+15-5
lib/fileset/default.nix
···366366 type :: String,
367367 ...
368368 } -> Bool)
369369- -> FileSet
369369+ -> Path
370370 -> FileSet
371371372372 Example:
···397397 Other attributes may be added in the future.
398398 */
399399 predicate:
400400- # The file set to filter based on the predicate function
401401- fileset:
400400+ # The path whose files to filter
401401+ path:
402402 if ! isFunction predicate then
403403 throw ''
404404 lib.fileset.fileFilter: First argument is of type ${typeOf predicate}, but it should be a function instead.''
405405+ else if ! isPath path then
406406+ if path._type or "" == "fileset" then
407407+ throw ''
408408+ lib.fileset.fileFilter: Second argument is a file set, but it should be a path instead.
409409+ If you need to filter files in a file set, use `intersection fileset (fileFilter pred ./.)` instead.''
410410+ else
411411+ throw ''
412412+ lib.fileset.fileFilter: Second argument is of type ${typeOf path}, but it should be a path instead.''
413413+ else if ! pathExists path then
414414+ throw ''
415415+ lib.fileset.fileFilter: Second argument (${toString path}) is a path that does not exist.''
405416 else
406406- _fileFilter predicate
407407- (_coerce "lib.fileset.fileFilter: Second argument" fileset);
417417+ _fileFilter predicate path;
408418409419 /*
410420 The file set containing all files that are in both of two given file sets.
+18-15
lib/fileset/internal.nix
···786786 _differenceTree (path + "/${name}") lhsValue (rhs.${name} or null)
787787 ) (_directoryEntries path lhs);
788788789789- # Filters all files in a file set based on a predicate
790790- # Type: ({ name, type, ... } -> Bool) -> FileSet -> FileSet
791791- _fileFilter = predicate: fileset:
789789+ # Filters all files in a path based on a predicate
790790+ # Type: ({ name, type, ... } -> Bool) -> Path -> FileSet
791791+ _fileFilter = predicate: root:
792792 let
793793 # Check the predicate for a single file
794794 # Type: String -> String -> filesetTree
···807807808808 # Check the predicate for all files in a directory
809809 # Type: Path -> filesetTree
810810- fromDir = path: tree:
811811- mapAttrs (name: subtree:
812812- if isAttrs subtree || subtree == "directory" then
813813- fromDir (path + "/${name}") subtree
814814- else if subtree == null then
815815- null
810810+ fromDir = path:
811811+ mapAttrs (name: type:
812812+ if type == "directory" then
813813+ fromDir (path + "/${name}")
816814 else
817817- fromFile name subtree
818818- ) (_directoryEntries path tree);
815815+ fromFile name type
816816+ ) (readDir path);
817817+818818+ rootType = pathType root;
819819 in
820820- if fileset._internalIsEmptyWithoutBase then
821821- _emptyWithoutBase
820820+ if rootType == "directory" then
821821+ _create root (fromDir root)
822822 else
823823- _create fileset._internalBase
824824- (fromDir fileset._internalBase fileset._internalTree);
823823+ # Single files are turned into a directory containing that file or nothing.
824824+ _create (dirOf root) {
825825+ ${baseNameOf root} =
826826+ fromFile (baseNameOf root) rootType;
827827+ };
825828}
+4-11
lib/fileset/tests.sh
···813813# The first argument needs to be a function
814814expectFailure 'fileFilter null (abort "this is not needed")' 'lib.fileset.fileFilter: First argument is of type null, but it should be a function instead.'
815815816816-# The second argument can be a file set or an existing path
817817-expectFailure 'fileFilter (file: abort "this is not needed") null' 'lib.fileset.fileFilter: Second argument is of type null, but it should be a file set or a path instead.'
816816+# The second argument needs to be an existing path
817817+expectFailure 'fileFilter (file: abort "this is not needed") _emptyWithoutBase' 'lib.fileset.fileFilter: Second argument is a file set, but it should be a path instead.
818818+\s*If you need to filter files in a file set, use `intersection fileset \(fileFilter pred \./\.\)` instead.'
819819+expectFailure 'fileFilter (file: abort "this is not needed") null' 'lib.fileset.fileFilter: Second argument is of type null, but it should be a path instead.'
818820expectFailure 'fileFilter (file: abort "this is not needed") ./a' 'lib.fileset.fileFilter: Second argument \('"$work"'/a\) is a path that does not exist.'
819821820822# The predicate is not called when there's no files
821823tree=()
822824checkFileset 'fileFilter (file: abort "this is not needed") ./.'
823823-checkFileset 'fileFilter (file: abort "this is not needed") _emptyWithoutBase'
824825825826# The predicate must be able to handle extra attributes
826827touch a
···881882checkFileset 'union ./c/a (fileFilter (file: assert file.name != "a"; true) ./.)'
882883# but here we need to use ./c
883884checkFileset 'union (fileFilter (file: assert file.name != "a"; true) ./.) ./c'
884884-885885-# Also lazy, the filter isn't called on a filtered out path
886886-tree=(
887887- [a]=1
888888- [b]=0
889889- [c]=0
890890-)
891891-checkFileset 'fileFilter (file: assert file.name != "c"; file.name == "a") (difference ./. ./c)'
892885893886# Make sure single files are filtered correctly
894887tree=(
···385385386386- The `prayer` package as well as `services.prayer` have been removed because it's been unmaintained for several years and the author's website has vanished.
387387388388+- The `chrony` NixOS module now tracks the Real-Time Clock drift from the System Clock with `rtcfile` and automatically adjusts it with `rtcautotrim` when it exceeds the maximum error specified in `services.chrony.autotrimThreshold` (default 30 seconds). If you enabled `rtcsync` in `extraConfig`, you should remove RTC related options from `extraConfig`. If you do not want chrony configured to keep the RTC in check, you can set `services.chrony.enableRTCTrimming = false;`
389389+388390## Other Notable Changes {#sec-release-23.11-notable-changes}
389391390392- A new option `system.switch.enable` was added. By default, this is option is
+38-1
nixos/modules/services/networking/ntp/chrony.nix
···99 stateDir = cfg.directory;
1010 driftFile = "${stateDir}/chrony.drift";
1111 keyFile = "${stateDir}/chrony.keys";
1212+ rtcFile = "${stateDir}/chrony.rtc";
12131314 configFile = pkgs.writeText "chrony.conf" ''
1415 ${concatMapStringsSep "\n" (server: "server " + server + " " + cfg.serverOption + optionalString (cfg.enableNTS) " nts") cfg.servers}
···20212122 driftfile ${driftFile}
2223 keyfile ${keyFile}
2424+ ${optionalString (cfg.enableRTCTrimming) "rtcfile ${rtcFile}"}
2325 ${optionalString (cfg.enableNTS) "ntsdumpdir ${stateDir}"}
24262727+ ${optionalString (cfg.enableRTCTrimming) "rtcautotrim ${builtins.toString cfg.autotrimThreshold}"}
2528 ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"}
26292730 ${cfg.extraConfig}
···8588 '';
8689 };
87909191+ enableRTCTrimming = mkOption {
9292+ type = types.bool;
9393+ default = true;
9494+ description = lib.mdDoc ''
9595+ Enable tracking of the RTC offset to the system clock and automatic trimming.
9696+ See also [](#opt-services.chrony.autotrimThreshold)
9797+9898+ ::: {.note}
9999+ This is not compatible with the `rtcsync` directive, which naively syncs the RTC time every 11 minutes.
100100+101101+ Tracking the RTC drift will allow more precise timekeeping,
102102+ especially on intermittently running devices, where the RTC is very relevant.
103103+ :::
104104+ '';
105105+ };
106106+107107+ autotrimThreshold = mkOption {
108108+ type = types.ints.positive;
109109+ default = 30;
110110+ example = 10;
111111+ description = ''
112112+ Maximum estimated error threshold for the `rtcautotrim` command.
113113+ When reached, the RTC will be trimmed.
114114+ Only used when [](#opt-services.chrony.enableRTCTrimming) is enabled.
115115+ '';
116116+ };
117117+88118 enableNTS = mkOption {
89119 type = types.bool;
90120 default = false;
···141171 };
142172143173 config = mkIf cfg.enable {
144144- meta.maintainers = with lib.maintainers; [ thoughtpolice ];
174174+ meta.maintainers = with lib.maintainers; [ thoughtpolice vifino ];
145175146176 environment.systemPackages = [ chronyPkg ];
147177···156186157187 services.timesyncd.enable = mkForce false;
158188189189+ # If chrony controls and tracks the RTC, writing it externally causes clock error.
190190+ systemd.services.save-hwclock = lib.mkIf cfg.enableRTCTrimming {
191191+ enable = lib.mkForce false;
192192+ };
193193+159194 systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "chronyd.service"; };
160195161196 systemd.tmpfiles.rules = [
162197 "d ${stateDir} 0750 chrony chrony - -"
163198 "f ${driftFile} 0640 chrony chrony - -"
164199 "f ${keyFile} 0640 chrony chrony - -"
200200+ ] ++ lib.optionals cfg.enableRTCTrimming [
201201+ "f ${rtcFile} 0640 chrony chrony - -"
165202 ];
166203167204 systemd.services.chronyd =
+12-16
nixos/modules/services/networking/unbound.nix
···166166 services.unbound.settings = {
167167 server = {
168168 directory = mkDefault cfg.stateDir;
169169- username = cfg.user;
169169+ username = ''""'';
170170 chroot = ''""'';
171171 pidfile = ''""'';
172172 # when running under systemd there is no need to daemonize
···245245 NotifyAccess = "main";
246246 Type = "notify";
247247248248- # FIXME: Which of these do we actually need, can we drop the chroot flag?
249248 AmbientCapabilities = [
249249+ "CAP_NET_BIND_SERVICE"
250250+ "CAP_NET_RAW" # needed if ip-transparent is set to true
251251+ ];
252252+ CapabilityBoundingSet = [
250253 "CAP_NET_BIND_SERVICE"
251254 "CAP_NET_RAW"
252252- "CAP_SETGID"
253253- "CAP_SETUID"
254254- "CAP_SYS_CHROOT"
255255- "CAP_SYS_RESOURCE"
256255 ];
257256258257 User = cfg.user;
···266265 ProtectControlGroups = true;
267266 ProtectKernelModules = true;
268267 ProtectSystem = "strict";
268268+ ProtectClock = true;
269269+ ProtectHostname = true;
270270+ ProtectProc = "invisible";
271271+ ProcSubset = "pid";
272272+ ProtectKernelLogs = true;
273273+ ProtectKernelTunables = true;
269274 RuntimeDirectory = "unbound";
270275 ConfigurationDirectory = "unbound";
271276 StateDirectory = "unbound";
272277 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_NETLINK" "AF_UNIX" ];
273278 RestrictRealtime = true;
274279 SystemCallArchitectures = "native";
275275- SystemCallFilter = [
276276- "~@clock"
277277- "@cpu-emulation"
278278- "@debug"
279279- "@keyring"
280280- "@module"
281281- "mount"
282282- "@obsolete"
283283- "@resources"
284284- ];
280280+ SystemCallFilter = [ "@system-service" ];
285281 RestrictNamespaces = true;
286282 LockPersonality = true;
287283 RestrictSUIDSGID = true;
+46-36
nixos/modules/services/networking/unifi.nix
···11{ config, options, lib, pkgs, utils, ... }:
22-with lib;
32let
43 cfg = config.services.unifi;
54 stateDir = "/var/lib/unifi";
66- cmd = ''
77- @${cfg.jrePackage}/bin/java java \
88- ${optionalString (lib.versionAtLeast (lib.getVersion cfg.jrePackage) "16")
99- ("--add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED "
1010- + "--add-opens java.base/sun.security.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED "
1111- + "--add-opens java.rmi/sun.rmi.transport=ALL-UNNAMED")} \
1212- ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \
1313- ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \
1414- -jar ${stateDir}/lib/ace.jar
1515- '';
55+ cmd = lib.escapeShellArgs ([ "@${cfg.jrePackage}/bin/java" "java" ]
66+ ++ lib.optionals (lib.versionAtLeast (lib.getVersion cfg.jrePackage) "16") [
77+ "--add-opens=java.base/java.lang=ALL-UNNAMED"
88+ "--add-opens=java.base/java.time=ALL-UNNAMED"
99+ "--add-opens=java.base/sun.security.util=ALL-UNNAMED"
1010+ "--add-opens=java.base/java.io=ALL-UNNAMED"
1111+ "--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED"
1212+ ]
1313+ ++ (lib.optional (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m")
1414+ ++ (lib.optional (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m")
1515+ ++ cfg.extraJvmOptions
1616+ ++ [ "-jar" "${stateDir}/lib/ace.jar" ]);
1617in
1718{
18191920 options = {
20212121- services.unifi.enable = mkOption {
2222- type = types.bool;
2222+ services.unifi.enable = lib.mkOption {
2323+ type = lib.types.bool;
2324 default = false;
2425 description = lib.mdDoc ''
2526 Whether or not to enable the unifi controller service.
2627 '';
2728 };
28292929- services.unifi.jrePackage = mkOption {
3030- type = types.package;
3030+ services.unifi.jrePackage = lib.mkOption {
3131+ type = lib.types.package;
3132 default = if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.5") then pkgs.jdk17_headless else if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3") then pkgs.jdk11 else pkgs.jre8;
3232- defaultText = literalExpression ''if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.5") then pkgs.jdk17_headless else if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3" then pkgs.jdk11 else pkgs.jre8'';
3333+ defaultText = lib.literalExpression ''if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.5") then pkgs.jdk17_headless else if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3" then pkgs.jdk11 else pkgs.jre8'';
3334 description = lib.mdDoc ''
3435 The JRE package to use. Check the release notes to ensure it is supported.
3536 '';
3637 };
37383838- services.unifi.unifiPackage = mkOption {
3939- type = types.package;
3939+ services.unifi.unifiPackage = lib.mkOption {
4040+ type = lib.types.package;
4041 default = pkgs.unifi5;
4141- defaultText = literalExpression "pkgs.unifi5";
4242+ defaultText = lib.literalExpression "pkgs.unifi5";
4243 description = lib.mdDoc ''
4344 The unifi package to use.
4445 '';
4546 };
46474747- services.unifi.mongodbPackage = mkOption {
4848- type = types.package;
4848+ services.unifi.mongodbPackage = lib.mkOption {
4949+ type = lib.types.package;
4950 default = pkgs.mongodb-4_4;
5050- defaultText = literalExpression "pkgs.mongodb";
5151+ defaultText = lib.literalExpression "pkgs.mongodb";
5152 description = lib.mdDoc ''
5253 The mongodb package to use. Please note: unifi7 officially only supports mongodb up until 3.6 but works with 4.4.
5354 '';
5455 };
55565656- services.unifi.openFirewall = mkOption {
5757- type = types.bool;
5757+ services.unifi.openFirewall = lib.mkOption {
5858+ type = lib.types.bool;
5859 default = false;
5960 description = lib.mdDoc ''
6061 Whether or not to open the minimum required ports on the firewall.
···6566 '';
6667 };
67686868- services.unifi.initialJavaHeapSize = mkOption {
6969- type = types.nullOr types.int;
6969+ services.unifi.initialJavaHeapSize = lib.mkOption {
7070+ type = with lib.types; nullOr int;
7071 default = null;
7172 example = 1024;
7273 description = lib.mdDoc ''
···7576 '';
7677 };
77787878- services.unifi.maximumJavaHeapSize = mkOption {
7979- type = types.nullOr types.int;
7979+ services.unifi.maximumJavaHeapSize = lib.mkOption {
8080+ type = with lib.types; nullOr int;
8081 default = null;
8182 example = 4096;
8283 description = lib.mdDoc ''
···8586 '';
8687 };
87888989+ services.unifi.extraJvmOptions = lib.mkOption {
9090+ type = with lib.types; listOf str;
9191+ default = [ ];
9292+ example = lib.literalExpression ''["-Xlog:gc"]'';
9393+ description = lib.mdDoc ''
9494+ Set extra options to pass to the JVM.
9595+ '';
9696+ };
9797+8898 };
89999090- config = mkIf cfg.enable {
100100+ config = lib.mkIf cfg.enable {
9110192102 users.users.unifi = {
93103 isSystemUser = true;
···97107 };
98108 users.groups.unifi = {};
99109100100- networking.firewall = mkIf cfg.openFirewall {
110110+ networking.firewall = lib.mkIf cfg.openFirewall {
101111 # https://help.ubnt.com/hc/en-us/articles/218506997
102112 allowedTCPPorts = [
103113 8080 # Port for UAP to inform controller.
···123133124134 serviceConfig = {
125135 Type = "simple";
126126- ExecStart = "${(removeSuffix "\n" cmd)} start";
127127- ExecStop = "${(removeSuffix "\n" cmd)} stop";
136136+ ExecStart = "${cmd} start";
137137+ ExecStop = "${cmd} stop";
128138 Restart = "on-failure";
129139 TimeoutSec = "5min";
130140 User = "unifi";
···166176 StateDirectory = "unifi";
167177 RuntimeDirectory = "unifi";
168178 LogsDirectory = "unifi";
169169- CacheDirectory= "unifi";
179179+ CacheDirectory = "unifi";
170180171181 TemporaryFileSystem = [
172182 # required as we want to create bind mounts below
···176186 # We must create the binary directories as bind mounts instead of symlinks
177187 # This is because the controller resolves all symlinks to absolute paths
178188 # to be used as the working directory.
179179- BindPaths = [
189189+ BindPaths = [
180190 "/var/log/unifi:${stateDir}/logs"
181191 "/run/unifi:${stateDir}/run"
182192 "${cfg.unifiPackage}/dl:${stateDir}/dl"
···194204195205 };
196206 imports = [
197197- (mkRemovedOptionModule [ "services" "unifi" "dataDir" ] "You should move contents of dataDir to /var/lib/unifi/data" )
198198- (mkRenamedOptionModule [ "services" "unifi" "openPorts" ] [ "services" "unifi" "openFirewall" ])
207207+ (lib.mkRemovedOptionModule [ "services" "unifi" "dataDir" ] "You should move contents of dataDir to /var/lib/unifi/data")
208208+ (lib.mkRenamedOptionModule [ "services" "unifi" "openPorts" ] [ "services" "unifi" "openFirewall" ])
199209 ];
200210}
···4646 name = "source-${rev}";
4747 inherit owner repo rev hash;
4848 };
4949+ # nixpkgs-update: no auto update
5050+ # easier to update all providers together
49515052 meta = {
5153 inherit homepage;