summary refs log tree commit diff
path: root/nixos/modules/config
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/config')
-rw-r--r--nixos/modules/config/fonts/fontconfig-penultimate.nix270
-rw-r--r--nixos/modules/config/fonts/fontconfig-ultimate.nix102
-rw-r--r--nixos/modules/config/fonts/fontconfig.nix171
-rw-r--r--nixos/modules/config/fonts/fonts.nix1
-rw-r--r--nixos/modules/config/networking.nix9
-rw-r--r--nixos/modules/config/pulseaudio.nix2
-rw-r--r--nixos/modules/config/shells-environment.nix3
-rw-r--r--nixos/modules/config/sysctl.nix4
-rw-r--r--nixos/modules/config/update-users-groups.pl72
9 files changed, 466 insertions, 168 deletions
diff --git a/nixos/modules/config/fonts/fontconfig-penultimate.nix b/nixos/modules/config/fonts/fontconfig-penultimate.nix
new file mode 100644
index 00000000000..3e163b8ec51
--- /dev/null
+++ b/nixos/modules/config/fonts/fontconfig-penultimate.nix
@@ -0,0 +1,270 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.fonts.fontconfig;
+
+  fcBool = x: "<bool>" + (boolToString x) + "</bool>";
+
+  # back-supported fontconfig version and package
+  # version is used for font cache generation
+  supportVersion = "210";
+  supportPkg     = pkgs."fontconfig_${supportVersion}";
+
+  # latest fontconfig version and package
+  # version is used for configuration folder name, /etc/fonts/VERSION/
+  # note: format differs from supportVersion and can not be used with makeCacheConf
+  latestVersion  = pkgs.fontconfig.configVersion;
+  latestPkg      = pkgs.fontconfig;
+
+  # supported version fonts.conf
+  supportFontsConf = pkgs.makeFontsConf { fontconfig = supportPkg; fontDirectories = config.fonts.fonts; };
+
+  # configuration file to read fontconfig cache
+  # version dependent
+  # priority 0
+  cacheConfSupport = makeCacheConf { version = supportVersion; };
+  cacheConfLatest  = makeCacheConf {};
+
+  # generate the font cache setting file for a fontconfig version
+  # use latest when no version is passed
+  makeCacheConf = { version ? null }:
+    let
+      fcPackage = if builtins.isNull version
+                  then "fontconfig"
+                  else "fontconfig_${version}";
+      makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; };
+      cache     = makeCache pkgs."${fcPackage}";
+      cache32   = makeCache pkgs.pkgsi686Linux."${fcPackage}";
+    in
+    pkgs.writeText "fc-00-nixos-cache.conf" ''
+      <?xml version='1.0'?>
+      <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+      <fontconfig>
+        <!-- Font directories -->
+        ${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.fonts)}
+        <!-- Pre-generated font caches -->
+        <cachedir>${cache}</cachedir>
+        ${optionalString (pkgs.stdenv.isx86_64 && cfg.cache32Bit) ''
+          <cachedir>${cache32}</cachedir>
+        ''}
+      </fontconfig>
+    '';
+
+  # The configuration to be included in /etc/font/
+  penultimateConf = pkgs.runCommand "font-penultimate-conf" {} ''
+    support_folder=$out/etc/fonts/conf.d
+    latest_folder=$out/etc/fonts/${latestVersion}/conf.d
+
+    mkdir -p $support_folder
+    mkdir -p $latest_folder
+
+    ln -s ${supportFontsConf} $support_folder/../fonts.conf
+    ln -s ${latestPkg.out}/etc/fonts/fonts.conf \
+          $latest_folder/../fonts.conf
+
+    # fontconfig-penultimate various configuration files
+    ln -s ${pkgs.fontconfig-penultimate}/etc/fonts/conf.d/*.conf \
+          $support_folder
+    ln -s ${pkgs.fontconfig-penultimate}/etc/fonts/conf.d/*.conf \
+          $latest_folder
+
+    ln -s ${cacheConfSupport} $support_folder/00-nixos-cache.conf
+    ln -s ${cacheConfLatest}  $latest_folder/00-nixos-cache.conf
+
+    rm $support_folder/10-antialias.conf $latest_folder/10-antialias.conf
+    ln -s ${antialiasConf} $support_folder/10-antialias.conf
+    ln -s ${antialiasConf} $latest_folder/10-antialias.conf
+
+    rm $support_folder/10-hinting.conf $latest_folder/10-hinting.conf
+    ln -s ${hintingConf} $support_folder/10-hinting.conf
+    ln -s ${hintingConf} $latest_folder/10-hinting.conf
+
+    ${optionalString cfg.useEmbeddedBitmaps ''
+    rm $support_folder/10-no-embedded-bitmaps.conf
+    rm $latest_folder/10-no-embedded-bitmaps.conf
+    ''}
+
+    rm $support_folder/10-subpixel.conf $latest_folder/10-subpixel.conf
+    ln -s ${subpixelConf} $support_folder/10-subpixel.conf
+    ln -s ${subpixelConf} $latest_folder/10-subpixel.conf
+
+    ${optionalString (cfg.dpi != 0) ''
+    ln -s ${dpiConf} $support_folder/11-dpi.conf
+    ln -s ${dpiConf} $latest_folder/11-dpi.conf
+    ''}
+
+    ${optionalString (!cfg.includeUserConf) ''
+    rm $support_folder/50-user.conf
+    rm $latest_folder/50-user.conf
+    ''}
+
+    # 51-local.conf
+    rm $latest_folder/51-local.conf
+    substitute \
+      ${pkgs.fontconfig-penultimate}/etc/fonts/conf.d/51-local.conf \
+      $latest_folder/51-local.conf \
+      --replace local.conf /etc/fonts/${latestVersion}/local.conf
+
+    ln -s ${defaultFontsConf} $support_folder/52-default-fonts.conf
+    ln -s ${defaultFontsConf} $latest_folder/52-default-fonts.conf
+
+    ${optionalString cfg.allowBitmaps ''
+    rm $support_folder/53-no-bitmaps.conf
+    rm $latest_folder/53-no-bitmaps.conf
+    ''}
+
+    ${optionalString (!cfg.allowType1) ''
+    ln -s ${rejectType1} $support_folder/53-no-type1.conf
+    ln -s ${rejectType1} $latest_folder/53-no-type1.conf
+    ''}
+  '';
+
+  hintingConf = pkgs.writeText "fc-10-hinting.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+    <fontconfig>
+
+      <!-- Default rendering settings -->
+      <match target="pattern">
+        <edit mode="append" name="hinting">
+          ${fcBool cfg.hinting.enable}
+        </edit>
+        <edit mode="append" name="autohint">
+          ${fcBool cfg.hinting.autohint}
+        </edit>
+        <edit mode="append" name="hintstyle">
+          <const>hintslight</const>
+        </edit>
+      </match>
+
+    </fontconfig>
+  '';
+
+  antialiasConf = pkgs.writeText "fc-10-antialias.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+    <fontconfig>
+
+      <!-- Default rendering settings -->
+      <match target="pattern">
+        <edit mode="append" name="antialias">
+          ${fcBool cfg.antialias}
+        </edit>
+      </match>
+
+    </fontconfig>
+  '';
+
+  subpixelConf = pkgs.writeText "fc-10-subpixel.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+    <fontconfig>
+
+      <!-- Default rendering settings -->
+      <match target="pattern">
+        <edit mode="append" name="rgba">
+          <const>${cfg.subpixel.rgba}</const>
+        </edit>
+        <edit mode="append" name="lcdfilter">
+          <const>lcd${cfg.subpixel.lcdfilter}</const>
+        </edit>
+      </match>
+
+    </fontconfig>
+  '';
+
+  dpiConf = pkgs.writeText "fc-11-dpi.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+    <fontconfig>
+
+      <match target="pattern">
+        <edit name="dpi" mode="assign">
+          <double>${toString cfg.dpi}</double>
+        </edit>
+      </match>
+
+    </fontconfig>
+  '';
+
+  defaultFontsConf =
+    let genDefault = fonts: name:
+      optionalString (fonts != []) ''
+        <alias>
+          <family>${name}</family>
+          <prefer>
+          ${concatStringsSep ""
+          (map (font: ''
+            <family>${font}</family>
+          '') fonts)}
+          </prefer>
+        </alias>
+      '';
+    in
+    pkgs.writeText "fc-52-nixos-default-fonts.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+    <fontconfig>
+
+      <!-- Default fonts -->
+      ${genDefault cfg.defaultFonts.sansSerif "sans-serif"}
+
+      ${genDefault cfg.defaultFonts.serif     "serif"}
+
+      ${genDefault cfg.defaultFonts.monospace "monospace"}
+
+    </fontconfig>
+  '';
+
+  rejectType1 = pkgs.writeText "fc-53-no-type1.conf" ''
+    <?xml version="1.0"?>
+    <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+    <fontconfig>
+
+    <!-- Reject Type 1 fonts -->
+    <selectfont>
+      <rejectfont>
+        <pattern>
+          <patelt name="fontformat"><string>Type 1</string></patelt>
+        </pattern>
+      </rejectfont>
+    </selectfont>
+
+    </fontconfig>
+  '';
+
+in
+{
+
+  options = {
+
+    fonts = {
+
+      fontconfig = {
+
+        penultimate = {
+          enable = mkOption {
+            type = types.bool;
+            default = true;
+            description = ''
+              Enable fontconfig-penultimate settings to supplement the
+              NixOS defaults by providing per-font rendering defaults and
+              metric aliases.
+            '';
+          };
+        };
+
+      };
+    };
+
+  };
+
+  config = mkIf (config.fonts.fontconfig.enable && cfg.enable) {
+
+    fonts.fontconfig.confPackages = [ penultimateConf ];
+
+  };
+
+}
diff --git a/nixos/modules/config/fonts/fontconfig-ultimate.nix b/nixos/modules/config/fonts/fontconfig-ultimate.nix
index a3f52fbd919..1bf9fdb1984 100644
--- a/nixos/modules/config/fonts/fontconfig-ultimate.nix
+++ b/nixos/modules/config/fonts/fontconfig-ultimate.nix
@@ -8,61 +8,6 @@ let fcBool = x: if x then "<bool>true</bool>" else "<bool>false</bool>";
 
     latestVersion  = pkgs.fontconfig.configVersion;
 
-    # fontconfig ultimate main configuration file
-    # priority 52
-    fontconfigUltimateConf = pkgs.writeText "fc-52-fontconfig-ultimate.conf" ''
-      <?xml version="1.0"?>
-      <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
-      <fontconfig>
-
-        ${optionalString (!cfg.allowBitmaps) ''
-        <!-- Reject bitmap fonts -->
-        <selectfont>
-          <rejectfont>
-            <pattern>
-              <patelt name="scalable"><bool>false</bool></patelt>
-            </pattern>
-          </rejectfont>
-        </selectfont>
-        ''}
-
-        ${optionalString cfg.allowType1 ''
-        <!-- Reject Type 1 fonts -->
-        <selectfont>
-          <rejectfont>
-            <pattern>
-              <patelt name="fontformat">
-                <string>Type 1</string>
-              </patelt>
-            </pattern>
-          </rejectfont>
-        </selectfont>
-        ''}
-
-        <!-- Use embedded bitmaps in fonts like Calibri? -->
-        <match target="font">
-          <edit name="embeddedbitmap" mode="assign">
-            ${fcBool cfg.useEmbeddedBitmaps}
-          </edit>
-        </match>
-
-        <!-- Force autohint always -->
-        <match target="font">
-          <edit name="force_autohint" mode="assign">
-            ${fcBool cfg.forceAutohint}
-          </edit>
-        </match>
-
-        <!-- Render some monospace TTF fonts as bitmaps -->
-        <match target="pattern">
-          <edit name="bitmap_monospace" mode="assign">
-            ${fcBool cfg.renderMonoTTFAsBitmap}
-          </edit>
-        </match>
-
-      </fontconfig>
-    '';
-
     # The configuration to be included in /etc/font/
     confPkg = pkgs.runCommand "font-ultimate-conf" {} ''
       support_folder=$out/etc/fonts/conf.d
@@ -71,12 +16,6 @@ let fcBool = x: if x then "<bool>true</bool>" else "<bool>false</bool>";
       mkdir -p $support_folder
       mkdir -p $latest_folder
 
-      # 52-fontconfig-ultimate.conf
-      ln -s ${fontconfigUltimateConf} \
-            $support_folder/52-fontconfig-ultimate.conf
-      ln -s ${fontconfigUltimateConf} \
-            $latest_folder/52-fontconfig-ultimate.conf
-
       # fontconfig ultimate substitutions
       ${optionalString (cfg.substitutions != "none") ''
       ln -s ${pkgs.fontconfig-ultimate}/etc/fonts/presets/${cfg.substitutions}/*.conf \
@@ -104,7 +43,7 @@ in
         ultimate = {
           enable = mkOption {
             type = types.bool;
-            default = true;
+            default = false;
             description = ''
               Enable fontconfig-ultimate settings (formerly known as
               Infinality). Besides the customizable settings in this NixOS
@@ -113,45 +52,6 @@ in
             '';
           };
 
-          allowBitmaps = mkOption {
-            type = types.bool;
-            default = true;
-            description = ''
-              Allow bitmap fonts. Set to <literal>false</literal> to ban all
-              bitmap fonts.
-            '';
-          };
-
-          allowType1 = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''
-              Allow Type-1 fonts. Default is <literal>false</literal> because of
-              poor rendering.
-            '';
-          };
-
-          useEmbeddedBitmaps = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''Use embedded bitmaps in fonts like Calibri.'';
-          };
-
-          forceAutohint = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''
-              Force use of the TrueType Autohinter. Useful for debugging or
-              free-software purists.
-            '';
-          };
-
-          renderMonoTTFAsBitmap = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''Render some monospace TTF fonts as bitmaps.'';
-          };
-
           substitutions = mkOption {
             type = types.nullOr (types.enum ["free" "combi" "ms"]);
             default = "free";
diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
index 52ad1e714fb..12f5ca2e799 100644
--- a/nixos/modules/config/fonts/fontconfig.nix
+++ b/nixos/modules/config/fonts/fontconfig.nix
@@ -20,7 +20,7 @@ with lib;
 
 let cfg = config.fonts.fontconfig;
 
-    fcBool = x: "<bool>" + (if x then "true" else "false") + "</bool>";
+    fcBool = x: "<bool>" + (boolToString x) + "</bool>";
 
     # back-supported fontconfig version and package
     # version is used for font cache generation
@@ -41,11 +41,11 @@ let cfg = config.fonts.fontconfig;
     # priority 0
     cacheConfSupport = makeCacheConf { version = supportVersion; };
     cacheConfLatest  = makeCacheConf {};
-    
+
     # generate the font cache setting file for a fontconfig version
     # use latest when no version is passed
     makeCacheConf = { version ? null }:
-      let 
+      let
         fcPackage = if builtins.isNull version
                     then "fontconfig"
                     else "fontconfig_${version}";
@@ -75,23 +75,23 @@ let cfg = config.fonts.fontconfig;
       <fontconfig>
 
         <!-- Default rendering settings -->
-        <match target="font">
-          <edit mode="assign" name="hinting">
+        <match target="pattern">
+          <edit mode="append" name="hinting">
             ${fcBool cfg.hinting.enable}
           </edit>
-          <edit mode="assign" name="autohint">
+          <edit mode="append" name="autohint">
             ${fcBool cfg.hinting.autohint}
           </edit>
-          <edit mode="assign" name="hintstyle">
-            <const>hint${cfg.hinting.style}</const>
+          <edit mode="append" name="hintstyle">
+            <const>hintslight</const>
           </edit>
-          <edit mode="assign" name="antialias">
+          <edit mode="append" name="antialias">
             ${fcBool cfg.antialias}
           </edit>
-          <edit mode="assign" name="rgba">
+          <edit mode="append" name="rgba">
             <const>${cfg.subpixel.rgba}</const>
           </edit>
-          <edit mode="assign" name="lcdfilter">
+          <edit mode="append" name="lcdfilter">
             <const>lcd${cfg.subpixel.lcdfilter}</const>
           </edit>
         </match>
@@ -113,7 +113,7 @@ let cfg = config.fonts.fontconfig;
 
     # default fonts configuration file
     # priority 52
-    defaultFontsConf = 
+    defaultFontsConf =
       let genDefault = fonts: name:
         optionalString (fonts != []) ''
           <alias>
@@ -142,7 +142,54 @@ let cfg = config.fonts.fontconfig;
       </fontconfig>
     '';
 
-    # fontconfig configuration package 
+    # bitmap font options
+    # priority 53
+    rejectBitmaps = pkgs.writeText "fc-53-nixos-bitmaps.conf" ''
+      <?xml version="1.0"?>
+      <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+      <fontconfig>
+
+      ${optionalString (!cfg.allowBitmaps) ''
+      <!-- Reject bitmap fonts -->
+      <selectfont>
+        <rejectfont>
+          <pattern>
+            <patelt name="scalable"><bool>false</bool></patelt>
+          </pattern>
+        </rejectfont>
+      </selectfont>
+      ''}
+
+      <!-- Use embedded bitmaps in fonts like Calibri? -->
+      <match target="font">
+        <edit name="embeddedbitmap" mode="assign">
+          ${fcBool cfg.useEmbeddedBitmaps}
+        </edit>
+      </match>
+
+      </fontconfig>
+    '';
+
+    # reject Type 1 fonts
+    # priority 53
+    rejectType1 = pkgs.writeText "fc-53-nixos-reject-type1.conf" ''
+      <?xml version="1.0"?>
+      <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+      <fontconfig>
+
+      <!-- Reject Type 1 fonts -->
+      <selectfont>
+        <rejectfont>
+          <pattern>
+            <patelt name="fontformat"><string>Type 1</string></patelt>
+          </pattern>
+        </rejectfont>
+      </selectfont>
+
+      </fontconfig>
+    '';
+
+    # fontconfig configuration package
     confPkg = pkgs.runCommand "fontconfig-conf" {} ''
       support_folder=$out/etc/fonts
       latest_folder=$out/etc/fonts/${latestVersion}
@@ -166,7 +213,7 @@ let cfg = config.fonts.fontconfig;
 
       substitute ${latestPkg.out}/etc/fonts/conf.d/51-local.conf \
                  $latest_folder/conf.d/51-local.conf \
-                 --replace local.conf /etc/fonts/${latestVersion}/local.conf 
+                 --replace local.conf /etc/fonts/${latestVersion}/local.conf
 
       # 00-nixos-cache.conf
       ln -s ${cacheConfSupport} \
@@ -192,6 +239,16 @@ let cfg = config.fonts.fontconfig;
       # 52-nixos-default-fonts.conf
       ln -s ${defaultFontsConf} $support_folder/conf.d/52-nixos-default-fonts.conf
       ln -s ${defaultFontsConf} $latest_folder/conf.d/52-nixos-default-fonts.conf
+
+      # 53-nixos-bitmaps.conf
+      ln -s ${rejectBitmaps} $support_folder/conf.d/53-nixos-bitmaps.conf
+      ln -s ${rejectBitmaps} $latest_folder/conf.d/53-nixos-bitmaps.conf
+
+      ${optionalString (! cfg.allowType1) ''
+      # 53-nixos-reject-type1.conf
+      ln -s ${rejectType1} $support_folder/conf.d/53-nixos-reject-type1.conf
+      ln -s ${rejectType1} $latest_folder/conf.d/53-nixos-reject-type1.conf
+      ''}
     '';
 
     # Package with configuration files
@@ -233,7 +290,11 @@ in
         antialias = mkOption {
           type = types.bool;
           default = true;
-          description = "Enable font antialiasing.";
+          description = ''
+            Enable font antialiasing. At high resolution (> 200 DPI),
+            antialiasing has no visible effect; users of such displays may want
+            to disable this option.
+          '';
         };
 
         dpi = mkOption {
@@ -249,7 +310,7 @@ in
           type = types.lines;
           default = "";
           description = ''
-            System-wide customization file contents, has higher priority than 
+            System-wide customization file contents, has higher priority than
             <literal>defaultFonts</literal> settings.
           '';
         };
@@ -287,26 +348,21 @@ in
           enable = mkOption {
             type = types.bool;
             default = true;
-            description = "Enable TrueType hinting.";
-          };
-
-          autohint = mkOption {
-            type = types.bool;
-            default = true;
             description = ''
-              Enable the autohinter, which provides hinting for otherwise
-              un-hinted fonts. The results are usually lower quality than
-              correctly-hinted fonts.
+              Enable font hinting. Hinting aligns glyphs to pixel boundaries to
+              improve rendering sharpness at low resolution. At high resolution
+              (> 200 dpi) hinting will do nothing (at best); users of such
+              displays may want to disable this option.
             '';
           };
 
-          style = mkOption {
-            type = types.enum ["none" "slight" "medium" "full"];
-            default = "full";
+          autohint = mkOption {
+            type = types.bool;
+            default = false;
             description = ''
-              TrueType hinting style, one of <literal>none</literal>,
-              <literal>slight</literal>, <literal>medium</literal>, or
-              <literal>full</literal>.
+              Enable the autohinter in place of the default interpreter.
+              The results are usually lower quality than correctly-hinted
+              fonts, but better than unhinted fonts.
             '';
           };
         };
@@ -327,7 +383,15 @@ in
             default = "rgb";
             type = types.enum ["rgb" "bgr" "vrgb" "vbgr" "none"];
             description = ''
-              Subpixel order.
+              Subpixel order. The overwhelming majority of displays are
+              <literal>rgb</literal> in their normal orientation. Select
+              <literal>vrgb</literal> for mounting such a display 90 degrees
+              clockwise from its normal orientation or <literal>vbgr</literal>
+              for mounting 90 degrees counter-clockwise. Select
+              <literal>bgr</literal> in the unlikely event of mounting 180
+              degrees from the normal orientation. Reverse these directions in
+              the improbable event that the display's native subpixel order is
+              <literal>bgr</literal>.
             '';
           };
 
@@ -335,7 +399,9 @@ in
             default = "default";
             type = types.enum ["none" "default" "light" "legacy"];
             description = ''
-              FreeType LCD filter.
+              FreeType LCD filter. At high resolution (> 200 DPI), LCD filtering
+              has no visible effect; users of such displays may want to select
+              <literal>none</literal>.
             '';
           };
 
@@ -349,16 +415,43 @@ in
           '';
         };
 
+        allowBitmaps = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Allow bitmap fonts. Set to <literal>false</literal> to ban all
+            bitmap fonts.
+          '';
+        };
+
+        allowType1 = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Allow Type-1 fonts. Default is <literal>false</literal> because of
+            poor rendering.
+          '';
+        };
+
+        useEmbeddedBitmaps = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''Use embedded bitmaps in fonts like Calibri.'';
+        };
+
       };
 
     };
 
   };
-  config = mkIf cfg.enable {
-    fonts.fontconfig.confPackages = [ confPkg ];
-
-    environment.systemPackages    = [ pkgs.fontconfig ];
-    environment.etc.fonts.source  = "${fontconfigEtc}/etc/fonts/";
-  };
+  config = mkMerge [
+    (mkIf cfg.enable {
+      environment.systemPackages    = [ pkgs.fontconfig ];
+      environment.etc.fonts.source  = "${fontconfigEtc}/etc/fonts/";
+    })
+    (mkIf (cfg.enable && !cfg.penultimate.enable) {
+      fonts.fontconfig.confPackages = [ confPkg ];
+    })
+  ];
 
 }
diff --git a/nixos/modules/config/fonts/fonts.nix b/nixos/modules/config/fonts/fonts.nix
index af3d93fc1bc..0dd01df9da7 100644
--- a/nixos/modules/config/fonts/fonts.nix
+++ b/nixos/modules/config/fonts/fonts.nix
@@ -37,6 +37,7 @@ with lib;
         pkgs.xorg.fontbhlucidatypewriter75dpi
         pkgs.dejavu_fonts
         pkgs.freefont_ttf
+        pkgs.gyre-fonts # TrueType substitutes for standard PostScript fonts
         pkgs.liberation_ttf
         pkgs.xorg.fontbh100dpi
         pkgs.xorg.fontmiscmisc
diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix
index 426aaa34885..ae30a710bf6 100644
--- a/nixos/modules/config/networking.nix
+++ b/nixos/modules/config/networking.nix
@@ -178,10 +178,10 @@ in
 
     environment.etc =
       { # /etc/services: TCP/UDP port assignments.
-        "services".source = pkgs.iana_etc + "/etc/services";
+        "services".source = pkgs.iana-etc + "/etc/services";
 
         # /etc/protocols: IP protocol numbers.
-        "protocols".source  = pkgs.iana_etc + "/etc/protocols";
+        "protocols".source  = pkgs.iana-etc + "/etc/protocols";
 
         # /etc/rpc: RPC program numbers.
         "rpc".source = pkgs.glibc.out + "/etc/rpc";
@@ -251,11 +251,6 @@ in
     # Install the proxy environment variables
     environment.sessionVariables = cfg.proxy.envVars;
 
-    # The ‘ip-up’ target is kept for backwards compatibility.
-    # New services should use systemd upstream targets:
-    # See https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget/
-    systemd.targets.ip-up.description = "Services Requiring IP Connectivity (deprecated)";
-
     # This is needed when /etc/resolv.conf is being overriden by networkd
     # and other configurations. If the file is destroyed by an environment
     # activation then it must be rebuilt so that applications which interface
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index eee8db376c8..bf66994b502 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -274,6 +274,8 @@ in {
           RestartSec = "500ms";
         };
       };
+
+      environment.variables.PULSE_COOKIE = "${stateDir}/.config/pulse/cookie";
     })
   ];
 
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index 8147fed39d0..152493151fd 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -168,9 +168,6 @@ in
 
          ${cfg.extraInit}
 
-         # The setuid wrappers override other bin directories.
-         export PATH="${config.security.wrapperDir}:$PATH"
-
          # ~/bin if it exists overrides other bin directories.
          export PATH="$HOME/bin:$PATH"
        '';
diff --git a/nixos/modules/config/sysctl.nix b/nixos/modules/config/sysctl.nix
index 61b02c5ffa6..a3f7e8f722f 100644
--- a/nixos/modules/config/sysctl.nix
+++ b/nixos/modules/config/sysctl.nix
@@ -64,5 +64,9 @@ in
     # Removed under grsecurity.
     boot.kernel.sysctl."kernel.kptr_restrict" =
       if (config.boot.kernelPackages.kernel.features.grsecurity or false) then null else 1;
+
+    # Disable YAMA by default to allow easy debugging.
+    boot.kernel.sysctl."kernel.yama.ptrace_scope" = mkDefault 0;
+
   };
 }
diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl
index cbbe216e5a1..ef5e6346f02 100644
--- a/nixos/modules/config/update-users-groups.pl
+++ b/nixos/modules/config/update-users-groups.pl
@@ -6,6 +6,21 @@ use JSON;
 make_path("/var/lib/nixos", { mode => 0755 });
 
 
+# Keep track of deleted uids and gids.
+my $uidMapFile = "/var/lib/nixos/uid-map";
+my $uidMap = -e $uidMapFile ? decode_json(read_file($uidMapFile)) : {};
+
+my $gidMapFile = "/var/lib/nixos/gid-map";
+my $gidMap = -e $gidMapFile ? decode_json(read_file($gidMapFile)) : {};
+
+
+sub updateFile {
+    my ($path, $contents, $perms) = @_;
+    write_file("$path.tmp", { binmode => ':utf8', perms => $perms // 0644 }, $contents);
+    rename("$path.tmp", $path) or die;
+}
+
+
 sub hashPassword {
     my ($password) = @_;
     my $salt = "";
@@ -18,10 +33,10 @@ sub hashPassword {
 # Functions for allocating free GIDs/UIDs. FIXME: respect ID ranges in
 # /etc/login.defs.
 sub allocId {
-    my ($used, $idMin, $idMax, $up, $getid) = @_;
+    my ($used, $prevUsed, $idMin, $idMax, $up, $getid) = @_;
     my $id = $up ? $idMin : $idMax;
     while ($id >= $idMin && $id <= $idMax) {
-        if (!$used->{$id} && !defined &$getid($id)) {
+        if (!$used->{$id} && !$prevUsed->{$id} && !defined &$getid($id)) {
             $used->{$id} = 1;
             return $id;
         }
@@ -31,23 +46,36 @@ sub allocId {
     die "$0: out of free UIDs or GIDs\n";
 }
 
-my (%gidsUsed, %uidsUsed);
+my (%gidsUsed, %uidsUsed, %gidsPrevUsed, %uidsPrevUsed);
 
 sub allocGid {
-    return allocId(\%gidsUsed, 400, 499, 0, sub { my ($gid) = @_; getgrgid($gid) });
+    my ($name) = @_;
+    my $prevGid = $gidMap->{$name};
+    if (defined $prevGid && !defined $gidsUsed{$prevGid}) {
+        print STDERR "reviving group '$name' with GID $prevGid\n";
+        $gidsUsed{$prevGid} = 1;
+        return $prevGid;
+    }
+    return allocId(\%gidsUsed, \%gidsPrevUsed, 400, 499, 0, sub { my ($gid) = @_; getgrgid($gid) });
 }
 
 sub allocUid {
-    my ($isSystemUser) = @_;
+    my ($name, $isSystemUser) = @_;
     my ($min, $max, $up) = $isSystemUser ? (400, 499, 0) : (1000, 29999, 1);
-    return allocId(\%uidsUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
+    my $prevUid = $uidMap->{$name};
+    if (defined $prevUid && $prevUid >= $min && $prevUid <= $max && !defined $uidsUsed{$prevUid}) {
+        print STDERR "reviving user '$name' with UID $prevUid\n";
+        $uidsUsed{$prevUid} = 1;
+        return $prevUid;
+    }
+    return allocId(\%uidsUsed, \%uidsPrevUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
 }
 
 
 # Read the declared users/groups.
 my $spec = decode_json(read_file($ARGV[0]));
 
-# Don't allocate UIDs/GIDs that are already in use.
+# Don't allocate UIDs/GIDs that are manually assigned.
 foreach my $g (@{$spec->{groups}}) {
     $gidsUsed{$g->{gid}} = 1 if defined $g->{gid};
 }
@@ -56,6 +84,11 @@ foreach my $u (@{$spec->{users}}) {
     $uidsUsed{$u->{uid}} = 1 if defined $u->{uid};
 }
 
+# Likewise for previously used but deleted UIDs/GIDs.
+$uidsPrevUsed{$_} = 1 foreach values %{$uidMap};
+$gidsPrevUsed{$_} = 1 foreach values %{$gidMap};
+
+
 # Read the current /etc/group.
 sub parseGroup {
     chomp;
@@ -114,16 +147,18 @@ foreach my $g (@{$spec->{groups}}) {
             }
         }
     } else {
-        $g->{gid} = allocGid if !defined $g->{gid};
+        $g->{gid} = allocGid($name) if !defined $g->{gid};
         $g->{password} = "x";
     }
 
     $g->{members} = join ",", sort(keys(%members));
     $groupsOut{$name} = $g;
+
+    $gidMap->{$name} = $g->{gid};
 }
 
 # Update the persistent list of declarative groups.
-write_file($declGroupsFile, { binmode => ':utf8' }, join(" ", sort(keys %groupsOut)));
+updateFile($declGroupsFile, join(" ", sort(keys %groupsOut)));
 
 # Merge in the existing /etc/group.
 foreach my $name (keys %groupsCur) {
@@ -140,8 +175,8 @@ foreach my $name (keys %groupsCur) {
 # Rewrite /etc/group. FIXME: acquire lock.
 my @lines = map { join(":", $_->{name}, $_->{password}, $_->{gid}, $_->{members}) . "\n" }
     (sort { $a->{gid} <=> $b->{gid} } values(%groupsOut));
-write_file("/etc/group.tmp", { binmode => ':utf8' }, @lines);
-rename("/etc/group.tmp", "/etc/group") or die;
+updateFile($gidMapFile, encode_json($gidMap));
+updateFile("/etc/group", \@lines);
 system("nscd --invalidate group");
 
 # Generate a new /etc/passwd containing the declared users.
@@ -167,7 +202,7 @@ foreach my $u (@{$spec->{users}}) {
             $u->{uid} = $existing->{uid};
         }
     } else {
-        $u->{uid} = allocUid($u->{isSystemUser}) if !defined $u->{uid};
+        $u->{uid} = allocUid($name, $u->{isSystemUser}) if !defined $u->{uid};
 
         if (defined $u->{initialPassword}) {
             $u->{hashedPassword} = hashPassword($u->{initialPassword});
@@ -177,7 +212,7 @@ foreach my $u (@{$spec->{users}}) {
     }
 
     # Create a home directory.
-    if ($u->{createHome} && ! -e $u->{home}) {
+    if ($u->{createHome}) {
         make_path($u->{home}, { mode => 0700 }) if ! -e $u->{home};
         chown $u->{uid}, $u->{gid}, $u->{home};
     }
@@ -195,10 +230,12 @@ foreach my $u (@{$spec->{users}}) {
 
     $u->{fakePassword} = $existing->{fakePassword} // "x";
     $usersOut{$name} = $u;
+
+    $uidMap->{$name} = $u->{uid};
 }
 
 # Update the persistent list of declarative users.
-write_file($declUsersFile, { binmode => ':utf8' }, join(" ", sort(keys %usersOut)));
+updateFile($declUsersFile, join(" ", sort(keys %usersOut)));
 
 # Merge in the existing /etc/passwd.
 foreach my $name (keys %usersCur) {
@@ -214,8 +251,8 @@ foreach my $name (keys %usersCur) {
 # Rewrite /etc/passwd. FIXME: acquire lock.
 @lines = map { join(":", $_->{name}, $_->{fakePassword}, $_->{uid}, $_->{gid}, $_->{description}, $_->{home}, $_->{shell}) . "\n" }
     (sort { $a->{uid} <=> $b->{uid} } (values %usersOut));
-write_file("/etc/passwd.tmp", { binmode => ':utf8' }, @lines);
-rename("/etc/passwd.tmp", "/etc/passwd") or die;
+updateFile($uidMapFile, encode_json($uidMap));
+updateFile("/etc/passwd", \@lines);
 system("nscd --invalidate passwd");
 
 
@@ -242,5 +279,4 @@ foreach my $u (values %usersOut) {
     push @shadowNew, join(":", $u->{name}, $hashedPassword, "1::::::") . "\n";
 }
 
-write_file("/etc/shadow.tmp", { binmode => ':utf8', perms => 0600 }, @shadowNew);
-rename("/etc/shadow.tmp", "/etc/shadow") or die;
+updateFile("/etc/shadow", \@shadowNew, 0600);