push sheeet
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s

This commit is contained in:
Dark Steveneq
2025-10-09 14:15:47 +02:00
commit 646b892680
49168 changed files with 5897842 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
{ config, lib, ... }:
{
options = {
appstream.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to install files to support the
[AppStream metadata specification](https://www.freedesktop.org/software/appstream/docs/index.html).
'';
};
};
config = lib.mkIf config.appstream.enable {
environment.pathsToLink = [
# per component metadata
"/share/metainfo"
# legacy path for above
"/share/appdata"
];
};
}

View File

@@ -0,0 +1,271 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.console;
makeColor = i: lib.concatMapStringsSep "," (x: "0x" + lib.substring (2 * i) 2 x);
isUnicode = lib.hasSuffix "UTF-8" (lib.toUpper config.i18n.defaultLocale);
optimizedKeymap =
pkgs.runCommand "keymap"
{
nativeBuildInputs = [ pkgs.buildPackages.kbd ];
LOADKEYS_KEYMAP_PATH = "${consoleEnv pkgs.kbd}/share/keymaps/**";
preferLocalBuild = true;
}
''
loadkeys -b ${lib.optionalString isUnicode "-u"} "${cfg.keyMap}" > $out
'';
# Sadly, systemd-vconsole-setup doesn't support binary keymaps.
vconsoleConf = pkgs.writeText "vconsole.conf" ''
KEYMAP=${cfg.keyMap}
${lib.optionalString (cfg.font != null) "FONT=${cfg.font}"}
'';
consoleEnv =
kbd:
pkgs.buildEnv {
name = "console-env";
paths = [ kbd ] ++ cfg.packages;
pathsToLink = [
"/share/consolefonts"
"/share/consoletrans"
"/share/keymaps"
"/share/unimaps"
];
};
in
{
###### interface
options.console = {
enable = lib.mkEnableOption "virtual console" // {
default = true;
};
font = lib.mkOption {
type = with lib.types; nullOr (either str path);
default = null;
example = "LatArCyrHeb-16";
description = ''
The font used for the virtual consoles.
Can be `null`, a font name, or a path to a PSF font file.
Use `null` to let the kernel choose a built-in font.
The default is 8x16, and, as of Linux 5.3, Terminus 32 bold for display
resolutions of 2560x1080 and higher.
These fonts cover the [IBM437][] character set.
[IBM437]: https://en.wikipedia.org/wiki/Code_page_437
'';
};
keyMap = lib.mkOption {
type = with lib.types; either str path;
default = "us";
example = "fr";
description = ''
The keyboard mapping table for the virtual consoles.
'';
};
colors = lib.mkOption {
type = with lib.types; listOf (strMatching "[[:xdigit:]]{6}");
default = [ ];
example = [
"002b36"
"dc322f"
"859900"
"b58900"
"268bd2"
"d33682"
"2aa198"
"eee8d5"
"002b36"
"cb4b16"
"586e75"
"657b83"
"839496"
"6c71c4"
"93a1a1"
"fdf6e3"
];
description = ''
The 16 colors palette used by the virtual consoles.
Leave empty to use the default colors.
Colors must be in hexadecimal format and listed in
order from color 0 to color 15.
'';
};
packages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
description = ''
List of additional packages that provide console fonts, keymaps and
other resources for virtual consoles use.
'';
};
useXkbConfig = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
If set, configure the virtual console keymap from the xserver
keyboard settings.
'';
};
earlySetup = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Enable setting virtual console options as early as possible (in initrd).
'';
};
};
###### implementation
config = lib.mkMerge [
{
console.keyMap =
with config.services.xserver;
lib.mkIf cfg.useXkbConfig (
pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
'${pkgs.buildPackages.ckbcomp}/bin/ckbcomp' \
${
lib.optionalString (
config.environment.sessionVariables ? XKB_CONFIG_ROOT
) "-I${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
} \
-model '${xkb.model}' -layout '${xkb.layout}' \
-option '${xkb.options}' -variant '${xkb.variant}' > "$out"
''
);
}
(lib.mkIf cfg.enable (
lib.mkMerge [
{
environment.systemPackages = [ pkgs.kbd ];
# Let systemd-vconsole-setup.service do the work of setting up the
# virtual consoles.
environment.etc."vconsole.conf".source = vconsoleConf;
# Provide kbd with additional packages.
environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
boot.initrd.preLVMCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (
lib.mkBefore ''
kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
loadkmap < ${optimizedKeymap}
${lib.optionalString (cfg.earlySetup && cfg.font != null) ''
setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
''}
''
);
boot.initrd.systemd.contents = {
"/etc/vconsole.conf".source = vconsoleConf;
# Add everything if we want full console setup...
"/etc/kbd" = lib.mkIf cfg.earlySetup {
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share";
};
# ...but only the keymaps if we don't
"/etc/kbd/keymaps" = lib.mkIf (!cfg.earlySetup) {
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/keymaps";
};
};
boot.initrd.systemd.additionalUpstreamUnits = [
"systemd-vconsole-setup.service"
];
boot.initrd.systemd.storePaths = [
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-vconsole-setup"
"${config.boot.initrd.systemd.package.kbd}/bin/setfont"
"${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
]
++ lib.optionals (cfg.font != null && lib.hasPrefix builtins.storeDir cfg.font) [
"${cfg.font}"
]
++ lib.optionals (lib.hasPrefix builtins.storeDir cfg.keyMap) [
"${cfg.keyMap}"
];
systemd.additionalUpstreamSystemUnits = [
"systemd-vconsole-setup.service"
];
systemd.services.reload-systemd-vconsole-setup = {
description = "Reset console on configuration changes";
wantedBy = [ "multi-user.target" ];
restartTriggers = [
vconsoleConf
(consoleEnv pkgs.kbd)
];
reloadIfChanged = true;
serviceConfig = {
RemainAfterExit = true;
ExecStart = "${pkgs.coreutils}/bin/true";
ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
};
};
}
(lib.mkIf (cfg.colors != [ ]) {
boot.kernelParams = [
"vt.default_red=${makeColor 0 cfg.colors}"
"vt.default_grn=${makeColor 1 cfg.colors}"
"vt.default_blu=${makeColor 2 cfg.colors}"
];
})
(lib.mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
boot.initrd.extraUtilsCommands = ''
mkdir -p $out/share/consolefonts
${
if lib.substring 0 1 cfg.font == "/" then
''
font="${cfg.font}"
''
else
''
font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
''
}
if [[ $font == *.gz ]]; then
gzip -cd $font > $out/share/consolefonts/font.psf
else
cp -L $font $out/share/consolefonts/font.psf
fi
'';
})
]
))
];
imports = [
(lib.mkRenamedOptionModule [ "i18n" "consoleFont" ] [ "console" "font" ])
(lib.mkRenamedOptionModule [ "i18n" "consoleKeyMap" ] [ "console" "keyMap" ])
(lib.mkRenamedOptionModule [ "i18n" "consoleColors" ] [ "console" "colors" ])
(lib.mkRenamedOptionModule [ "i18n" "consolePackages" ] [ "console" "packages" ])
(lib.mkRenamedOptionModule [ "i18n" "consoleUseXkbConfig" ] [ "console" "useXkbConfig" ])
(lib.mkRenamedOptionModule [ "boot" "earlyVconsoleSetup" ] [ "console" "earlySetup" ])
(lib.mkRenamedOptionModule [ "boot" "extraTTYs" ] [ "console" "extraTTYs" ])
(lib.mkRemovedOptionModule [ "console" "extraTTYs" ] ''
Since NixOS switched to systemd (circa 2012), TTYs have been spawned on
demand, so there is no need to configure them manually.
'')
];
}

View File

@@ -0,0 +1,67 @@
{
config,
lib,
pkgs,
...
}:
{
options = {
environment.enableDebugInfo = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Some NixOS packages provide debug symbols. However, these are
not included in the system closure by default to save disk
space. Enabling this option causes the debug symbols to appear
in {file}`/run/current-system/sw/lib/debug/.build-id`,
where tools such as {command}`gdb` can find them.
If you need debug symbols for a package that doesn't
provide them by default, you can enable them as follows:
nixpkgs.config.packageOverrides = pkgs: {
hello = pkgs.hello.overrideAttrs (oldAttrs: {
separateDebugInfo = true;
});
};
'';
};
environment.debuginfodServers = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = ''
List of urls of debuginfod servers for tools like {command}`gdb` and {command}`valgrind` to use.
Unrelated to {option}`environment.enableDebugInfo`.
'';
};
};
config = lib.mkMerge [
(lib.mkIf config.environment.enableDebugInfo {
# FIXME: currently disabled because /lib is already in
# environment.pathsToLink, and we can't have both.
#environment.pathsToLink = [ "/lib/debug/.build-id" ];
environment.extraOutputsToInstall = [ "debug" ];
environment.variables.NIX_DEBUG_INFO_DIRS = [ "/run/current-system/sw/lib/debug" ];
})
(lib.mkIf (config.environment.debuginfodServers != [ ]) {
environment.variables.DEBUGINFOD_URLS = lib.strings.concatStringsSep " " config.environment.debuginfodServers;
environment.systemPackages = [
# valgrind support requires debuginfod-find on PATH
(lib.getBin pkgs.elfutils)
];
environment.etc."gdb/gdbinit.d/nixseparatedebuginfod2.gdb".text = "set debuginfod enabled on";
})
];
}

View File

@@ -0,0 +1,56 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.fanout;
mknodCmds =
n:
lib.lists.imap0 (i: s: "mknod /dev/fanout${builtins.toString i} c $MAJOR ${builtins.toString i}") (
lib.lists.replicate n ""
);
in
{
options.services.fanout = {
enable = lib.mkEnableOption "fanout";
fanoutDevices = lib.mkOption {
type = lib.types.int;
default = 1;
description = "Number of /dev/fanout devices";
};
bufferSize = lib.mkOption {
type = lib.types.int;
default = 16384;
description = "Size of /dev/fanout buffer in bytes";
};
};
config = lib.mkIf cfg.enable {
boot.extraModulePackages = [ config.boot.kernelPackages.fanout.out ];
boot.kernelModules = [ "fanout" ];
boot.extraModprobeConfig = ''
options fanout buffersize=${builtins.toString cfg.bufferSize}
'';
systemd.services.fanout = {
description = "Bring up /dev/fanout devices";
script = ''
MAJOR=$(${pkgs.gnugrep}/bin/grep fanout /proc/devices | ${pkgs.gawk}/bin/awk '{print $1}')
${lib.strings.concatLines (mknodCmds cfg.fanoutDevices)}
'';
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
User = "root";
RemainAfterExit = "yes";
Restart = "no";
};
};
};
}

View File

@@ -0,0 +1,570 @@
/*
Configuration files are linked to /etc/fonts/conf.d/
This module generates a package containing configuration files and link it in /etc/fonts.
Fontconfig reads files in folder name / file name order, so the number prepended to the configuration file name decide the order of parsing.
Low number means high priority.
NOTE: Please take extreme care when adjusting the default settings of this module.
People care a lot, and I mean A LOT, about their font rendering, and you will be
The Person That Broke It if it changes in a way people don't like.
See prior art:
- https://github.com/NixOS/nixpkgs/pull/194594
- https://github.com/NixOS/nixpkgs/pull/222236
- https://github.com/NixOS/nixpkgs/pull/222689
And do not repeat our mistakes.
- @K900, March 2023
*/
{
config,
pkgs,
lib,
...
}:
let
cfg = config.fonts.fontconfig;
fcBool = x: "<bool>" + (lib.boolToString x) + "</bool>";
pkg = pkgs.fontconfig;
# configuration file to read fontconfig cache
# priority 0
cacheConf = makeCacheConf { };
# generate the font cache setting file
# When cross-compiling, we cant generate the cache, so we skip the
# <cachedir> part. fontconfig still works but is a little slower in
# looking things up.
makeCacheConf =
{ }:
let
makeCache =
fontconfig:
pkgs.makeFontsCache {
inherit fontconfig;
fontDirectories = config.fonts.packages;
};
cache = makeCache pkgs.fontconfig;
cache32 = makeCache pkgs.pkgsi686Linux.fontconfig;
in
pkgs.writeText "fc-00-nixos-cache.conf" ''
<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
<fontconfig>
<!-- Font directories -->
${lib.concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.packages)}
${lib.optionalString (pkgs.stdenv.hostPlatform.emulatorAvailable pkgs.buildPackages) ''
<!-- Pre-generated font caches -->
<cachedir>${cache}</cachedir>
${lib.optionalString (pkgs.stdenv.hostPlatform.isx86_64 && cfg.cache32Bit) ''
<cachedir>${cache32}</cachedir>
''}
''}
</fontconfig>
'';
# rendering settings configuration file
# priority 10
renderConf = pkgs.writeText "fc-10-nixos-rendering.conf" ''
<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'urn:fontconfig: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>
</match>
</fontconfig>
'';
# local configuration file
localConf = pkgs.writeText "fc-local.conf" cfg.localConf;
# default fonts configuration file
# priority 52
defaultFontsConf =
let
genDefault =
fonts: name:
lib.optionalString (fonts != [ ]) ''
<alias binding="same">
<family>${name}</family>
<prefer>
${lib.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 'urn:fontconfig:fonts.dtd'>
<fontconfig>
<!-- Default fonts -->
${genDefault cfg.defaultFonts.sansSerif "sans-serif"}
${genDefault cfg.defaultFonts.serif "serif"}
${genDefault cfg.defaultFonts.monospace "monospace"}
${genDefault cfg.defaultFonts.emoji "emoji"}
</fontconfig>
'';
# bitmap font options
# priority 53
rejectBitmaps = pkgs.writeText "fc-53-no-bitmaps.conf" ''
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
${lib.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 "urn:fontconfig:fonts.dtd">
<fontconfig>
<!-- Reject Type 1 fonts -->
<selectfont>
<rejectfont>
<pattern>
<patelt name="fontformat"><string>Type 1</string></patelt>
</pattern>
</rejectfont>
</selectfont>
</fontconfig>
'';
# Replace default linked config with a different variant
replaceDefaultConfig = defaultConfig: newConfig: ''
rm $dst/${defaultConfig}
ln -s ${pkg.out}/share/fontconfig/conf.avail/${newConfig} \
$dst/
'';
# fontconfig configuration package
confPkg =
pkgs.runCommand "fontconfig-conf"
{
preferLocalBuild = true;
}
''
dst=$out/etc/fonts/conf.d
mkdir -p $dst
# fonts.conf
ln -s ${pkg.out}/etc/fonts/fonts.conf \
$dst/../fonts.conf
# TODO: remove this legacy symlink once people stop using packages built before #95358 was merged
mkdir -p $out/etc/fonts/2.11
ln -s /etc/fonts/fonts.conf \
$out/etc/fonts/2.11/fonts.conf
# fontconfig default config files
ln -s ${pkg.out}/etc/fonts/conf.d/*.conf \
$dst/
${lib.optionalString (!cfg.antialias) (
replaceDefaultConfig "10-yes-antialias.conf" "10-no-antialias.conf"
)}
${lib.optionalString (cfg.hinting.style != "slight") (
replaceDefaultConfig "10-hinting-slight.conf" "10-hinting-${cfg.hinting.style}.conf"
)}
${lib.optionalString (cfg.subpixel.rgba != "none") (
replaceDefaultConfig "10-sub-pixel-none.conf" "10-sub-pixel-${cfg.subpixel.rgba}.conf"
)}
${lib.optionalString (cfg.subpixel.lcdfilter != "default") (
replaceDefaultConfig "11-lcdfilter-default.conf" "11-lcdfilter-${cfg.subpixel.lcdfilter}.conf"
)}
# 00-nixos-cache.conf
ln -s ${cacheConf} $dst/00-nixos-cache.conf
# 10-nixos-rendering.conf
ln -s ${renderConf} $dst/10-nixos-rendering.conf
# 50-user.conf
${lib.optionalString (!cfg.includeUserConf) ''
rm $dst/50-user.conf
''}
# local.conf (indirect priority 51)
${lib.optionalString (cfg.localConf != "") ''
ln -s ${localConf} $dst/../local.conf
''}
# 52-nixos-default-fonts.conf
ln -s ${defaultFontsConf} $dst/52-nixos-default-fonts.conf
# 53-no-bitmaps.conf
ln -s ${rejectBitmaps} $dst/53-no-bitmaps.conf
${lib.optionalString (!cfg.allowType1) ''
# 53-nixos-reject-type1.conf
ln -s ${rejectType1} $dst/53-nixos-reject-type1.conf
''}
'';
# Package with configuration files
# this merge all the packages in the fonts.fontconfig.confPackages list
fontconfigEtc = pkgs.buildEnv {
name = "fontconfig-etc";
paths = cfg.confPackages;
ignoreCollisions = true;
};
fontconfigNote = "Consider manually configuring fonts.fontconfig according to personal preference.";
in
{
imports = [
(lib.mkRenamedOptionModule
[ "fonts" "fontconfig" "ultimate" "allowBitmaps" ]
[ "fonts" "fontconfig" "allowBitmaps" ]
)
(lib.mkRenamedOptionModule
[ "fonts" "fontconfig" "ultimate" "allowType1" ]
[ "fonts" "fontconfig" "allowType1" ]
)
(lib.mkRenamedOptionModule
[ "fonts" "fontconfig" "ultimate" "useEmbeddedBitmaps" ]
[ "fonts" "fontconfig" "useEmbeddedBitmaps" ]
)
(lib.mkRenamedOptionModule
[ "fonts" "fontconfig" "ultimate" "forceAutohint" ]
[ "fonts" "fontconfig" "forceAutohint" ]
)
(lib.mkRenamedOptionModule
[ "fonts" "fontconfig" "ultimate" "renderMonoTTFAsBitmap" ]
[ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ]
)
(lib.mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "")
(lib.mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "")
(lib.mkRemovedOptionModule [ "fonts" "fontconfig" "dpi" ] "Use display server-specific options")
(lib.mkRemovedOptionModule [ "hardware" "video" "hidpi" "enable" ] fontconfigNote)
(lib.mkRemovedOptionModule [ "fonts" "optimizeForVeryHighDPI" ] fontconfigNote)
]
++ lib.forEach [ "enable" "substitutions" "preset" ] (
opt:
lib.mkRemovedOptionModule [ "fonts" "fontconfig" "ultimate" "${opt}" ] ''
The fonts.fontconfig.ultimate module and configuration is obsolete.
The repository has since been archived and activity has ceased.
https://github.com/bohoomil/fontconfig-ultimate/issues/171.
No action should be needed for font configuration, as the fonts.fontconfig
module is already used by default.
''
);
options = {
fonts = {
fontconfig = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
If enabled, a Fontconfig configuration file will be built
pointing to a set of default fonts. If you don't care about
running X11 applications or any other program that uses
Fontconfig, you can turn this option off and prevent a
dependency on all those fonts.
'';
};
confPackages = lib.mkOption {
internal = true;
type = with lib.types; listOf path;
default = [ ];
description = ''
Fontconfig configuration packages.
'';
};
antialias = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Enable font antialiasing. At high resolution (> 200 DPI),
antialiasing has no visible effect; users of such displays may want
to disable this option.
'';
};
localConf = lib.mkOption {
type = lib.types.lines;
default = "";
description = ''
System-wide customization file contents, has higher priority than
`defaultFonts` settings.
'';
};
defaultFonts = {
monospace = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "DejaVu Sans Mono" ];
description = ''
System-wide default monospace font(s). Multiple fonts may be
listed in case multiple languages must be supported.
'';
};
sansSerif = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "DejaVu Sans" ];
description = ''
System-wide default sans serif font(s). Multiple fonts may be
listed in case multiple languages must be supported.
'';
};
serif = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "DejaVu Serif" ];
description = ''
System-wide default serif font(s). Multiple fonts may be listed
in case multiple languages must be supported.
'';
};
emoji = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "Noto Color Emoji" ];
description = ''
System-wide default emoji font(s). Multiple fonts may be listed
in case a font does not support all emoji.
Note that fontconfig matches color emoji fonts preferentially,
so if you want to use a black and white font while having
a color font installed (eg. Noto Color Emoji installed alongside
Noto Emoji), fontconfig will still choose the color font even
when it is later in the list.
'';
};
};
hinting = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
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.
'';
};
autohint = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable the autohinter in place of the default interpreter.
The results are usually lower quality than correctly-hinted
fonts, but better than unhinted fonts.
'';
};
style = lib.mkOption {
type = lib.types.enum [
"none"
"slight"
"medium"
"full"
];
default = "slight";
description = ''
Hintstyle is the amount of font reshaping done to line up
to the grid.
slight will make the font more fuzzy to line up to the grid but
will be better in retaining font shape, while full will be a
crisp font that aligns well to the pixel grid but will lose a
greater amount of font shape.
'';
apply =
val:
let
from = "fonts.fontconfig.hinting.style";
val' = lib.removePrefix "hint" val;
warning = "The option `${from}` contains a deprecated value `${val}`. Use `${val'}` instead.";
in
lib.warnIf (lib.hasPrefix "hint" val) warning val';
};
};
includeUserConf = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Include the user configuration from
{file}`~/.config/fontconfig/fonts.conf` or
{file}`~/.config/fontconfig/conf.d`.
'';
};
subpixel = {
rgba = lib.mkOption {
default = "none";
type = lib.types.enum [
"rgb"
"bgr"
"vrgb"
"vbgr"
"none"
];
description = ''
Subpixel order. The overwhelming majority of displays are
`rgb` in their normal orientation. Select
`vrgb` for mounting such a display 90 degrees
clockwise from its normal orientation or `vbgr`
for mounting 90 degrees counter-clockwise. Select
`bgr` 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
`bgr`.
'';
};
lcdfilter = lib.mkOption {
default = "default";
type = lib.types.enum [
"none"
"default"
"light"
"legacy"
];
description = ''
FreeType LCD filter. At high resolution (> 200 DPI), LCD filtering
has no visible effect; users of such displays may want to select
`none`.
'';
};
};
cache32Bit = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Generate system fonts cache for 32-bit applications.
'';
};
allowBitmaps = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Allow bitmap fonts. Set to `false` to ban all
bitmap fonts.
'';
};
allowType1 = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Allow Type-1 fonts. Default is `false` because of
poor rendering.
'';
};
useEmbeddedBitmaps = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Use embedded bitmaps in fonts like Calibri.";
};
};
};
};
config = lib.mkMerge [
(lib.mkIf cfg.enable {
environment.systemPackages = [ pkgs.fontconfig ];
environment.etc.fonts.source = "${fontconfigEtc}/etc/fonts/";
security.apparmor.includes."abstractions/fonts" = ''
# fonts.conf
r ${pkg.out}/etc/fonts/fonts.conf,
# fontconfig default config files
r ${pkg.out}/etc/fonts/conf.d/*.conf,
# 00-nixos-cache.conf
r ${cacheConf},
# 10-nixos-rendering.conf
r ${renderConf},
# 50-user.conf
${lib.optionalString cfg.includeUserConf ''
r ${pkg.out}/etc/fonts/conf.d.bak/50-user.conf,
''}
# local.conf (indirect priority 51)
${lib.optionalString (cfg.localConf != "") ''
r ${localConf},
''}
# 52-nixos-default-fonts.conf
r ${defaultFontsConf},
# 53-no-bitmaps.conf
r ${rejectBitmaps},
${lib.optionalString (!cfg.allowType1) ''
# 53-nixos-reject-type1.conf
r ${rejectType1},
''}
'';
})
(lib.mkIf cfg.enable {
fonts.fontconfig.confPackages = [ confPkg ];
})
];
}

View File

@@ -0,0 +1,85 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.fonts.fontDir;
x11Fonts = pkgs.callPackage (
{
runCommand,
gzip,
xorg,
}:
runCommand "X11-fonts"
{
preferLocalBuild = true;
nativeBuildInputs = [
gzip
xorg.mkfontscale
xorg.mkfontdir
];
}
''
mkdir -p "$out/share/X11/fonts"
font_regexp='.*\.\(ttf\|ttc\|otb\|otf\|pcf\|pfa\|pfb\|bdf\)\(\.gz\)?'
find ${toString config.fonts.packages} -regex "$font_regexp" \
-exec ln -sf -t "$out/share/X11/fonts" '{}' \;
cd "$out/share/X11/fonts"
${lib.optionalString cfg.decompressFonts ''
gunzip -f *.gz
''}
mkfontscale
mkfontdir
cat $(find ${pkgs.xorg.fontalias}/ -name fonts.alias) >fonts.alias
''
) { };
in
{
options = {
fonts.fontDir = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to create a directory with links to all fonts in
{file}`/run/current-system/sw/share/X11/fonts`.
'';
};
decompressFonts = lib.mkOption {
type = lib.types.bool;
default = config.programs.xwayland.enable;
defaultText = lib.literalExpression "config.programs.xwayland.enable";
description = ''
Whether to decompress fonts in
{file}`/run/current-system/sw/share/X11/fonts`.
'';
};
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ x11Fonts ];
environment.pathsToLink = [ "/share/X11/fonts" ];
services.xserver.filesSection = ''
FontPath "${x11Fonts}/share/X11/fonts"
'';
};
imports = [
(lib.mkRenamedOptionModule [ "fonts" "enableFontDir" ] [ "fonts" "fontDir" "enable" ])
];
}

View File

@@ -0,0 +1,25 @@
{
config,
lib,
pkgs,
...
}:
{
options = {
fonts.enableGhostscriptFonts = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to add the fonts provided by Ghostscript (such as
various URW fonts and the Base-14 Postscript fonts) to the
list of system fonts, making them available to X11
applications.
'';
};
};
config = lib.mkIf config.fonts.enableGhostscriptFonts {
fonts.packages = [ pkgs.ghostscript.fonts ];
};
}

View File

@@ -0,0 +1,54 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.fonts;
in
{
imports = [
(lib.mkRemovedOptionModule [
"fonts"
"enableCoreFonts"
] "Use fonts.packages = [ pkgs.corefonts ]; instead.")
(lib.mkRenamedOptionModule [ "fonts" "enableDefaultFonts" ] [ "fonts" "enableDefaultPackages" ])
(lib.mkRenamedOptionModule [ "fonts" "fonts" ] [ "fonts" "packages" ])
];
options = {
fonts = {
packages = lib.mkOption {
type = with lib.types; listOf path;
default = [ ];
example = lib.literalExpression "[ pkgs.dejavu_fonts ]";
description = "List of primary font packages.";
};
enableDefaultPackages = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable a basic set of fonts providing several styles
and families and reasonable coverage of Unicode.
'';
};
};
};
config = {
fonts.packages = lib.mkIf cfg.enableDefaultPackages (
with pkgs;
[
dejavu_fonts
freefont_ttf
gyre-fonts # TrueType substitutes for standard PostScript fonts
liberation_ttf
unifont
noto-fonts-color-emoji
]
);
};
}

View File

@@ -0,0 +1,120 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.networking.getaddrinfo;
formatTableEntries =
tableName: table:
if table == null then
[ ]
else
lib.mapAttrsToList (cidr: val: "${tableName} ${cidr} ${toString val}") table;
gaiConfText = lib.concatStringsSep "\n" (
[
"# Generated by NixOS module networking.getaddrinfo"
"# Do not edit manually!"
"reload ${if cfg.reload then "yes" else "no"}"
]
++ formatTableEntries "label" cfg.label
++ formatTableEntries "precedence" cfg.precedence
++ formatTableEntries "scopev4" cfg.scopev4
);
in
{
options.networking.getaddrinfo = {
enable = lib.mkOption {
type = lib.types.bool;
default = pkgs.stdenv.hostPlatform.libc == "glibc";
defaultText = lib.literalExpression ''
pkgs.stdenv.hostPlatform.libc == "glibc"
'';
description = ''
Enables custom address sorting configuration for {manpage}`getaddrinfo(3)` according to RFC 3484.
This option generates a {file}`/etc/gai.conf` file to override the default address sorting tables,
as described in {manpage}`gai.conf(5)`.
This setting is only applicable when using the GNU C Library (glibc).
It has no effect with other libc implementations.
'';
};
reload = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Determines whether a process should detect changes to the configuration file since it was last read.
If enabled, the file is re-read automatically. This may cause issues in multithreaded applications
and is generally discouraged.
'';
};
label = lib.mkOption {
type = lib.types.nullOr (lib.types.attrsOf lib.types.int);
default = null;
description = ''
Adds entries to the label table, as described in section 2.1 of RFC 3484.
If any label entries are provided, the glibcs default label table is ignored.
'';
example = {
"::/0" = 1;
"2002::/16" = 2;
"::/96" = 3;
"::ffff:0:0/96" = 4;
"fec0::/10" = 5;
"fc00::/7" = 6;
"2001:0::/32" = 7;
};
};
precedence = lib.mkOption {
type = lib.types.nullOr (lib.types.attrsOf lib.types.int);
default = null;
description = ''
Similar to {option}`networking.getaddrinfo.label`, but this option
defines entries for the precedence table instead.
See sections 2.1 and 10.3 of RFC 3484 for details.
Providing any value will disable the glibc's default precedence table.
'';
example = {
"::1/128" = 50;
"::/0" = 40;
"2002::/16" = 30;
"::/96" = 20;
"::ffff:0:0/96" = 10;
};
};
scopev4 = lib.mkOption {
type = lib.types.nullOr (lib.types.attrsOf lib.types.int);
default = null;
description = ''
Adds custom rules to the IPv4 scope table.
By default, the scope IDs described in section 3.2 of RFC 6724 are used.
Modifying these values is rarely necessary.
'';
example = {
"::ffff:169.254.0.0/112" = 2;
"::ffff:127.0.0.0/104" = 2;
"::ffff:0.0.0.0/96" = 14;
};
};
};
config = lib.mkIf cfg.enable {
environment.etc."gai.conf".text = gaiConfText;
};
meta.maintainers = with lib.maintainers; [ moraxyc ];
}

View File

@@ -0,0 +1,88 @@
{
config,
lib,
pkgs,
...
}:
{
options = {
gtk.iconCache.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.xserver.enable;
defaultText = lib.literalExpression "config.services.xserver.enable";
description = ''
Whether to build icon theme caches for GTK applications.
'';
};
};
config = lib.mkIf config.gtk.iconCache.enable {
# (Re)build icon theme caches
# ---------------------------
# Each icon theme has its own cache. The difficult is that many
# packages may contribute with icons to the same theme by installing
# some icons.
#
# For instance, on my current NixOS system, the following packages
# (among many others) have icons installed into the hicolor icon
# theme: hicolor-icon-theme, psensor, wpa_gui, caja, etc.
#
# As another example, the mate icon theme has icons installed by the
# packages mate-icon-theme, mate-settings-daemon, and libmateweather.
#
# The HighContrast icon theme also has icons from different packages,
# like gnome-theme-extras and meld.
# When the cache is built all of its icons has to be known. How to
# implement this?
#
# I think that most themes have all icons installed by only one
# package. On my system there are 71 themes installed. Only 3 of them
# have icons installed from more than one package.
#
# If the main package of the theme provides a cache, presumably most
# of its icons will be available to applications without running this
# module. But additional icons offered by other packages will not be
# available. Therefore I think that it is good that the main theme
# package installs a cache (although it does not completely fixes the
# situation for packages installed with nix-env).
#
# The module solution presented here keeps the cache when there is
# only one package contributing with icons to the theme. Otherwise it
# rebuilds the cache taking into account the icons provided all
# packages.
environment.extraSetup = ''
# For each icon theme directory ...
find $out/share/icons -exec test -d {} ';' -mindepth 1 -maxdepth 1 -print0 | while read -d $'\0' themedir
do
# In order to build the cache, the theme dir should be
# writable. When the theme dir is a symbolic link to somewhere
# in the nix store it is not writable and it means that only
# one package is contributing to the theme. If it already has
# a cache, no rebuild is needed. Otherwise a cache has to be
# built, and to be able to do that we first remove the
# symbolic link and make a directory, and then make symbolic
# links from the original directory into the new one.
if [ ! -w "$themedir" -a -L "$themedir" -a ! -r "$themedir"/icon-theme.cache ]; then
name=$(basename "$themedir")
path=$(readlink -f "$themedir")
rm "$themedir"
mkdir -p "$themedir"
ln -s "$path"/* "$themedir"/
fi
# (Re)build the cache if the theme dir is writable, replacing any
# existing cache for the theme
if [ -w "$themedir" ]; then
rm -f "$themedir"/icon-theme.cache
${pkgs.buildPackages.gtk3.out}/bin/gtk-update-icon-cache --ignore-theme-index "$themedir"
fi
done
'';
};
}

View File

@@ -0,0 +1,189 @@
{
config,
lib,
pkgs,
...
}:
let
sanitizeUTF8Capitalization =
lang: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] lang);
aggregatedLocales = [
"${config.i18n.defaultLocale}/${config.i18n.defaultCharset}"
]
++ lib.pipe config.i18n.extraLocaleSettings [
# See description of extraLocaleSettings for why is this ignored here.
(lib.filterAttrs (n: v: n != "LANGUAGE"))
(lib.mapAttrs (n: v: (sanitizeUTF8Capitalization v)))
(lib.mapAttrsToList (LCRole: lang: lang + "/" + (config.i18n.localeCharsets.${LCRole} or "UTF-8")))
]
++ (builtins.map sanitizeUTF8Capitalization (
lib.optionals (builtins.isList config.i18n.extraLocales) config.i18n.extraLocales
))
++ (lib.optional (builtins.isString config.i18n.extraLocales) config.i18n.extraLocales);
in
{
###### interface
options = {
i18n = {
glibcLocales = lib.mkOption {
type = lib.types.path;
default = pkgs.glibcLocales.override {
allLocales = lib.any (x: x == "all") config.i18n.supportedLocales;
locales = config.i18n.supportedLocales;
};
defaultText = lib.literalExpression ''
pkgs.glibcLocales.override {
allLocales = lib.any (x: x == "all") config.i18n.supportedLocales;
locales = config.i18n.supportedLocales;
}
'';
example = lib.literalExpression "pkgs.glibcLocales";
description = ''
Customized pkg.glibcLocales package.
Changing this option can disable handling of i18n.defaultLocale
and supportedLocale.
'';
};
defaultLocale = lib.mkOption {
type = lib.types.str;
default = "en_US.UTF-8";
example = "nl_NL.UTF-8";
description = ''
The default locale. It determines the language for program messages,
the format for dates and times, sort order, and so on. Setting the
default character set is done via {option}`i18n.defaultCharset`.
'';
};
defaultCharset = lib.mkOption {
type = lib.types.str;
default = "UTF-8";
example = "ISO-8859-8";
description = ''
The default locale character set.
'';
};
extraLocales = lib.mkOption {
type = lib.types.either (lib.types.listOf lib.types.str) (lib.types.enum [ "all" ]);
default = [ ];
example = [ "nl_NL.UTF-8/UTF-8" ];
description = ''
Additional locales that the system should support, besides the ones
configured with {option}`i18n.defaultLocale` and
{option}`i18n.extraLocaleSettings`.
Set this to `"all"` to install all available locales.
'';
};
extraLocaleSettings = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
example = {
LC_MESSAGES = "en_US.UTF-8";
LC_TIME = "de_DE.UTF-8";
};
description = ''
A set of additional system-wide locale settings other than `LANG`
which can be configured with {option}`i18n.defaultLocale`. Note that
the `/UTF-8` suffix used in {option}`i18n.extraLocales` indicates a
character set, and it must not be added manually here. To use a
non-`UTF-8` character set such as ISO-XXXX-8, the
{option}`i18n.localeCharsets` can be used.
Note that if the [`LANGUAGE`
key](https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html)
is used in this option, it is ignored when computing the locales
required to be installed, because the possible values of this key are
more diverse and flexible then the others.
'';
};
localeCharsets = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
example = {
LC_MESSAGES = "ISO-8859-15";
LC_TIME = "ISO-8859-1";
};
description = ''
Per each {option}`i18n.extraLocaleSettings`, choose the character set
to use for it. Essentially defaults to UTF-8 for all of them.
'';
};
supportedLocales = lib.mkOption {
type = lib.types.listOf lib.types.str;
visible = false;
default = lib.unique (
[
"C.UTF-8/UTF-8"
"en_US.UTF-8/UTF-8"
]
++ aggregatedLocales
);
example = [
"en_US.UTF-8/UTF-8"
"nl_NL.UTF-8/UTF-8"
"nl_NL/ISO-8859-1"
];
description = ''
List of locales that the system should support. The value
`"all"` means that all locales supported by
Glibc will be installed. A full list of supported locales
can be found at <https://sourceware.org/git/?p=glibc.git;a=blob;f=localedata/SUPPORTED>.
'';
};
};
};
###### implementation
config = {
warnings =
lib.optional
(
!(
(lib.subtractLists config.i18n.supportedLocales aggregatedLocales) == [ ]
|| lib.any (x: x == "all") config.i18n.supportedLocales
)
)
''
`i18n.supportedLocales` is deprecated in favor of `i18n.extraLocales`,
and it seems you are using `i18n.supportedLocales` and forgot to
include some locales specified in `i18n.defaultLocale`,
`i18n.extraLocales` or `i18n.extraLocaleSettings`.
If you're trying to install additional locales not specified in
`i18n.defaultLocale` or `i18n.extraLocaleSettings`, consider adding
only those locales to `i18n.extraLocales`.
'';
environment.systemPackages =
# We increase the priority a little, so that plain glibc in systemPackages can't win.
lib.optional (config.i18n.supportedLocales != [ ]) (lib.setPrio (-1) config.i18n.glibcLocales);
environment.sessionVariables = {
LANG = config.i18n.defaultLocale;
LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
}
// config.i18n.extraLocaleSettings;
systemd.globalEnvironment = lib.mkIf (config.i18n.supportedLocales != [ ]) {
LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive";
};
# /etc/locale.conf is used by systemd.
environment.etc."locale.conf".source = pkgs.writeText "locale.conf" ''
LANG=${config.i18n.defaultLocale}
${lib.concatStringsSep "\n" (
lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings
)}
'';
};
}

View File

@@ -0,0 +1,28 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.networking.iproute2;
in
{
options.networking.iproute2 = {
enable = lib.mkEnableOption "copying IP route configuration files";
rttablesExtraConfig = lib.mkOption {
type = lib.types.lines;
default = "";
description = ''
Verbatim lines to add to /etc/iproute2/rt_tables
'';
};
};
config = lib.mkIf cfg.enable {
environment.etc."iproute2/rt_tables.d/nixos.conf" = {
mode = "0644";
text = cfg.rttablesExtraConfig;
};
};
}

View File

@@ -0,0 +1,330 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
mkEnableOption
mkIf
mkMerge
mkOption
mkRenamedOptionModule
types
;
cfg = config.users.ldap;
# Careful: OpenLDAP seems to be very picky about the indentation of
# this file. Directives HAVE to start in the first column!
ldapConfig = {
target = "ldap.conf";
source = pkgs.writeText "ldap.conf" ''
uri ${config.users.ldap.server}
base ${config.users.ldap.base}
timelimit ${toString config.users.ldap.timeLimit}
bind_timelimit ${toString config.users.ldap.bind.timeLimit}
bind_policy ${config.users.ldap.bind.policy}
${lib.optionalString config.users.ldap.useTLS ''
ssl start_tls
''}
${lib.optionalString (config.users.ldap.bind.distinguishedName != "") ''
binddn ${config.users.ldap.bind.distinguishedName}
''}
${lib.optionalString (cfg.extraConfig != "") cfg.extraConfig}
'';
};
nslcdConfig = pkgs.writeText "nslcd.conf" ''
uri ${cfg.server}
base ${cfg.base}
timelimit ${toString cfg.timeLimit}
bind_timelimit ${toString cfg.bind.timeLimit}
${lib.optionalString (cfg.bind.distinguishedName != "") "binddn ${cfg.bind.distinguishedName}"}
${lib.optionalString (cfg.daemon.rootpwmoddn != "") "rootpwmoddn ${cfg.daemon.rootpwmoddn}"}
${lib.optionalString (cfg.daemon.extraConfig != "") cfg.daemon.extraConfig}
'';
# nslcd normally reads configuration from /etc/nslcd.conf.
# this file might contain secrets. We append those at runtime,
# so redirect its location to something more temporary.
nslcdWrapped = pkgs.runCommand "nslcd-wrapped" { nativeBuildInputs = [ pkgs.makeWrapper ]; } ''
mkdir -p $out/bin
makeWrapper ${pkgs.nss_pam_ldapd}/sbin/nslcd $out/bin/nslcd \
--set LD_PRELOAD "${pkgs.libredirect}/lib/libredirect.so" \
--set NIX_REDIRECTS "/etc/nslcd.conf=/run/nslcd/nslcd.conf"
'';
in
{
###### interface
options = {
users.ldap = {
enable = mkEnableOption "authentication against an LDAP server";
loginPam = mkOption {
type = types.bool;
default = true;
description = "Whether to include authentication against LDAP in login PAM.";
};
nsswitch = mkOption {
type = types.bool;
default = true;
description = "Whether to include lookup against LDAP in NSS.";
};
server = mkOption {
type = types.str;
example = "ldap://ldap.example.org/";
description = "The URL of the LDAP server.";
};
base = mkOption {
type = types.str;
example = "dc=example,dc=org";
description = "The distinguished name of the search base.";
};
useTLS = mkOption {
type = types.bool;
default = false;
description = ''
If enabled, use TLS (encryption) over an LDAP (port 389)
connection. The alternative is to specify an LDAPS server (port
636) in {option}`users.ldap.server` or to forego
security.
'';
};
timeLimit = mkOption {
default = 0;
type = types.int;
description = ''
Specifies the time limit (in seconds) to use when performing
searches. A value of zero (0), which is the default, is to
wait indefinitely for searches to be completed.
'';
};
daemon = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Whether to let the nslcd daemon (nss-pam-ldapd) handle the
LDAP lookups for NSS and PAM. This can improve performance,
and if you need to bind to the LDAP server with a password,
it increases security, since only the nslcd user needs to
have access to the bindpw file, not everyone that uses NSS
and/or PAM. If this option is enabled, a local nscd user is
created automatically, and the nslcd service is started
automatically when the network get up.
'';
};
extraConfig = mkOption {
default = "";
type = types.lines;
description = ''
Extra configuration options that will be added verbatim at
the end of the nslcd configuration file ({manpage}`nslcd.conf(5)`).
'';
};
rootpwmoddn = mkOption {
default = "";
example = "cn=admin,dc=example,dc=com";
type = types.str;
description = ''
The distinguished name to use to bind to the LDAP server
when the root user tries to modify a user's password.
'';
};
rootpwmodpwFile = mkOption {
default = "";
example = "/run/keys/nslcd.rootpwmodpw";
type = types.str;
description = ''
The path to a file containing the credentials with which to bind to
the LDAP server if the root user tries to change a user's password.
'';
};
};
bind = {
distinguishedName = mkOption {
default = "";
example = "cn=admin,dc=example,dc=com";
type = types.str;
description = ''
The distinguished name to bind to the LDAP server with. If this
is not specified, an anonymous bind will be done.
'';
};
passwordFile = mkOption {
default = "/etc/ldap/bind.password";
type = types.str;
description = ''
The path to a file containing the credentials to use when binding
to the LDAP server (if not binding anonymously).
'';
};
timeLimit = mkOption {
default = 30;
type = types.int;
description = ''
Specifies the time limit (in seconds) to use when connecting
to the directory server. This is distinct from the time limit
specified in {option}`users.ldap.timeLimit` and affects
the initial server connection only.
'';
};
policy = mkOption {
default = "hard_open";
type = types.enum [
"hard_open"
"hard_init"
"soft"
];
description = ''
Specifies the policy to use for reconnecting to an unavailable
LDAP server. The default is `hard_open`, which
reconnects if opening the connection to the directory server
failed. By contrast, `hard_init` reconnects if
initializing the connection failed. Initializing may not
actually contact the directory server, and it is possible that
a malformed configuration file will trigger reconnection. If
`soft` is specified, then
`nss_ldap` will return immediately on server
failure. All hard reconnect policies block with exponential
backoff before retrying.
'';
};
};
extraConfig = mkOption {
default = "";
type = types.lines;
description = ''
Extra configuration options that will be added verbatim at
the end of the ldap configuration file ({manpage}`ldap.conf(5)`).
If {option}`users.ldap.daemon` is enabled, this
configuration will not be used. In that case, use
{option}`users.ldap.daemon.extraConfig` instead.
'';
};
};
};
###### implementation
config = mkIf cfg.enable {
environment.etc = lib.optionalAttrs (!cfg.daemon.enable) {
"ldap.conf" = ldapConfig;
};
system.nssModules = mkIf cfg.nsswitch (
lib.singleton (if cfg.daemon.enable then pkgs.nss_pam_ldapd else pkgs.nss_ldap)
);
system.nssDatabases.group = lib.optional cfg.nsswitch "ldap";
system.nssDatabases.passwd = lib.optional cfg.nsswitch "ldap";
system.nssDatabases.shadow = lib.optional cfg.nsswitch "ldap";
users = mkIf cfg.daemon.enable {
groups.nslcd = {
gid = config.ids.gids.nslcd;
};
users.nslcd = {
uid = config.ids.uids.nslcd;
description = "nslcd user.";
group = "nslcd";
};
};
systemd.services = mkMerge [
(mkIf (!cfg.daemon.enable) {
ldap-password = {
wantedBy = [ "sysinit.target" ];
before = [
"sysinit.target"
"shutdown.target"
];
conflicts = [ "shutdown.target" ];
unitConfig.DefaultDependencies = false;
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
script = ''
if test -f "${cfg.bind.passwordFile}" ; then
umask 0077
conf="$(mktemp)"
printf 'bindpw %s\n' "$(cat ${cfg.bind.passwordFile})" |
cat ${ldapConfig.source} - >"$conf"
mv -fT "$conf" /etc/ldap.conf
fi
'';
};
})
(mkIf cfg.daemon.enable {
nslcd = {
wantedBy = [ "multi-user.target" ];
preStart = ''
umask 0077
conf="$(mktemp)"
{
cat ${nslcdConfig}
test -z '${cfg.bind.distinguishedName}' -o ! -f '${cfg.bind.passwordFile}' ||
printf 'bindpw %s\n' "$(cat '${cfg.bind.passwordFile}')"
test -z '${cfg.daemon.rootpwmoddn}' -o ! -f '${cfg.daemon.rootpwmodpwFile}' ||
printf 'rootpwmodpw %s\n' "$(cat '${cfg.daemon.rootpwmodpwFile}')"
} >"$conf"
mv -fT "$conf" /run/nslcd/nslcd.conf
'';
restartTriggers = [
nslcdConfig
cfg.bind.passwordFile
cfg.daemon.rootpwmodpwFile
];
serviceConfig = {
ExecStart = "${nslcdWrapped}/bin/nslcd";
Type = "forking";
Restart = "always";
User = "nslcd";
Group = "nslcd";
RuntimeDirectory = [ "nslcd" ];
PIDFile = "/run/nslcd/nslcd.pid";
AmbientCapabilities = "CAP_SYS_RESOURCE";
};
};
})
];
};
imports = [
(mkRenamedOptionModule
[ "users" "ldap" "bind" "password" ]
[ "users" "ldap" "bind" "passwordFile" ]
)
];
}

View File

@@ -0,0 +1,81 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
last
splitString
mkOption
types
optionals
;
libDir = pkgs.stdenv.hostPlatform.libDir;
ldsoBasename = builtins.unsafeDiscardStringContext (
last (splitString "/" pkgs.stdenv.cc.bintools.dynamicLinker)
);
# Hard-code to avoid creating another instance of nixpkgs. Also avoids eval errors in some cases.
libDir32 = "lib"; # pkgs.pkgsi686Linux.stdenv.hostPlatform.libDir
ldsoBasename32 = "ld-linux.so.2"; # last (splitString "/" pkgs.pkgsi686Linux.stdenv.cc.bintools.dynamicLinker)
in
{
options = {
environment.ldso = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
The executable to link into the normal FHS location of the ELF loader.
'';
};
environment.ldso32 = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
The executable to link into the normal FHS location of the 32-bit ELF loader.
This currently only works on x86_64 architectures.
'';
};
};
config = {
assertions = [
{
assertion = isNull config.environment.ldso32 || pkgs.stdenv.hostPlatform.isx86_64;
message = "Option environment.ldso32 currently only works on x86_64.";
}
];
systemd.tmpfiles.rules =
(
if isNull config.environment.ldso then
[
"r /${libDir}/${ldsoBasename} - - - - -"
]
else
[
"d /${libDir} 0755 root root - -"
"L+ /${libDir}/${ldsoBasename} - - - - ${config.environment.ldso}"
]
)
++ optionals pkgs.stdenv.hostPlatform.isx86_64 (
if isNull config.environment.ldso32 then
[
"r /${libDir32}/${ldsoBasename32} - - - - -"
]
else
[
"d /${libDir32} 0755 root root - -"
"L+ /${libDir32}/${ldsoBasename32} - - - - ${config.environment.ldso32}"
]
);
};
meta.maintainers = with lib.maintainers; [ tejing ];
}

View File

@@ -0,0 +1,102 @@
{
config,
lib,
pkgs,
...
}:
let
tzdir = "${pkgs.tzdata}/share/zoneinfo";
nospace = str: lib.filter (c: c == " ") (lib.stringToCharacters str) == [ ];
timezone = lib.types.nullOr (lib.types.addCheck lib.types.str nospace) // {
description = "null or string without spaces";
};
lcfg = config.location;
in
{
options = {
time = {
timeZone = lib.mkOption {
default = null;
type = timezone;
example = "America/New_York";
description = ''
The time zone used when displaying times and dates. See <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>
for a comprehensive list of possible values for this setting.
If null, the timezone will default to UTC and can be set imperatively
using timedatectl.
'';
};
hardwareClockInLocalTime = lib.mkOption {
default = false;
type = lib.types.bool;
description = "If set, keep the hardware clock in local time instead of UTC.";
};
};
location = {
latitude = lib.mkOption {
type = lib.types.float;
description = ''
Your current latitude, between
`-90.0` and `90.0`. Must be provided
along with longitude.
'';
};
longitude = lib.mkOption {
type = lib.types.float;
description = ''
Your current longitude, between
between `-180.0` and `180.0`. Must be
provided along with latitude.
'';
};
provider = lib.mkOption {
type = lib.types.enum [
"manual"
"geoclue2"
];
default = "manual";
description = ''
The location provider to use for determining your location. If set to
`manual` you must also provide latitude/longitude.
'';
};
};
};
config = {
environment.sessionVariables.TZDIR = "/etc/zoneinfo";
services.geoclue2.enable = lib.mkIf (lcfg.provider == "geoclue2") true;
# This way services are restarted when tzdata changes.
systemd.globalEnvironment.TZDIR = tzdir;
systemd.services.systemd-timedated.environment = lib.optionalAttrs (config.time.timeZone != null) {
NIXOS_STATIC_TIMEZONE = "1";
};
environment.etc = {
zoneinfo.source = tzdir;
}
// lib.optionalAttrs (config.time.timeZone != null) {
localtime.source = "/etc/zoneinfo/${config.time.timeZone}";
localtime.mode = "direct-symlink";
};
};
}

View File

@@ -0,0 +1,141 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.environment.memoryAllocator;
# The set of alternative malloc(3) providers.
providers = {
graphene-hardened = {
libPath = "${pkgs.graphene-hardened-malloc}/lib/libhardened_malloc.so";
description = ''
Hardened memory allocator coming from GrapheneOS project.
The default configuration template has all normal optional security
features enabled and is quite aggressive in terms of sacrificing
performance and memory usage for security.
'';
};
graphene-hardened-light = {
libPath = "${pkgs.graphene-hardened-malloc}/lib/libhardened_malloc-light.so";
description = ''
Hardened memory allocator coming from GrapheneOS project.
The light configuration template disables the slab quarantines,
write after free check, slot randomization and raises the guard
slab interval from 1 to 8 but leaves zero-on-free and slab canaries enabled.
The light configuration has solid performance and memory usage while still
being far more secure than mainstream allocators with much better security
properties.
'';
};
jemalloc = {
libPath = "${pkgs.jemalloc}/lib/libjemalloc.so";
description = ''
A general purpose allocator that emphasizes fragmentation avoidance
and scalable concurrency support.
'';
};
scudo =
let
platformMap = {
aarch64-linux = "aarch64";
x86_64-linux = "x86_64";
};
systemPlatform =
platformMap.${pkgs.stdenv.hostPlatform.system}
or (throw "scudo not supported on ${pkgs.stdenv.hostPlatform.system}");
in
{
libPath = "${pkgs.llvmPackages.compiler-rt}/lib/linux/libclang_rt.scudo_standalone-${systemPlatform}.so";
description = ''
A user-mode allocator based on LLVM Sanitizers CombinedAllocator,
which aims at providing additional mitigations against heap based
vulnerabilities, while maintaining good performance.
'';
};
mimalloc = {
libPath = "${pkgs.mimalloc}/lib/libmimalloc.so";
description = ''
A compact and fast general purpose allocator, which may
optionally be built with mitigations against various heap
vulnerabilities.
'';
};
};
providerConf = providers.${cfg.provider};
# An output that contains only the shared library, to avoid
# needlessly bloating the system closure
mallocLib =
pkgs.runCommand "malloc-provider-${cfg.provider}"
rec {
preferLocalBuild = true;
allowSubstitutes = false;
origLibPath = providerConf.libPath;
libName = baseNameOf origLibPath;
}
''
mkdir -p $out/lib
cp -L $origLibPath $out/lib/$libName
'';
# The full path to the selected provider shlib.
providerLibPath = "${mallocLib}/lib/${mallocLib.libName}";
in
{
meta = {
maintainers = [ lib.maintainers.joachifm ];
};
options = {
environment.memoryAllocator.provider = lib.mkOption {
type = lib.types.enum ([ "libc" ] ++ lib.attrNames providers);
default = "libc";
description = ''
The system-wide memory allocator.
Briefly, the system-wide memory allocator providers are:
- `libc`: the standard allocator provided by libc
${lib.concatStringsSep "\n" (
lib.mapAttrsToList (
name: value: "- `${name}`: ${lib.replaceStrings [ "\n" ] [ " " ] value.description}"
) providers
)}
::: {.warning}
Selecting an alternative allocator (i.e., anything other than
`libc`) may result in instability, data loss,
and/or service failure.
:::
'';
};
};
config = lib.mkIf (cfg.provider != "libc") {
environment.etc."ld-nix.so.preload".text = ''
${providerLibPath}
'';
security.apparmor.includes = {
"abstractions/base" = ''
r /etc/ld-nix.so.preload,
r ${config.environment.etc."ld-nix.so.preload".source},
include "${
pkgs.apparmorRulesFromClosure {
name = "mallocLib";
baseRules = [ "mr $path/lib/**.so*" ];
} [ mallocLib ]
}"
'';
};
};
}

View File

@@ -0,0 +1,504 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.users.mysql;
in
{
meta.maintainers = [ lib.maintainers.netali ];
options = {
users.mysql = {
enable = lib.mkEnableOption "authentication against a MySQL/MariaDB database";
host = lib.mkOption {
type = lib.types.str;
example = "localhost";
description = "The hostname of the MySQL/MariaDB server";
};
database = lib.mkOption {
type = lib.types.str;
example = "auth";
description = "The name of the database containing the users";
};
user = lib.mkOption {
type = lib.types.str;
example = "nss-user";
description = "The username to use when connecting to the database";
};
passwordFile = lib.mkOption {
type = lib.types.path;
example = "/run/secrets/mysql-auth-db-passwd";
description = "The path to the file containing the password for the user";
};
pam = lib.mkOption {
description = "Settings for `pam_mysql`";
type = lib.types.submodule {
options = {
table = lib.mkOption {
type = lib.types.str;
example = "users";
description = "The name of table that maps unique login names to the passwords.";
};
updateTable = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "users_updates";
description = ''
The name of the table used for password alteration. If not defined, the value
of the `table` option will be used instead.
'';
};
userColumn = lib.mkOption {
type = lib.types.str;
example = "username";
description = "The name of the column that contains a unix login name.";
};
passwordColumn = lib.mkOption {
type = lib.types.str;
example = "password";
description = "The name of the column that contains a (encrypted) password string.";
};
statusColumn = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "status";
description = ''
The name of the column or an SQL expression that indicates the status of
the user. The status is expressed by the combination of two bitfields
shown below:
- `bit 0 (0x01)`:
if flagged, `pam_mysql` deems the account to be expired and
returns `PAM_ACCT_EXPIRED`. That is, the account is supposed
to no longer be available. Note this doesn't mean that `pam_mysql`
rejects further authentication operations.
- `bit 1 (0x02)`:
if flagged, `pam_mysql` deems the authentication token
(password) to be expired and returns `PAM_NEW_AUTHTOK_REQD`.
This ends up requiring that the user enter a new password.
'';
};
passwordCrypt = lib.mkOption {
example = "2";
type = lib.types.enum [
"0"
"plain"
"1"
"Y"
"2"
"mysql"
"3"
"md5"
"4"
"sha1"
"5"
"drupal7"
"6"
"joomla15"
"7"
"ssha"
"8"
"sha512"
"9"
"sha256"
];
description = ''
The method to encrypt the user's password:
- `0` (or `"plain"`):
No encryption. Passwords are stored in plaintext. HIGHLY DISCOURAGED.
- `1` (or `"Y"`):
Use {manpage}`crypt(3)` function.
- `2` (or `"mysql"`):
Use the MySQL PASSWORD() function. It is possible that the encryption function used
by `pam_mysql` is different from that of the MySQL server, as
`pam_mysql` uses the function defined in MySQL's C-client API
instead of using PASSWORD() SQL function in the query.
- `3` (or `"md5"`):
Use plain hex MD5.
- `4` (or `"sha1"`):
Use plain hex SHA1.
- `5` (or `"drupal7"`):
Use Drupal7 salted passwords.
- `6` (or `"joomla15"`):
Use Joomla15 salted passwords.
- `7` (or `"ssha"`):
Use ssha hashed passwords.
- `8` (or `"sha512"`):
Use sha512 hashed passwords.
- `9` (or `"sha256"`):
Use sha256 hashed passwords.
'';
};
cryptDefault = lib.mkOption {
type = lib.types.nullOr (
lib.types.enum [
"md5"
"sha256"
"sha512"
"blowfish"
]
);
default = null;
example = "blowfish";
description = "The default encryption method to use for `passwordCrypt = 1`.";
};
where = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "host.name='web' AND user.active=1";
description = "Additional criteria for the query.";
};
verbose = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
If enabled, produces logs with detailed messages that describes what
`pam_mysql` is doing. May be useful for debugging.
'';
};
disconnectEveryOperation = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
By default, `pam_mysql` keeps the connection to the MySQL
database until the session is closed. If this option is set to true it
disconnects every time the PAM operation has finished. This option may
be useful in case the session lasts quite long.
'';
};
logging = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enables logging of authentication attempts in the MySQL database.";
};
table = lib.mkOption {
type = lib.types.str;
example = "logs";
description = "The name of the table to which logs are written.";
};
msgColumn = lib.mkOption {
type = lib.types.str;
example = "msg";
description = ''
The name of the column in the log table to which the description
of the performed operation is stored.
'';
};
userColumn = lib.mkOption {
type = lib.types.str;
example = "user";
description = ''
The name of the column in the log table to which the name of the
user being authenticated is stored.
'';
};
pidColumn = lib.mkOption {
type = lib.types.str;
example = "pid";
description = ''
The name of the column in the log table to which the pid of the
process utilising the `pam_mysql` authentication
service is stored.
'';
};
hostColumn = lib.mkOption {
type = lib.types.str;
example = "host";
description = ''
The name of the column in the log table to which the name of the user
being authenticated is stored.
'';
};
rHostColumn = lib.mkOption {
type = lib.types.str;
example = "rhost";
description = ''
The name of the column in the log table to which the name of the remote
host that initiates the session is stored. The value is supposed to be
set by the PAM-aware application with `pam_set_item(PAM_RHOST)`.
'';
};
timeColumn = lib.mkOption {
type = lib.types.str;
example = "timestamp";
description = ''
The name of the column in the log table to which the timestamp of the
log entry is stored.
'';
};
};
};
};
};
nss = lib.mkOption {
description = ''
Settings for `libnss-mysql`.
All examples are from the [minimal example](https://github.com/saknopper/libnss-mysql/tree/master/sample/minimal)
of `libnss-mysql`, but they are modified with NixOS paths for bash.
'';
type = lib.types.submodule {
options = {
getpwnam = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = lib.literalExpression ''
SELECT username,'x',uid,'5000','MySQL User', CONCAT('/home/',username),'/run/sw/current-system/bin/bash' \
FROM users \
WHERE username='%1$s' \
LIMIT 1
'';
description = ''
SQL query for the [getpwnam](https://man7.org/linux/man-pages/man3/getpwnam.3.html)
syscall.
'';
};
getpwuid = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = lib.literalExpression ''
SELECT username,'x',uid,'5000','MySQL User', CONCAT('/home/',username),'/run/sw/current-system/bin/bash' \
FROM users \
WHERE uid='%1$u' \
LIMIT 1
'';
description = ''
SQL query for the [getpwuid](https://man7.org/linux/man-pages/man3/getpwuid.3.html)
syscall.
'';
};
getspnam = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = lib.literalExpression ''
SELECT username,password,'1','0','99999','0','0','-1','0' \
FROM users \
WHERE username='%1$s' \
LIMIT 1
'';
description = ''
SQL query for the [getspnam](https://man7.org/linux/man-pages/man3/getspnam.3.html)
syscall.
'';
};
getpwent = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = lib.literalExpression ''
SELECT username,'x',uid,'5000','MySQL User', CONCAT('/home/',username),'/run/sw/current-system/bin/bash' FROM users
'';
description = ''
SQL query for the [getpwent](https://man7.org/linux/man-pages/man3/getpwent.3.html)
syscall.
'';
};
getspent = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = lib.literalExpression ''
SELECT username,password,'1','0','99999','0','0','-1','0' FROM users
'';
description = ''
SQL query for the [getspent](https://man7.org/linux/man-pages/man3/getspent.3.html)
syscall.
'';
};
getgrnam = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = lib.literalExpression ''
SELECT name,password,gid FROM groups WHERE name='%1$s' LIMIT 1
'';
description = ''
SQL query for the [getgrnam](https://man7.org/linux/man-pages/man3/getgrnam.3.html)
syscall.
'';
};
getgrgid = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = lib.literalExpression ''
SELECT name,password,gid FROM groups WHERE gid='%1$u' LIMIT 1
'';
description = ''
SQL query for the [getgrgid](https://man7.org/linux/man-pages/man3/getgrgid.3.html)
syscall.
'';
};
getgrent = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = lib.literalExpression ''
SELECT name,password,gid FROM groups
'';
description = ''
SQL query for the [getgrent](https://man7.org/linux/man-pages/man3/getgrent.3.html)
syscall.
'';
};
memsbygid = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = lib.literalExpression ''
SELECT username FROM grouplist WHERE gid='%1$u'
'';
description = ''
SQL query for the [memsbygid](https://man7.org/linux/man-pages/man3/memsbygid.3.html)
syscall.
'';
};
gidsbymem = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = lib.literalExpression ''
SELECT gid FROM grouplist WHERE username='%1$s'
'';
description = ''
SQL query for the [gidsbymem](https://man7.org/linux/man-pages/man3/gidsbymem.3.html)
syscall.
'';
};
};
};
};
};
};
config = lib.mkIf cfg.enable {
system.nssModules = [ pkgs.libnss-mysql ];
system.nssDatabases.shadow = [ "mysql" ];
system.nssDatabases.group = [ "mysql" ];
system.nssDatabases.passwd = [ "mysql" ];
environment.etc."security/pam_mysql.conf" = {
user = "root";
group = "root";
mode = "0600";
# password will be added from password file in systemd oneshot
text = ''
users.host=${cfg.host}
users.db_user=${cfg.user}
users.database=${cfg.database}
users.table=${cfg.pam.table}
users.user_column=${cfg.pam.userColumn}
users.password_column=${cfg.pam.passwordColumn}
users.password_crypt=${cfg.pam.passwordCrypt}
users.disconnect_every_operation=${if cfg.pam.disconnectEveryOperation then "1" else "0"}
verbose=${if cfg.pam.verbose then "1" else "0"}
''
+ lib.optionalString (cfg.pam.cryptDefault != null) ''
users.use_${cfg.pam.cryptDefault}=1
''
+ lib.optionalString (cfg.pam.where != null) ''
users.where_clause=${cfg.pam.where}
''
+ lib.optionalString (cfg.pam.statusColumn != null) ''
users.status_column=${cfg.pam.statusColumn}
''
+ lib.optionalString (cfg.pam.updateTable != null) ''
users.update_table=${cfg.pam.updateTable}
''
+ lib.optionalString cfg.pam.logging.enable ''
log.enabled=true
log.table=${cfg.pam.logging.table}
log.message_column=${cfg.pam.logging.msgColumn}
log.pid_column=${cfg.pam.logging.pidColumn}
log.user_column=${cfg.pam.logging.userColumn}
log.host_column=${cfg.pam.logging.hostColumn}
log.rhost_column=${cfg.pam.logging.rHostColumn}
log.time_column=${cfg.pam.logging.timeColumn}
'';
};
environment.etc."libnss-mysql.cfg" = {
mode = "0600";
user = config.services.nscd.user;
group = config.services.nscd.group;
text =
lib.optionalString (cfg.nss.getpwnam != null) ''
getpwnam ${cfg.nss.getpwnam}
''
+ lib.optionalString (cfg.nss.getpwuid != null) ''
getpwuid ${cfg.nss.getpwuid}
''
+ lib.optionalString (cfg.nss.getspnam != null) ''
getspnam ${cfg.nss.getspnam}
''
+ lib.optionalString (cfg.nss.getpwent != null) ''
getpwent ${cfg.nss.getpwent}
''
+ lib.optionalString (cfg.nss.getspent != null) ''
getspent ${cfg.nss.getspent}
''
+ lib.optionalString (cfg.nss.getgrnam != null) ''
getgrnam ${cfg.nss.getgrnam}
''
+ lib.optionalString (cfg.nss.getgrgid != null) ''
getgrgid ${cfg.nss.getgrgid}
''
+ lib.optionalString (cfg.nss.getgrent != null) ''
getgrent ${cfg.nss.getgrent}
''
+ lib.optionalString (cfg.nss.memsbygid != null) ''
memsbygid ${cfg.nss.memsbygid}
''
+ lib.optionalString (cfg.nss.gidsbymem != null) ''
gidsbymem ${cfg.nss.gidsbymem}
''
+ ''
host ${cfg.host}
database ${cfg.database}
'';
};
environment.etc."libnss-mysql-root.cfg" = {
mode = "0600";
user = config.services.nscd.user;
group = config.services.nscd.group;
# password will be added from password file in systemd oneshot
text = ''
username ${cfg.user}
'';
};
systemd.services.mysql-auth-pw-init = {
description = "Adds the mysql password to the mysql auth config files";
before = [ "nscd.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
User = "root";
Group = "root";
};
restartTriggers = [
config.environment.etc."security/pam_mysql.conf".source
config.environment.etc."libnss-mysql.cfg".source
config.environment.etc."libnss-mysql-root.cfg".source
];
script = ''
if [[ -r ${cfg.passwordFile} ]]; then
umask 0077
conf_nss="$(mktemp)"
cp /etc/libnss-mysql-root.cfg $conf_nss
printf 'password %s\n' "$(cat ${cfg.passwordFile})" >> $conf_nss
mv -fT "$conf_nss" /etc/libnss-mysql-root.cfg
chown ${config.services.nscd.user}:${config.services.nscd.group} /etc/libnss-mysql-root.cfg
conf_pam="$(mktemp)"
cp /etc/security/pam_mysql.conf $conf_pam
printf 'users.db_passwd=%s\n' "$(cat ${cfg.passwordFile})" >> $conf_pam
mv -fT "$conf_pam" /etc/security/pam_mysql.conf
fi
'';
};
};
}

View File

@@ -0,0 +1,262 @@
# /etc files related to networking, such as /etc/services.
{
config,
lib,
options,
pkgs,
...
}:
let
cfg = config.networking;
opt = options.networking;
localhostMultiple = lib.any (lib.elem "localhost") (
lib.attrValues (
removeAttrs cfg.hosts [
"127.0.0.1"
"::1"
]
)
);
in
{
imports = [
(lib.mkRemovedOptionModule [ "networking" "hostConf" ] "Use environment.etc.\"host.conf\" instead.")
];
options = {
networking.hosts = lib.mkOption {
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
example = lib.literalExpression ''
{
"127.0.0.1" = [ "foo.bar.baz" ];
"192.168.0.2" = [ "fileserver.local" "nameserver.local" ];
};
'';
description = ''
Locally defined maps of hostnames to IP addresses.
'';
};
networking.hostFiles = lib.mkOption {
type = lib.types.listOf lib.types.path;
defaultText = lib.literalMD "Hosts from {option}`networking.hosts` and {option}`networking.extraHosts`";
example = lib.literalExpression ''[ "''${pkgs.my-blocklist-package}/share/my-blocklist/hosts" ]'';
description = ''
Files that should be concatenated together to form {file}`/etc/hosts`.
'';
};
networking.extraHosts = lib.mkOption {
type = lib.types.lines;
default = "";
example = "192.168.0.1 lanlocalhost";
description = ''
Additional verbatim entries to be appended to {file}`/etc/hosts`.
For adding hosts from derivation results, use {option}`networking.hostFiles` instead.
'';
};
networking.timeServers = lib.mkOption {
default = [
"0.nixos.pool.ntp.org"
"1.nixos.pool.ntp.org"
"2.nixos.pool.ntp.org"
"3.nixos.pool.ntp.org"
];
type = lib.types.listOf lib.types.str;
description = ''
The set of NTP servers from which to synchronise.
'';
};
networking.proxy = {
default = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
This option specifies the default value for httpProxy, httpsProxy, ftpProxy and rsyncProxy.
'';
example = "http://127.0.0.1:3128";
};
httpProxy = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = cfg.proxy.default;
defaultText = lib.literalExpression "config.${opt.proxy.default}";
description = ''
This option specifies the http_proxy environment variable.
'';
example = "http://127.0.0.1:3128";
};
httpsProxy = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = cfg.proxy.default;
defaultText = lib.literalExpression "config.${opt.proxy.default}";
description = ''
This option specifies the https_proxy environment variable.
'';
example = "http://127.0.0.1:3128";
};
ftpProxy = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = cfg.proxy.default;
defaultText = lib.literalExpression "config.${opt.proxy.default}";
description = ''
This option specifies the ftp_proxy environment variable.
'';
example = "http://127.0.0.1:3128";
};
rsyncProxy = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = cfg.proxy.default;
defaultText = lib.literalExpression "config.${opt.proxy.default}";
description = ''
This option specifies the rsync_proxy environment variable.
'';
example = "http://127.0.0.1:3128";
};
allProxy = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = cfg.proxy.default;
defaultText = lib.literalExpression "config.${opt.proxy.default}";
description = ''
This option specifies the all_proxy environment variable.
'';
example = "http://127.0.0.1:3128";
};
noProxy = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
This option specifies the no_proxy environment variable.
If a default proxy is used and noProxy is null,
then noProxy will be set to 127.0.0.1,localhost.
'';
example = "127.0.0.1,localhost,.localdomain";
};
envVars = lib.mkOption {
type = lib.types.attrs;
internal = true;
default = { };
description = ''
Environment variables used for the network proxy.
'';
};
};
};
config = {
assertions = [
{
assertion = !localhostMultiple;
message = ''
`networking.hosts` maps "localhost" to something other than "127.0.0.1"
or "::1". This will break some applications. Please use
`networking.extraHosts` if you really want to add such a mapping.
'';
}
];
# These entries are required for "hostname -f" and to resolve both the
# hostname and FQDN correctly:
networking.hosts =
let
hostnames = # Note: The FQDN (canonical hostname) has to come first:
lib.optional (cfg.hostName != "" && cfg.domain != null) "${cfg.hostName}.${cfg.domain}"
++ lib.optional (cfg.hostName != "") cfg.hostName; # Then the hostname (without the domain)
in
{
"127.0.0.2" = hostnames;
};
networking.hostFiles =
let
# Note: localhostHosts has to appear first in /etc/hosts so that 127.0.0.1
# resolves back to "localhost" (as some applications assume) instead of
# the FQDN! By default "networking.hosts" also contains entries for the
# FQDN so that e.g. "hostname -f" works correctly.
localhostHosts = pkgs.writeText "localhost-hosts" ''
127.0.0.1 localhost
${lib.optionalString cfg.enableIPv6 "::1 localhost"}
'';
stringHosts =
let
oneToString = set: ip: ip + " " + lib.concatStringsSep " " set.${ip} + "\n";
allToString = set: lib.concatMapStrings (oneToString set) (lib.attrNames set);
in
pkgs.writeText "string-hosts" (allToString (lib.filterAttrs (_: v: v != [ ]) cfg.hosts));
extraHosts = pkgs.writeText "extra-hosts" cfg.extraHosts;
in
lib.mkBefore [
localhostHosts
stringHosts
extraHosts
];
environment.etc = {
# /etc/services: TCP/UDP port assignments.
services.source = pkgs.iana-etc + "/etc/services";
# /etc/protocols: IP protocol numbers.
protocols.source = pkgs.iana-etc + "/etc/protocols";
# /etc/hosts: Hostname-to-IP mappings.
hosts.source = pkgs.concatText "hosts" cfg.hostFiles;
# /etc/netgroup: Network-wide groups.
netgroup.text = lib.mkDefault "";
# /etc/host.conf: resolver configuration file
"host.conf".text = ''
multi on
'';
}
// lib.optionalAttrs (pkgs.stdenv.hostPlatform.libc == "glibc") {
# /etc/rpc: RPC program numbers.
rpc.source = pkgs.stdenv.cc.libc.out + "/etc/rpc";
};
networking.proxy.envVars =
lib.optionalAttrs (cfg.proxy.default != null) {
# other options already fallback to proxy.default
no_proxy = "127.0.0.1,localhost";
}
// lib.optionalAttrs (cfg.proxy.httpProxy != null) {
http_proxy = cfg.proxy.httpProxy;
}
// lib.optionalAttrs (cfg.proxy.httpsProxy != null) {
https_proxy = cfg.proxy.httpsProxy;
}
// lib.optionalAttrs (cfg.proxy.rsyncProxy != null) {
rsync_proxy = cfg.proxy.rsyncProxy;
}
// lib.optionalAttrs (cfg.proxy.ftpProxy != null) {
ftp_proxy = cfg.proxy.ftpProxy;
}
// lib.optionalAttrs (cfg.proxy.allProxy != null) {
all_proxy = cfg.proxy.allProxy;
}
// lib.optionalAttrs (cfg.proxy.noProxy != null) {
no_proxy = cfg.proxy.noProxy;
};
# Install the proxy environment variables
environment.sessionVariables = cfg.proxy.envVars;
};
}

View File

@@ -0,0 +1,106 @@
/*
Manages the things that are needed for a traditional nix-channel based
configuration to work.
See also
- ./nix.nix
- ./nix-flakes.nix
*/
{ config, lib, ... }:
let
inherit (lib)
mkDefault
mkIf
mkOption
stringAfter
types
;
cfg = config.nix;
in
{
options = {
nix = {
channel = {
enable = mkOption {
description = ''
Whether the `nix-channel` command and state files are made available on the machine.
The following files are initialized when enabled:
- `/nix/var/nix/profiles/per-user/root/channels`
- `/root/.nix-channels`
- `$HOME/.nix-defexpr/channels` (on login)
Disabling this option will not remove the state files from the system.
'';
type = types.bool;
default = true;
};
};
nixPath = mkOption {
type = types.listOf types.str;
default =
if cfg.channel.enable then
[
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
"nixos-config=/etc/nixos/configuration.nix"
"/nix/var/nix/profiles/per-user/root/channels"
]
else
[ ];
defaultText = ''
if nix.channel.enable
then [
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
"nixos-config=/etc/nixos/configuration.nix"
"/nix/var/nix/profiles/per-user/root/channels"
]
else [];
'';
description = ''
The default Nix expression search path, used by the Nix
evaluator to look up paths enclosed in angle brackets
(e.g. `<nixpkgs>`).
'';
};
};
system = {
defaultChannel = mkOption {
internal = true;
type = types.str;
default = "https://nixos.org/channels/nixos-unstable";
description = "Default NixOS channel to which the root user is subscribed.";
};
};
};
config = mkIf cfg.enable {
environment.extraInit = mkIf cfg.channel.enable ''
if [ -e "$HOME/.nix-defexpr/channels" ]; then
export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
fi
'';
environment.extraSetup = mkIf (!cfg.channel.enable) ''
rm --force $out/bin/nix-channel
'';
# NIX_PATH has a non-empty default according to Nix docs, so we don't unset
# it when empty.
environment.sessionVariables = {
NIX_PATH = cfg.nixPath;
};
systemd.tmpfiles.rules = lib.mkIf cfg.channel.enable [
''f /root/.nix-channels - - - - ${config.system.defaultChannel} nixos\n''
];
system.activationScripts.no-nix-channel = mkIf (!cfg.channel.enable) (
stringAfter [ "etc" "users" ] (builtins.readFile ./nix-channel/activation-check.sh)
);
};
}

View File

@@ -0,0 +1,21 @@
# shellcheck shell=bash
explainChannelWarning=0
if [[ -e "/root/.nix-defexpr/channels" ]]; then
warn '/root/.nix-defexpr/channels exists, but channels have been disabled.'
explainChannelWarning=1
fi
if [[ -e "/nix/var/nix/profiles/per-user/root/channels" ]]; then
warn "/nix/var/nix/profiles/per-user/root/channels exists, but channels have been disabled."
explainChannelWarning=1
fi
while IFS=: read -r _ _ _ _ _ home _ ; do
if [[ -n "$home" && -e "$home/.nix-defexpr/channels" ]]; then
warn "$home/.nix-defexpr/channels exists, but channels have been disabled." 1>&2
explainChannelWarning=1
fi
done < <(getent passwd)
if [[ $explainChannelWarning -eq 1 ]]; then
echo "Due to https://github.com/NixOS/nix/issues/9574, Nix may still use these channels when NIX_PATH is unset." 1>&2
echo "Delete the above directory or directories to prevent this." 1>&2
fi

View File

@@ -0,0 +1,20 @@
# Run:
# nix-build -A nixosTests.nix-channel
{ lib, testers }:
let
inherit (lib) fileset;
runShellcheck = testers.shellcheck {
name = "activation-check";
src = fileset.toSource {
root = ./.;
fileset = fileset.unions [
./activation-check.sh
];
};
};
in
lib.recurseIntoAttrs {
inherit runShellcheck;
}

View File

@@ -0,0 +1,124 @@
/*
Manages the flake registry.
See also
- ./nix.nix
- ./nix-channel.nix
*/
{ config, lib, ... }:
let
inherit (lib)
filterAttrs
literalExpression
mapAttrsToList
mkDefault
mkIf
mkOption
types
;
cfg = config.nix;
flakeRefFormat = ''
The format of flake references is described in {manpage}`nix3-flake(1)`.
'';
in
{
options = {
nix = {
registry = mkOption {
type = types.attrsOf (
types.submodule (
let
referenceAttrs =
with types;
attrsOf (oneOf [
str
int
bool
path
package
]);
in
{ config, name, ... }:
{
options = {
from = mkOption {
type = referenceAttrs;
example = {
type = "indirect";
id = "nixpkgs";
};
description = ''
The flake reference to be rewritten.
${flakeRefFormat}
'';
};
to = mkOption {
type = referenceAttrs;
example = {
type = "github";
owner = "my-org";
repo = "my-nixpkgs";
};
description = ''
The flake reference {option}`from` is rewritten to.
${flakeRefFormat}
'';
};
flake = mkOption {
type = types.nullOr types.attrs;
default = null;
example = literalExpression "nixpkgs";
description = ''
The flake input {option}`from` is rewritten to.
'';
};
exact = mkOption {
type = types.bool;
default = true;
description = ''
Whether the {option}`from` reference needs to match exactly. If set,
a {option}`from` reference like `nixpkgs` does not
match with a reference like `nixpkgs/nixos-20.03`.
'';
};
};
config = {
from = mkDefault {
type = "indirect";
id = name;
};
to = mkIf (config.flake != null) (
mkDefault (
{
type = "path";
path = config.flake.outPath;
}
// filterAttrs (n: _: n == "lastModified" || n == "rev" || n == "narHash") config.flake
)
);
};
}
)
);
default = { };
description = ''
A system-wide flake registry.
See {manpage}`nix3-registry(1)` for more information.
'';
};
};
};
config = mkIf cfg.enable {
environment.etc."nix/registry.json".text = builtins.toJSON {
version = 2;
flakes = mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry;
};
};
}

View File

@@ -0,0 +1,256 @@
/*
Manages the remote build configuration, /etc/nix/machines
See also
- ./nix.nix
- nixos/modules/services/system/nix-daemon.nix
*/
{ config, lib, ... }:
let
inherit (lib)
any
concatMapStrings
concatStringsSep
filter
getVersion
mkIf
mkMerge
mkOption
optional
optionalString
types
versionAtLeast
;
cfg = config.nix;
nixPackage = cfg.package.out;
isNixAtLeast = versionAtLeast (getVersion nixPackage);
buildMachinesText = concatMapStrings (
machine:
(concatStringsSep " " (
[
"${optionalString (machine.protocol != null) "${machine.protocol}://"}${
optionalString (machine.sshUser != null) "${machine.sshUser}@"
}${machine.hostName}"
(
if machine.system != null then
machine.system
else if machine.systems != [ ] then
concatStringsSep "," machine.systems
else
"-"
)
(if machine.sshKey != null then machine.sshKey else "-")
(toString machine.maxJobs)
(toString machine.speedFactor)
(
let
res = (machine.supportedFeatures ++ machine.mandatoryFeatures);
in
if (res == [ ]) then "-" else (concatStringsSep "," res)
)
(
let
res = machine.mandatoryFeatures;
in
if (res == [ ]) then "-" else (concatStringsSep "," machine.mandatoryFeatures)
)
]
++ optional (isNixAtLeast "2.4pre") (
if machine.publicHostKey != null then machine.publicHostKey else "-"
)
))
+ "\n"
) cfg.buildMachines;
in
{
options = {
nix = {
buildMachines = mkOption {
type = types.listOf (
types.submodule {
options = {
hostName = mkOption {
type = types.str;
example = "nixbuilder.example.org";
description = ''
The hostname of the build machine.
'';
};
protocol = mkOption {
type = types.enum [
null
"ssh"
"ssh-ng"
];
default = "ssh";
example = "ssh-ng";
description = ''
The protocol used for communicating with the build machine.
Use `ssh-ng` if your remote builder and your
local Nix version support that improved protocol.
Use `null` when trying to change the special localhost builder
without a protocol which is for example used by hydra.
'';
};
system = mkOption {
type = types.nullOr types.str;
default = null;
example = "x86_64-linux";
description = ''
The system type the build machine can execute derivations on.
Either this attribute or {var}`systems` must be
present, where {var}`system` takes precedence if
both are set.
'';
};
systems = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"x86_64-linux"
"aarch64-linux"
];
description = ''
The system types the build machine can execute derivations on.
Either this attribute or {var}`system` must be
present, where {var}`system` takes precedence if
both are set.
'';
};
sshUser = mkOption {
type = types.nullOr types.str;
default = null;
example = "builder";
description = ''
The username to log in as on the remote host. This user must be
able to log in and run nix commands non-interactively. It must
also be privileged to build derivations, so must be included in
{option}`nix.settings.trusted-users`.
'';
};
sshKey = mkOption {
type = types.nullOr types.str;
default = null;
example = "/root/.ssh/id_buildhost_builduser";
description = ''
The path to the SSH private key with which to authenticate on
the build machine. The private key must not have a passphrase.
If null, the building user (root on NixOS machines) must have an
appropriate ssh configuration to log in non-interactively.
Note that for security reasons, this path must point to a file
in the local filesystem, *not* to the nix store.
'';
};
maxJobs = mkOption {
type = types.int;
default = 1;
description = ''
The number of concurrent jobs the build machine supports. The
build machine will enforce its own limits, but this allows hydra
to schedule better since there is no work-stealing between build
machines.
'';
};
speedFactor = mkOption {
type = types.int;
default = 1;
description = ''
The relative speed of this builder. This is an arbitrary integer
that indicates the speed of this builder, relative to other
builders. Higher is faster.
'';
};
mandatoryFeatures = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "big-parallel" ];
description = ''
A list of features mandatory for this builder. The builder will
be ignored for derivations that don't require all features in
this list. All mandatory features are automatically included in
{var}`supportedFeatures`.
'';
};
supportedFeatures = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"kvm"
"big-parallel"
];
description = ''
A list of features supported by this builder. The builder will
be ignored for derivations that require features not in this
list.
'';
};
publicHostKey = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The (base64-encoded) public host key of this builder. The field
is calculated via {command}`base64 -w0 /etc/ssh/ssh_host_type_key.pub`.
If null, SSH will use its regular known-hosts file when connecting.
'';
};
};
}
);
default = [ ];
description = ''
This option lists the machines to be used if distributed builds are
enabled (see {option}`nix.distributedBuilds`).
Nix will perform derivations on those machines via SSH by copying the
inputs to the Nix store on the remote machine, starting the build,
then copying the output back to the local Nix store.
'';
};
distributedBuilds = mkOption {
type = types.bool;
default = false;
description = ''
Whether to distribute builds to the machines listed in
{option}`nix.buildMachines`.
'';
};
};
};
# distributedBuilds does *not* inhibit /etc/nix/machines generation; caller may
# override that nix option.
config = mkIf cfg.enable {
assertions =
let
badMachine = m: m.system == null && m.systems == [ ];
in
[
{
assertion = !(any badMachine cfg.buildMachines);
message = ''
At least one system type (via <varname>system</varname> or
<varname>systems</varname>) must be set for every build machine.
Invalid machine specifications:
''
+ " "
+ (concatStringsSep "\n " (map (m: m.hostName) (filter badMachine cfg.buildMachines)));
}
];
# List of machines for distributed Nix builds
environment.etc."nix/machines" = mkIf (cfg.buildMachines != [ ]) {
text = buildMachinesText;
};
# Legacy configuration conversion.
nix.settings = mkIf (!cfg.distributedBuilds) { builders = null; };
};
}

View File

@@ -0,0 +1,377 @@
/*
Manages /etc/nix/nix.conf.
See also
- ./nix-channel.nix
- ./nix-flakes.nix
- ./nix-remote-build.nix
- nixos/modules/services/system/nix-daemon.nix
*/
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
literalExpression
mapAttrsToList
mkAfter
mkIf
mkOption
mkRenamedOptionModuleWith
optionals
systems
types
;
cfg = config.nix;
nixPackage = cfg.package.out;
defaultSystemFeatures = [
"nixos-test"
"benchmark"
"big-parallel"
"kvm"
]
++ optionals (pkgs.stdenv.hostPlatform ? gcc.arch) (
# a builder can run code for `gcc.arch` and inferior architectures
[ "gccarch-${pkgs.stdenv.hostPlatform.gcc.arch}" ]
++ map (x: "gccarch-${x}") (
systems.architectures.inferiors.${pkgs.stdenv.hostPlatform.gcc.arch} or [ ]
)
);
legacyConfMappings = {
useSandbox = "sandbox";
buildCores = "cores";
maxJobs = "max-jobs";
sandboxPaths = "extra-sandbox-paths";
binaryCaches = "substituters";
trustedBinaryCaches = "trusted-substituters";
binaryCachePublicKeys = "trusted-public-keys";
autoOptimiseStore = "auto-optimise-store";
requireSignedBinaryCaches = "require-sigs";
trustedUsers = "trusted-users";
allowedUsers = "allowed-users";
systemFeatures = "system-features";
};
semanticConfType =
with types;
let
confAtom =
nullOr (oneOf [
bool
int
float
str
path
package
])
// {
description = "Nix config atom (null, bool, int, float, str, path or package)";
};
in
attrsOf (either confAtom (listOf confAtom));
nixConf =
(pkgs.formats.nixConf {
inherit (cfg)
package
checkAllErrors
checkConfig
extraOptions
;
inherit (nixPackage) version;
}).generate
"nix.conf"
cfg.settings;
in
{
imports = [
(mkRenamedOptionModuleWith {
sinceRelease = 2003;
from = [
"nix"
"useChroot"
];
to = [
"nix"
"useSandbox"
];
})
(mkRenamedOptionModuleWith {
sinceRelease = 2003;
from = [
"nix"
"chrootDirs"
];
to = [
"nix"
"sandboxPaths"
];
})
]
++ mapAttrsToList (
oldConf: newConf:
mkRenamedOptionModuleWith {
sinceRelease = 2205;
from = [
"nix"
oldConf
];
to = [
"nix"
"settings"
newConf
];
}
) legacyConfMappings;
options = {
nix = {
checkConfig = mkOption {
type = types.bool;
default = true;
description = ''
If enabled, checks that Nix can parse the generated nix.conf.
'';
};
checkAllErrors = mkOption {
type = types.bool;
default = true;
description = ''
If enabled, checks the nix.conf parsing for any kind of error. When disabled, checks only for unknown settings.
'';
};
extraOptions = mkOption {
type = types.lines;
default = "";
example = ''
keep-outputs = true
keep-derivations = true
'';
description = "Additional text appended to {file}`nix.conf`.";
};
settings = mkOption {
type = types.submodule {
freeformType = semanticConfType;
options = {
max-jobs = mkOption {
type = types.either types.int (types.enum [ "auto" ]);
default = "auto";
example = 64;
description = ''
This option defines the maximum number of jobs that Nix will try to
build in parallel. The default is auto, which means it will use all
available logical cores. It is recommend to set it to the total
number of logical cores in your system (e.g., 16 for two CPUs with 4
cores each and hyper-threading).
'';
};
auto-optimise-store = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
If set to true, Nix automatically detects files in the store that have
identical contents, and replaces them with hard links to a single copy.
This saves disk space. If set to false (the default), you can still run
nix-store --optimise to get rid of duplicate files.
'';
};
cores = mkOption {
type = types.int;
default = 0;
example = 64;
description = ''
This option defines the maximum number of concurrent tasks during
one build. It affects, e.g., -j option for make.
The special value 0 means that the builder should use all
available CPU cores in the system. Some builds may become
non-deterministic with this option; use with care! Packages will
only be affected if enableParallelBuilding is set for them.
'';
};
sandbox = mkOption {
type = types.either types.bool (types.enum [ "relaxed" ]);
default = true;
description = ''
If set, Nix will perform builds in a sandboxed environment that it
will set up automatically for each build. This prevents impurities
in builds by disallowing access to dependencies outside of the Nix
store by using network and mount namespaces in a chroot environment.
This is enabled by default even though it has a possible performance
impact due to the initial setup time of a sandbox for each build. It
doesn't affect derivation hashes, so changing this option will not
trigger a rebuild of packages.
When set to "relaxed", this option permits derivations that set
`__noChroot = true;` to run outside of the sandboxed environment.
Exercise caution when using this mode of operation! It is intended to
be a quick hack when building with packages that are not easily setup
to be built reproducibly.
'';
};
extra-sandbox-paths = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"/dev"
"/proc"
];
description = ''
Directories from the host filesystem to be included
in the sandbox.
'';
};
substituters = mkOption {
type = types.listOf types.str;
description = ''
List of binary cache URLs used to obtain pre-built binaries
of Nix packages.
By default https://cache.nixos.org/ is added.
'';
};
trusted-substituters = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "https://hydra.nixos.org/" ];
description = ''
List of binary cache URLs that non-root users can use (in
addition to those specified using
{option}`nix.settings.substituters`) by passing
`--option binary-caches` to Nix commands.
'';
};
require-sigs = mkOption {
type = types.bool;
default = true;
description = ''
If enabled (the default), Nix will only download binaries from binary caches if
they are cryptographically signed with any of the keys listed in
{option}`nix.settings.trusted-public-keys`. If disabled, signatures are neither
required nor checked, so it's strongly recommended that you use only
trustworthy caches and https to prevent man-in-the-middle attacks.
'';
};
trusted-public-keys = mkOption {
type = types.listOf types.str;
example = [ "hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=" ];
description = ''
List of public keys used to sign binary caches. If
{option}`nix.settings.trusted-public-keys` is enabled,
then Nix will use a binary from a binary cache if and only
if it is signed by *any* of the keys
listed here. By default, only the key for
`cache.nixos.org` is included.
'';
};
trusted-users = mkOption {
type = types.listOf types.str;
example = [
"root"
"alice"
"@wheel"
];
description = ''
A list of names of users that have additional rights when
connecting to the Nix daemon, such as the ability to specify
additional binary caches, or to import unsigned NARs. You
can also specify groups by prefixing them with
`@`; for instance,
`@wheel` means all users in the wheel
group.
'';
};
system-features = mkOption {
type = types.listOf types.str;
# We expose system-featuers here and in config below.
# This allows users to access the default value via `options.nix.settings.system-features`
default = defaultSystemFeatures;
defaultText = literalExpression ''[ "nixos-test" "benchmark" "big-parallel" "kvm" "gccarch-<arch>" ]'';
description = ''
The set of features supported by the machine. Derivations
can express dependencies on system features through the
`requiredSystemFeatures` attribute.
'';
};
allowed-users = mkOption {
type = types.listOf types.str;
default = [ "*" ];
example = [
"@wheel"
"@builders"
"alice"
"bob"
];
description = ''
A list of names of users (separated by whitespace) that are
allowed to connect to the Nix daemon. As with
{option}`nix.settings.trusted-users`, you can specify groups by
prefixing them with `@`. Also, you can
allow all users by specifying `*`. The
default is `*`. Note that trusted users are
always allowed to connect.
'';
};
};
};
default = { };
example = literalExpression ''
{
use-sandbox = true;
show-trace = true;
sandbox-paths = [ "/bin/sh=''${pkgs.busybox-sandbox-shell.out}/bin/busybox" ];
}
'';
description = ''
Configuration for Nix, see
<https://nixos.org/manual/nix/stable/command-ref/conf-file.html> or
{manpage}`nix.conf(5)` for available options.
The value declared here will be translated directly to the key-value pairs Nix expects.
You can use {command}`nix-instantiate --eval --strict '<nixpkgs/nixos>' -A config.nix.settings`
to view the current value. By default it is empty.
Nix configurations defined under {option}`nix.*` will be translated and applied to this
option. In addition, configuration specified in {option}`nix.extraOptions` will be appended
verbatim to the resulting config file.
'';
};
};
};
config = mkIf cfg.enable {
environment.etc."nix/nix.conf".source = nixConf;
nix.settings = {
trusted-public-keys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ];
trusted-users = [ "root" ];
substituters = mkAfter [ "https://cache.nixos.org/" ];
system-features = defaultSystemFeatures;
};
};
}

View File

@@ -0,0 +1,150 @@
# Configuration for the Name Service Switch (/etc/nsswitch.conf).
{
config,
lib,
pkgs,
...
}:
{
options = {
# NSS modules. Hacky!
# Only works with nscd!
system.nssModules = lib.mkOption {
type = lib.types.listOf lib.types.path;
internal = true;
default = [ ];
description = ''
Search path for NSS (Name Service Switch) modules. This allows
several DNS resolution methods to be specified via
{file}`/etc/nsswitch.conf`.
'';
apply = list: {
inherit list;
path = lib.makeLibraryPath list;
};
};
system.nssDatabases = {
passwd = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
List of passwd entries to configure in {file}`/etc/nsswitch.conf`.
Note that "files" is always prepended while "systemd" is appended if nscd is enabled.
This option only takes effect if nscd is enabled.
'';
default = [ ];
};
group = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
List of group entries to configure in {file}`/etc/nsswitch.conf`.
Note that "files" is always prepended while "systemd" is appended if nscd is enabled.
This option only takes effect if nscd is enabled.
'';
default = [ ];
};
shadow = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
List of shadow entries to configure in {file}`/etc/nsswitch.conf`.
Note that "files" is always prepended.
This option only takes effect if nscd is enabled.
'';
default = [ ];
};
sudoers = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
List of sudoers entries to configure in {file}`/etc/nsswitch.conf`.
Note that "files" is always prepended.
This option only takes effect if nscd is enabled.
'';
default = [ ];
};
hosts = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
List of hosts entries to configure in {file}`/etc/nsswitch.conf`.
Note that "files" is always prepended, and "dns" and "myhostname" are always appended.
This option only takes effect if nscd is enabled.
'';
default = [ ];
};
services = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
List of services entries to configure in {file}`/etc/nsswitch.conf`.
Note that "files" is always prepended.
This option only takes effect if nscd is enabled.
'';
default = [ ];
};
};
};
imports = [
(lib.mkRenamedOptionModule [ "system" "nssHosts" ] [ "system" "nssDatabases" "hosts" ])
];
config = {
assertions = [
{
assertion = config.system.nssModules.path != "" -> config.services.nscd.enable;
message = ''
Loading NSS modules from system.nssModules (${config.system.nssModules.path}),
requires services.nscd.enable being set to true.
If disabling nscd is really necessary, it is possible to disable loading NSS modules
by setting `system.nssModules = lib.mkForce [];` in your configuration.nix.
'';
}
];
# Name Service Switch configuration file. Required by the C
# library.
environment.etc."nsswitch.conf".text = ''
passwd: ${lib.concatStringsSep " " config.system.nssDatabases.passwd}
group: ${lib.concatStringsSep " " config.system.nssDatabases.group}
shadow: ${lib.concatStringsSep " " config.system.nssDatabases.shadow}
sudoers: ${lib.concatStringsSep " " config.system.nssDatabases.sudoers}
hosts: ${lib.concatStringsSep " " config.system.nssDatabases.hosts}
networks: files
ethers: files
services: ${lib.concatStringsSep " " config.system.nssDatabases.services}
protocols: files
rpc: files
'';
system.nssDatabases = {
passwd = lib.mkBefore [ "files" ];
group = lib.mkBefore [ "files" ];
shadow = lib.mkBefore [ "files" ];
sudoers = lib.mkBefore [ "files" ];
hosts = lib.mkMerge [
(lib.mkOrder 998 [ "files" ])
(lib.mkOrder 1499 [ "dns" ])
];
services = lib.mkBefore [ "files" ];
};
};
}

View File

@@ -0,0 +1,102 @@
{ config, lib, ... }:
let
cfg = config.powerManagement;
in
{
###### interface
options = {
powerManagement = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to enable power management. This includes support
for suspend-to-RAM and powersave features on laptops.
'';
};
resumeCommands = lib.mkOption {
type = lib.types.lines;
default = "";
description = "Commands executed after the system resumes from suspend-to-RAM.";
};
powerUpCommands = lib.mkOption {
type = lib.types.lines;
default = "";
example = lib.literalExpression ''
"''${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda"
'';
description = ''
Commands executed when the machine powers up. That is,
they're executed both when the system first boots and when
it resumes from suspend or hibernation.
'';
};
powerDownCommands = lib.mkOption {
type = lib.types.lines;
default = "";
example = lib.literalExpression ''
"''${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda"
'';
description = ''
Commands executed when the machine powers down. That is,
they're executed both when the system shuts down and when
it goes to suspend or hibernation.
'';
};
};
};
###### implementation
config = lib.mkIf cfg.enable {
systemd.targets.post-resume = {
description = "Post-Resume Actions";
requires = [ "post-resume.service" ];
after = [ "post-resume.service" ];
wantedBy = [ "sleep.target" ];
unitConfig.StopWhenUnneeded = true;
};
# Service executed before suspending/hibernating.
systemd.services.pre-sleep = {
description = "Pre-Sleep Actions";
wantedBy = [ "sleep.target" ];
before = [ "sleep.target" ];
script = ''
${cfg.powerDownCommands}
'';
serviceConfig.Type = "oneshot";
};
systemd.services.post-resume = {
description = "Post-Resume Actions";
after = [
"suspend.target"
"hibernate.target"
"hybrid-sleep.target"
"suspend-then-hibernate.target"
];
script = ''
/run/current-system/systemd/bin/systemctl try-restart --no-block post-resume.target
${cfg.resumeCommands}
${cfg.powerUpCommands}
'';
serviceConfig.Type = "oneshot";
};
};
}

236
nixos/modules/config/qt.nix Normal file
View File

@@ -0,0 +1,236 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.qt;
platformPackages = with pkgs; {
gnome = [
qgnomeplatform
qgnomeplatform-qt6
];
gtk2 = [
libsForQt5.qtstyleplugins
qt6Packages.qt6gtk2
];
kde = [
kdePackages.kio
kdePackages.plasma-integration
kdePackages.systemsettings
];
lxqt = [
lxqt.lxqt-qtplugin
lxqt.lxqt-config
];
qt5ct = [
libsForQt5.qt5ct
qt6Packages.qt6ct
];
};
stylePackages = with pkgs; {
bb10bright = [ libsForQt5.qtstyleplugins ];
bb10dark = [ libsForQt5.qtstyleplugins ];
cde = [ libsForQt5.qtstyleplugins ];
cleanlooks = [ libsForQt5.qtstyleplugins ];
gtk2 = [
libsForQt5.qtstyleplugins
qt6Packages.qt6gtk2
];
motif = [ libsForQt5.qtstyleplugins ];
plastique = [ libsForQt5.qtstyleplugins ];
adwaita = [
adwaita-qt
adwaita-qt6
];
adwaita-dark = [
adwaita-qt
adwaita-qt6
];
adwaita-highcontrast = [
adwaita-qt
adwaita-qt6
];
adwaita-highcontrastinverse = [
adwaita-qt
adwaita-qt6
];
breeze = [
kdePackages.breeze
kdePackages.breeze.qt5
];
kvantum = [
libsForQt5.qtstyleplugin-kvantum
qt6Packages.qtstyleplugin-kvantum
];
};
in
{
meta.maintainers = with lib.maintainers; [
romildo
thiagokokada
];
imports = [
(lib.mkRenamedOptionModule [ "qt5" "enable" ] [ "qt" "enable" ])
(lib.mkRenamedOptionModule [ "qt5" "platformTheme" ] [ "qt" "platformTheme" ])
(lib.mkRenamedOptionModule [ "qt5" "style" ] [ "qt" "style" ])
];
options = {
qt = {
enable = lib.mkEnableOption "" // {
description = ''
Whether to enable Qt configuration, including theming.
Enabling this option is necessary for Qt plugins to work in the
installed profiles (e.g.: `nix-env -i` or `environment.systemPackages`).
'';
};
platformTheme = lib.mkOption {
type = with lib.types; nullOr (enum (lib.attrNames platformPackages));
default = null;
example = "gnome";
relatedPackages = [
"qgnomeplatform"
"qgnomeplatform-qt6"
[
"libsForQt5"
"qt5ct"
]
[
"libsForQt5"
"qtstyleplugins"
]
[
"kdePackages"
"plasma-integration"
]
[
"kdePackages"
"systemsettings"
]
[
"lxqt"
"lxqt-config"
]
[
"lxqt"
"lxqt-qtplugin"
]
[
"qt6Packages"
"qt6ct"
]
[
"qt6Packages"
"qt6gtk2"
]
];
description = ''
Selects the platform theme to use for Qt applications.
The options are
- `gnome`: Use GNOME theme with [qgnomeplatform](https://github.com/FedoraQt/QGnomePlatform)
- `gtk2`: Use GTK theme with [qtstyleplugins](https://github.com/qt/qtstyleplugins)
- `kde`: Use Qt settings from Plasma.
- `lxqt`: Use LXQt style set using the [lxqt-config-appearance](https://github.com/lxqt/lxqt-config)
application.
- `qt5ct`: Use Qt style set using the [qt5ct](https://sourceforge.net/projects/qt5ct/)
and [qt6ct](https://github.com/trialuser02/qt6ct) applications.
'';
};
style = lib.mkOption {
type = with lib.types; nullOr (enum (lib.attrNames stylePackages));
default = null;
example = "adwaita";
relatedPackages = [
"adwaita-qt"
"adwaita-qt6"
[
"libsForQt5"
"qtstyleplugin-kvantum"
]
[
"libsForQt5"
"qtstyleplugins"
]
[
"qt6Packages"
"qt6gtk2"
]
[
"qt6Packages"
"qtstyleplugin-kvantum"
]
];
description = ''
Selects the style to use for Qt applications.
The options are
- `adwaita`, `adwaita-dark`, `adwaita-highcontrast`, `adawaita-highcontrastinverse`:
Use Adwaita Qt style with
[adwaita](https://github.com/FedoraQt/adwaita-qt)
- `breeze`: Use the Breeze style from
[breeze](https://github.com/KDE/breeze)
- `bb10bright`, `bb10dark`, `cleanlooks`, `gtk2`, `motif`, `plastique`:
Use styles from
[qtstyleplugins](https://github.com/qt/qtstyleplugins)
- `kvantum`: Use styles from
[kvantum](https://github.com/tsujan/Kvantum)
'';
};
};
};
config = lib.mkIf cfg.enable {
assertions =
let
gnomeStyles = [
"adwaita"
"adwaita-dark"
"adwaita-highcontrast"
"adwaita-highcontrastinverse"
"breeze"
];
in
[
{
assertion = cfg.platformTheme == "gnome" -> (builtins.elem cfg.style gnomeStyles);
message = ''
`qt.platformTheme` "gnome" must have `qt.style` set to a theme that supports both Qt and Gtk,
for example: ${lib.concatStringsSep ", " gnomeStyles}.
'';
}
];
environment.variables = {
QT_QPA_PLATFORMTHEME = lib.mkIf (cfg.platformTheme != null) cfg.platformTheme;
QT_STYLE_OVERRIDE = lib.mkIf (cfg.style != null) cfg.style;
};
environment.profileRelativeSessionVariables =
let
qtVersions = with pkgs; [
qt5
qt6
];
in
{
QT_PLUGIN_PATH = map (qt: "/${qt.qtbase.qtPluginPrefix}") qtVersions;
QML2_IMPORT_PATH = map (qt: "/${qt.qtbase.qtQmlPrefix}") qtVersions;
};
environment.systemPackages =
lib.optionals (cfg.platformTheme != null) (platformPackages.${cfg.platformTheme})
++ lib.optionals (cfg.style != null) (stylePackages.${cfg.style});
};
}

View File

@@ -0,0 +1,203 @@
# /etc files related to networking, such as /etc/services.
{
config,
lib,
pkgs,
...
}:
let
cfg = config.networking.resolvconf;
resolvconfOptions =
cfg.extraOptions
++ lib.optional cfg.dnsSingleRequest "single-request"
++ lib.optional cfg.dnsExtensionMechanism "edns0"
++ lib.optional cfg.useLocalResolver "trust-ad";
configText = ''
# This is the default, but we must set it here to prevent
# a collision with an apparently unrelated environment
# variable with the same name exported by dhcpcd.
interface_order='lo lo[0-9]*'
''
+ lib.optionalString config.services.nscd.enable ''
# Invalidate the nscd cache whenever resolv.conf is
# regenerated.
libc_restart='/run/current-system/systemd/bin/systemctl try-restart --no-block nscd.service 2> /dev/null'
''
+ lib.optionalString (lib.length resolvconfOptions > 0) ''
# Options as described in resolv.conf(5)
resolv_conf_options='${lib.concatStringsSep " " resolvconfOptions}'
''
+ lib.optionalString cfg.useLocalResolver ''
# This hosts runs a full-blown DNS resolver.
name_servers='127.0.0.1${lib.optionalString config.networking.enableIPv6 " ::1"}'
''
+ cfg.extraConfig;
in
{
imports = [
(lib.mkRenamedOptionModule
[ "networking" "dnsSingleRequest" ]
[ "networking" "resolvconf" "dnsSingleRequest" ]
)
(lib.mkRenamedOptionModule
[ "networking" "dnsExtensionMechanism" ]
[ "networking" "resolvconf" "dnsExtensionMechanism" ]
)
(lib.mkRenamedOptionModule
[ "networking" "extraResolvconfConf" ]
[ "networking" "resolvconf" "extraConfig" ]
)
(lib.mkRenamedOptionModule
[ "networking" "resolvconfOptions" ]
[ "networking" "resolvconf" "extraOptions" ]
)
(lib.mkRemovedOptionModule [
"networking"
"resolvconf"
"useHostResolvConf"
] "This option was never used for anything anyways")
];
options = {
networking.resolvconf = {
enable = lib.mkOption {
type = lib.types.bool;
default = !(config.environment.etc ? "resolv.conf");
defaultText = lib.literalExpression ''!(config.environment.etc ? "resolv.conf")'';
description = ''
Whether DNS configuration is managed by resolvconf.
'';
};
package = lib.mkOption {
type = lib.types.package;
default = pkgs.openresolv;
defaultText = lib.literalExpression "pkgs.openresolv";
description = ''
The package that provides the system-wide resolvconf command. Defaults to `openresolv`
if this module is enabled. Otherwise, can be used by other modules (for example {option}`services.resolved`) to
provide a compatibility layer.
This option generally shouldn't be set by the user.
'';
};
dnsSingleRequest = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Recent versions of glibc will issue both ipv4 (A) and ipv6 (AAAA)
address queries at the same time, from the same port. Sometimes upstream
routers will systemically drop the ipv4 queries. The symptom of this problem is
that 'getent hosts example.com' only returns ipv6 (or perhaps only ipv4) addresses. The
workaround for this is to specify the option 'single-request' in
/etc/resolv.conf. This option enables that.
'';
};
dnsExtensionMechanism = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Enable the `edns0` option in {file}`resolv.conf`. With
that option set, `glibc` supports use of the extension mechanisms for
DNS (EDNS) specified in RFC 2671. The most popular user of that feature is DNSSEC,
which does not work without it.
'';
};
extraConfig = lib.mkOption {
type = lib.types.lines;
default = "";
example = "libc=NO";
description = ''
Extra configuration to append to {file}`resolvconf.conf`.
'';
};
extraOptions = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [
"ndots:1"
"rotate"
];
description = ''
Set the options in {file}`/etc/resolv.conf`.
'';
};
useLocalResolver = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Use local DNS server for resolving.
'';
};
subscriberFiles = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [ ];
description = ''
Files written by resolvconf updates
'';
internal = true;
};
};
};
config = lib.mkMerge [
{
environment.etc."resolvconf.conf".text =
if !cfg.enable then
# Force-stop any attempts to use resolvconf
''
echo "resolvconf is disabled on this system but was used anyway:" >&2
echo "$0 $*" >&2
exit 1
''
else
configText;
}
(lib.mkIf cfg.enable {
users.groups.resolvconf = { };
networking.resolvconf.subscriberFiles = [ "/etc/resolv.conf" ];
environment.systemPackages = [ cfg.package ];
systemd.services.resolvconf = {
description = "resolvconf update";
before = [ "network-pre.target" ];
wants = [ "network-pre.target" ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [ config.environment.etc."resolvconf.conf".source ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
script = ''
${lib.getExe cfg.package} -u
chgrp resolvconf ${lib.escapeShellArgs cfg.subscriberFiles}
chmod g=u ${lib.escapeShellArgs cfg.subscriberFiles}
${lib.getExe' pkgs.acl "setfacl"} -R \
-m group:resolvconf:rwx \
-m default:group:resolvconf:rwx \
/run/resolvconf
'';
};
})
];
}

View File

@@ -0,0 +1,262 @@
# This module defines a global environment configuration and
# a common configuration for all shells.
{
config,
lib,
utils,
pkgs,
...
}:
let
cfg = config.environment;
exportedEnvVars =
let
absoluteVariables = lib.mapAttrs (n: lib.toList) cfg.variables;
suffixedVariables = lib.flip lib.mapAttrs cfg.profileRelativeEnvVars (
envVar: listSuffixes:
lib.concatMap (profile: map (suffix: "${profile}${suffix}") listSuffixes) cfg.profiles
);
allVariables = lib.zipAttrsWith (n: lib.concatLists) [
absoluteVariables
suffixedVariables
];
exportVariables = lib.mapAttrsToList (
n: v: ''export ${n}="${lib.concatStringsSep ":" v}"''
) allVariables;
in
lib.concatStringsSep "\n" exportVariables;
in
{
options = {
environment.variables = lib.mkOption {
default = { };
example = {
EDITOR = "nvim";
VISUAL = "nvim";
};
description = ''
A set of environment variables used in the global environment.
These variables will be set on shell initialisation (e.g. in /etc/profile).
The value of each variable can be either a string or a list of
strings. The latter is concatenated, interspersed with colon
characters.
Setting a variable to `null` does nothing. You can override a
variable set by another module to `null` to unset it.
'';
type =
with lib.types;
attrsOf (
nullOr (oneOf [
(listOf (oneOf [
int
str
path
]))
int
str
path
])
);
apply =
let
toStr = v: if lib.isPath v then "${v}" else toString v;
in
attrs:
lib.mapAttrs (n: v: if lib.isList v then lib.concatMapStringsSep ":" toStr v else toStr v) (
lib.filterAttrs (n: v: v != null) attrs
);
};
environment.profiles = lib.mkOption {
default = [ ];
description = ''
A list of profiles used to setup the global environment.
'';
type = lib.types.listOf lib.types.str;
};
environment.profileRelativeEnvVars = lib.mkOption {
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
example = {
PATH = [ "/bin" ];
MANPATH = [
"/man"
"/share/man"
];
};
description = ''
Attribute set of environment variable. Each attribute maps to a list
of relative paths. Each relative path is appended to the each profile
of {option}`environment.profiles` to form the content of the
corresponding environment variable.
'';
};
# !!! isn't there a better way?
environment.extraInit = lib.mkOption {
default = "";
description = ''
Shell script code called during global environment initialisation
after all variables and profileVariables have been set.
This code is assumed to be shell-independent, which means you should
stick to pure sh without sh word split.
'';
type = lib.types.lines;
};
environment.shellInit = lib.mkOption {
default = "";
description = ''
Shell script code called during shell initialisation.
This code is assumed to be shell-independent, which means you should
stick to pure sh without sh word split.
'';
type = lib.types.lines;
};
environment.loginShellInit = lib.mkOption {
default = "";
description = ''
Shell script code called during login shell initialisation.
This code is assumed to be shell-independent, which means you should
stick to pure sh without sh word split.
'';
type = lib.types.lines;
};
environment.interactiveShellInit = lib.mkOption {
default = "";
description = ''
Shell script code called during interactive shell initialisation.
This code is assumed to be shell-independent, which means you should
stick to pure sh without sh word split.
'';
type = lib.types.lines;
};
environment.shellAliases = lib.mkOption {
example = {
l = null;
ll = "ls -l";
};
description = ''
An attribute set that maps aliases (the top level attribute names in
this option) to command strings or directly to build outputs. The
aliases are added to all users' shells.
Aliases mapped to `null` are ignored.
'';
type = with lib.types; attrsOf (nullOr (either str path));
};
environment.homeBinInPath = lib.mkOption {
description = ''
Include ~/bin/ in $PATH.
'';
default = false;
type = lib.types.bool;
};
environment.localBinInPath = lib.mkOption {
description = ''
Add ~/.local/bin/ to $PATH
'';
default = false;
type = lib.types.bool;
};
environment.binsh = lib.mkOption {
default = "${config.system.build.binsh}/bin/sh";
defaultText = lib.literalExpression ''"''${config.system.build.binsh}/bin/sh"'';
example = lib.literalExpression ''"''${pkgs.dash}/bin/dash"'';
type = lib.types.path;
visible = false;
description = ''
The shell executable that is linked system-wide to
`/bin/sh`. Please note that NixOS assumes all
over the place that shell to be Bash, so override the default
setting only if you know exactly what you're doing.
'';
};
environment.shells = lib.mkOption {
default = [ ];
example = lib.literalExpression "[ pkgs.bashInteractive pkgs.zsh ]";
description = ''
A list of permissible login shells for user accounts.
No need to mention `/bin/sh`
here, it is placed into this list implicitly.
'';
type = lib.types.listOf (lib.types.either lib.types.shellPackage lib.types.path);
};
};
config = {
system.build.binsh = pkgs.bashInteractive;
# Set session variables in the shell as well. This is usually
# unnecessary, but it allows changes to session variables to take
# effect without restarting the session (e.g. by opening a new
# terminal instead of logging out of X11).
environment.variables = config.environment.sessionVariables;
environment.profileRelativeEnvVars = config.environment.profileRelativeSessionVariables;
environment.shellAliases = lib.mapAttrs (name: lib.mkDefault) {
ls = "ls --color=tty";
ll = "ls -l";
l = "ls -alh";
};
environment.etc.shells.text = ''
${lib.concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
/bin/sh
'';
# For resetting environment with `. /etc/set-environment` when needed
# and discoverability (see motivation of #30418).
environment.etc.set-environment.source = config.system.build.setEnvironment;
system.build.setEnvironment = pkgs.writeText "set-environment" ''
# DO NOT EDIT -- this file has been generated automatically.
# Prevent this file from being sourced by child shells.
export __NIXOS_SET_ENVIRONMENT_DONE=1
${exportedEnvVars}
${cfg.extraInit}
${lib.optionalString cfg.homeBinInPath ''
# ~/bin if it exists overrides other bin directories.
export PATH="$HOME/bin:$PATH"
''}
${lib.optionalString cfg.localBinInPath ''
export PATH="$HOME/.local/bin:$PATH"
''}
'';
system.activationScripts.binsh = lib.stringAfter [ "stdio" ] ''
# Create the required /bin/sh symlink; otherwise lots of things
# (notably the system() function) won't work.
mkdir -p /bin
chmod 0755 /bin
ln -sfn "${cfg.binsh}" /bin/.sh.tmp
mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
'';
};
}

View File

@@ -0,0 +1,49 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
getOutput
maintainers
mkEnableOption
mkIf
mkOption
mkPackageOption
types
;
cfg = config.networking.stevenblack;
in
{
options.networking.stevenblack = {
enable = mkEnableOption "the stevenblack hosts file blocklist";
package = mkPackageOption pkgs "stevenblack-blocklist" { };
block = mkOption {
type = types.listOf (
types.enum [
"fakenews"
"gambling"
"porn"
"social"
]
);
default = [ ];
description = "Additional blocklist extensions.";
};
};
config = mkIf cfg.enable {
networking.hostFiles = map (x: "${getOutput x cfg.package}/hosts") ([ "ads" ] ++ cfg.block);
};
meta.maintainers = with maintainers; [
moni
artturin
frontear
];
}

View File

@@ -0,0 +1,68 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
optionalString
mkOption
types
mkIf
mkDefault
;
cfg = config.environment.stub-ld;
message = ''
NixOS cannot run dynamically linked executables intended for generic
linux environments out of the box. For more information, see:
https://nix.dev/permalink/stub-ld
'';
stub-ld-for =
pkgsArg: messageArg:
pkgsArg.pkgsStatic.runCommandCC "stub-ld"
{
nativeBuildInputs = [ pkgsArg.unixtools.xxd ];
inherit messageArg;
}
''
printf "%s" "$messageArg" | xxd -i -n message >main.c
cat <<EOF >>main.c
#include <stdio.h>
int main(int argc, char * argv[]) {
fprintf(stderr, "Could not start dynamically linked executable: %s\n", argv[0]);
fwrite(message, sizeof(unsigned char), message_len, stderr);
return 127; // matches behavior of bash and zsh without a loader. fish uses 139
}
EOF
$CC -Os main.c -o $out
'';
stub-ld = stub-ld-for pkgs message;
in
{
options = {
environment.stub-ld = {
enable = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Install a stub ELF loader to print an informative error message
in the event that a user attempts to run an ELF binary not
compiled for NixOS.
'';
};
};
};
config = mkIf cfg.enable {
environment.ldso = mkDefault stub-ld;
};
meta.maintainers = with lib.maintainers; [ tejing ];
}

View File

@@ -0,0 +1,354 @@
{
config,
lib,
pkgs,
utils,
...
}:
let
inherit (lib) mkIf mkOption types;
randomEncryptionCoerce = enable: { inherit enable; };
randomEncryptionOpts =
{ ... }:
{
options = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
Encrypt swap device with a random key. This way you won't have a persistent swap device.
WARNING: Don't try to hibernate when you have at least one swap partition with
this option enabled! We have no way to set the partition into which hibernation image
is saved, so if your image ends up on an encrypted one you would lose it!
WARNING #2: Do not use /dev/disk/by-uuid/ or /dev/disk/by-label/ as your swap device
when using randomEncryption as the UUIDs and labels will get erased on every boot when
the partition is encrypted. Best to use /dev/disk/by-partuuid/
'';
};
cipher = mkOption {
default = "aes-xts-plain64";
example = "serpent-xts-plain64";
type = types.str;
description = ''
Use specified cipher for randomEncryption.
Hint: Run "cryptsetup benchmark" to see which one is fastest on your machine.
'';
};
keySize = mkOption {
default = null;
example = "512";
type = types.nullOr types.int;
description = ''
Set the encryption key size for the plain device.
If not specified, the amount of data to read from `source` will be
determined by cryptsetup.
See {manpage}`cryptsetup-open(8)` for details.
'';
};
sectorSize = mkOption {
default = null;
example = "4096";
type = types.nullOr types.int;
description = ''
Set the sector size for the plain encrypted device type.
If not specified, the default sector size is determined from the
underlying block device.
See {manpage}`cryptsetup-open(8)` for details.
'';
};
source = mkOption {
default = "/dev/urandom";
example = "/dev/random";
type = types.str;
description = ''
Define the source of randomness to obtain a random key for encryption.
'';
};
allowDiscards = mkOption {
default = false;
type = types.bool;
description = ''
Whether to allow TRIM requests to the underlying device. This option
has security implications; please read the LUKS documentation before
activating it.
'';
};
};
};
swapCfg =
{ config, options, ... }:
{
options = {
device = mkOption {
example = "/dev/sda3";
type = types.nonEmptyStr;
description = "Path of the device or swap file.";
};
label = mkOption {
example = "swap";
type = types.str;
description = ''
Label of the device. Can be used instead of {var}`device`.
'';
};
size = mkOption {
default = null;
example = 2048;
type = types.nullOr types.int;
description = ''
If this option is set, device is interpreted as the
path of a swapfile that will be created automatically
with the indicated size in MiB (1024×1024 bytes).
'';
};
priority = mkOption {
default = null;
example = 2048;
type = types.nullOr types.int;
description = ''
Specify the priority of the swap device. Priority is a value between 0 and 32767.
Higher numbers indicate higher priority.
null lets the kernel choose a priority, which will show up as a negative value.
'';
};
randomEncryption = mkOption {
default = false;
example = {
enable = true;
cipher = "serpent-xts-plain64";
source = "/dev/random";
};
type = types.coercedTo types.bool randomEncryptionCoerce (types.submodule randomEncryptionOpts);
description = ''
Encrypt swap device with a random key. This way you won't have a persistent swap device.
HINT: run "cryptsetup benchmark" to test cipher performance on your machine.
WARNING: Don't try to hibernate when you have at least one swap partition with
this option enabled! We have no way to set the partition into which hibernation image
is saved, so if your image ends up on an encrypted one you would lose it!
WARNING #2: Do not use /dev/disk/by-uuid/ or /dev/disk/by-label/ as your swap device
when using randomEncryption as the UUIDs and labels will get erased on every boot when
the partition is encrypted. Best to use /dev/disk/by-partuuid/
'';
};
discardPolicy = mkOption {
default = null;
example = "once";
type = types.nullOr (
types.enum [
"once"
"pages"
"both"
]
);
description = ''
Specify the discard policy for the swap device. If "once", then the
whole swap space is discarded at swapon invocation. If "pages",
asynchronous discard on freed pages is performed, before returning to
the available pages pool. With "both", both policies are activated.
See {manpage}`swapon(8)` for more information.
'';
};
options = mkOption {
default = [ "defaults" ];
example = [ "nofail" ];
type = types.listOf types.nonEmptyStr;
description = ''
Options used to mount the swap.
'';
};
deviceName = mkOption {
type = types.str;
internal = true;
};
realDevice = mkOption {
type = types.path;
internal = true;
};
};
config = {
device = mkIf options.label.isDefined "/dev/disk/by-label/${config.label}";
deviceName = lib.replaceStrings [ "\\" ] [ "" ] (utils.escapeSystemdPath config.device);
realDevice =
if config.randomEncryption.enable then "/dev/mapper/${config.deviceName}" else config.device;
};
};
in
{
###### interface
options = {
swapDevices = mkOption {
default = [ ];
example = [
{ device = "/dev/hda7"; }
{ device = "/var/swapfile"; }
{ label = "bigswap"; }
];
description = ''
The swap devices and swap files. These must have been
initialised using {command}`mkswap`. Each element
should be an attribute set specifying either the path of the
swap device or file (`device`) or the label
of the swap device (`label`, see
{command}`mkswap -L`). Using a label is
recommended.
'';
type = types.listOf (types.submodule swapCfg);
};
};
config = mkIf ((lib.length config.swapDevices) != 0) {
assertions = lib.map (sw: {
assertion =
sw.randomEncryption.enable -> builtins.match "/dev/disk/by-(uuid|label)/.*" sw.device == null;
message = ''
You cannot use swap device "${sw.device}" with randomEncryption enabled.
The UUIDs and labels will get erased on every boot when the partition is encrypted.
Use /dev/disk/by-partuuid/ instead.
'';
}) config.swapDevices;
warnings = lib.concatMap (
sw:
if sw.size != null && lib.hasPrefix "/dev/" sw.device then
[ "Setting the swap size of block device ${sw.device} has no effect" ]
else
[ ]
) config.swapDevices;
system.requiredKernelConfig = [
(config.lib.kernelConfig.isYes "SWAP")
];
# Create missing swapfiles.
systemd.services =
let
createSwapDevice =
sw:
let
realDevice' = utils.escapeSystemdPath sw.realDevice;
btrfsInSystem = config.boot.supportedFilesystems.btrfs or false;
in
lib.nameValuePair "mkswap-${sw.deviceName}" {
description = "Initialisation of swap device ${sw.device}";
# The mkswap service fails for file-backed swap devices if the
# loop module has not been loaded before the service runs.
# We add an ordering constraint to run after systemd-modules-load to
# avoid this race condition.
after = [ "systemd-modules-load.service" ];
wantedBy = [ "${realDevice'}.swap" ];
requiredBy = lib.optionals sw.randomEncryption.enable [ "${realDevice'}.swap" ];
before = [
"${realDevice'}.swap"
"shutdown.target"
];
conflicts = [ "shutdown.target" ];
path = [
pkgs.util-linux
pkgs.e2fsprogs
]
++ lib.optional btrfsInSystem pkgs.btrfs-progs
++ lib.optional sw.randomEncryption.enable pkgs.cryptsetup;
environment.DEVICE = sw.device;
script = ''
${lib.optionalString (sw.size != null) ''
currentSize=$(( $(stat -c "%s" "$DEVICE" 2>/dev/null || echo 0) / 1024 / 1024 ))
if [[ ! -b "$DEVICE" && "${toString sw.size}" != "$currentSize" ]]; then
if [[ $(stat -f -c %T $(dirname "$DEVICE")) == "btrfs" ]]; then
# Use btrfs mkswapfile to speed up the creation of swapfile.
rm -f "$DEVICE"
btrfs filesystem mkswapfile --size "${toString sw.size}M" --uuid clear "$DEVICE"
else
# Disable CoW for CoW based filesystems.
truncate --size 0 "$DEVICE"
chattr +C "$DEVICE" 2>/dev/null || true
echo "Creating swap file using dd and mkswap."
dd if=/dev/zero of="$DEVICE" bs=1M count=${toString sw.size} status=progress
${lib.optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"}
fi
fi
''}
${lib.optionalString sw.randomEncryption.enable ''
cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \
${
lib.concatStringsSep " \\\n" (
lib.flatten [
(lib.optional (
sw.randomEncryption.sectorSize != null
) "--sector-size=${toString sw.randomEncryption.sectorSize}")
(lib.optional (
sw.randomEncryption.keySize != null
) "--key-size=${toString sw.randomEncryption.keySize}")
(lib.optional sw.randomEncryption.allowDiscards "--allow-discards")
]
)
} ${sw.device} ${sw.deviceName}
mkswap ${sw.realDevice}
''}
'';
unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ];
unitConfig.DefaultDependencies = false; # needed to prevent a cycle
serviceConfig = {
Type = "oneshot";
RemainAfterExit = sw.randomEncryption.enable;
UMask = "0177";
ExecStop = lib.optionalString sw.randomEncryption.enable "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}";
};
restartIfChanged = false;
};
in
lib.listToAttrs (
lib.map createSwapDevice (
lib.filter (sw: sw.size != null || sw.randomEncryption.enable) config.swapDevices
)
);
};
}

View File

@@ -0,0 +1,92 @@
{ config, lib, ... }:
let
sysctlOption = lib.mkOptionType {
name = "sysctl option value";
check =
val:
let
checkType = x: lib.isBool x || lib.isString x || lib.isInt x || x == null;
in
checkType val || (val._type or "" == "override" && checkType val.content);
merge = loc: defs: lib.mergeOneOption loc defs;
};
in
{
options = {
boot.kernel.sysctl = lib.mkOption {
type =
let
highestValueType = lib.types.ints.unsigned // {
merge = loc: defs: lib.foldl (a: b: if b.value == null then null else lib.max a b.value) 0 defs;
};
in
lib.types.submodule {
freeformType = lib.types.attrsOf sysctlOption;
options = {
"net.core.rmem_max" = lib.mkOption {
type = lib.types.nullOr highestValueType;
default = null;
description = "The maximum receive socket buffer size in bytes. In case of conflicting values, the highest will be used.";
};
"net.core.wmem_max" = lib.mkOption {
type = lib.types.nullOr highestValueType;
default = null;
description = "The maximum send socket buffer size in bytes. In case of conflicting values, the highest will be used.";
};
};
};
default = { };
example = lib.literalExpression ''
{ "net.ipv4.tcp_syncookies" = false; "vm.swappiness" = 60; }
'';
description = ''
Runtime parameters of the Linux kernel, as set by
{manpage}`sysctl(8)`. Note that sysctl
parameters names must be enclosed in quotes
(e.g. `"vm.swappiness"` instead of
`vm.swappiness`). The value of each
parameter may be a string, integer, boolean, or null
(signifying the option will not appear at all).
'';
};
};
config = {
environment.etc."sysctl.d/60-nixos.conf".text = lib.concatStrings (
lib.mapAttrsToList (
n: v: lib.optionalString (v != null) "${n}=${if v == false then "0" else toString v}\n"
) config.boot.kernel.sysctl
);
systemd.services.systemd-sysctl = {
wantedBy = [ "multi-user.target" ];
restartTriggers = [ config.environment.etc."sysctl.d/60-nixos.conf".source ];
};
# NixOS wide defaults
boot.kernel.sysctl = {
# Hide kernel pointers (e.g. in /proc/modules) for unprivileged
# users as these make it easier to exploit kernel vulnerabilities.
"kernel.kptr_restrict" = lib.mkDefault 1;
# Improve compatibility with applications that allocate
# a lot of memory, like modern games
"vm.max_map_count" = lib.mkDefault 1048576;
# The default max inotify watches is 8192.
# Nowadays most apps require a good number of inotify watches,
# the value below is used by default on several other distros.
"fs.inotify.max_user_instances" = lib.mkDefault 524288;
"fs.inotify.max_user_watches" = lib.mkDefault 524288;
};
};
}

View File

@@ -0,0 +1,265 @@
{
lib,
config,
utils,
pkgs,
...
}:
let
inherit (lib)
all
any
concatLines
concatStringsSep
escapeShellArg
flatten
floatToString
foldl'
head
isAttrs
isDerivation
isFloat
isList
length
listToAttrs
match
mapAttrsToList
nameValuePair
removePrefix
tail
throwIf
;
inherit (lib.options)
showDefs
showOption
;
inherit (lib.strings)
escapeC
isConvertibleWithToString
;
inherit (lib.path.subpath) join;
inherit (utils) escapeSystemdPath;
cfg = config.boot.kernel.sysfs;
sysfsAttrs = with lib.types; nullOr (either sysfsValue (attrsOf sysfsAttrs));
sysfsValue = lib.mkOptionType {
name = "sysfs value";
description = "sysfs attribute value";
descriptionClass = "noun";
check = v: isConvertibleWithToString v;
merge =
loc: defs:
if length defs == 1 then
(head defs).value
else
(foldl' (
first: def:
# merge definitions if they produce the same value string
throwIf (mkValueString first.value != mkValueString def.value)
"The option \"${showOption loc}\" has conflicting definition values:${
showDefs [
first
def
]
}"
first
) (head defs) (tail defs)).value;
};
mapAttrsToListRecursive =
fn: set:
let
recurse =
p: v:
if isAttrs v && !isDerivation v then mapAttrsToList (n: v: recurse (p ++ [ n ]) v) v else fn p v;
in
flatten (recurse [ ] set);
mkPath = p: "/sys" + removePrefix "." (join p);
hasGlob = p: any (n: match ''(.*[^\\])?[*?[].*'' n != null) p;
mkValueString =
v:
# true will be converted to "1" by toString, saving one branch
if v == false then
"0"
else if isFloat v then
floatToString v # warn about loss of precision
else if isList v then
concatStringsSep "," (map mkValueString v)
else
toString v;
# escape whitespace and linebreaks, as well as the escape character itself,
# to ensure that field boundaries are always preserved
escapeTmpfiles = escapeC [
"\t"
"\n"
"\r"
" "
"\\"
];
tmpfiles = pkgs.runCommand "nixos-sysfs-tmpfiles.d" { } (
''
mkdir "$out"
''
+ concatLines (
mapAttrsToListRecursive (
p: v:
let
path = mkPath p;
in
if v == null then
[ ]
else
''
printf 'w %s - - - - %s\n' \
${escapeShellArg (escapeTmpfiles path)} \
${escapeShellArg (escapeTmpfiles (mkValueString v))} \
>"$out"/${escapeShellArg (escapeSystemdPath path)}.conf
''
) cfg
)
);
in
{
options = {
boot.kernel.sysfs = lib.mkOption {
type = lib.types.submodule {
freeformType = lib.types.attrsOf sysfsAttrs // {
description = "nested attribute set of null or sysfs attribute values";
};
};
description = ''
sysfs attributes to be set as soon as they become available.
Attribute names represent path components in the sysfs filesystem and
cannot be `.` or `..` nor contain any slash character (`/`).
Names may contain shellstyle glob patterns (`*`, `?` and `[]`)
matching a single path component, these should however be used with
caution, as they may produce unexpected results if attribute paths
overlap.
Values will be converted to strings, with list elements concatenated
with commata and booleans converted to numeric values (`0` or `1`).
`null` values are ignored, allowing removal of values defined in other
modules, as are empty attribute sets.
List values defined in different modules will _not_ be concatenated.
This option may only be used for attributes which can be set
idempotently, as the configured values might be written more than once.
'';
default = { };
example = lib.literalExpression ''
{
# enable transparent hugepages with deferred defragmentaion
kernel.mm.transparent_hugepage = {
enabled = "always";
defrag = "defer";
shmem_enabled = "within_size";
};
devices.system.cpu = {
# configure powesave frequency governor for all CPUs
# the [0-9]* glob pattern ensures that other paths
# like cpufreq or cpuidle are not matched
"cpu[0-9]*" = {
scaling_governor = "powersave";
energy_performance_preference = 8;
};
# disable frequency boost
intel_pstate.no_turbo = true;
};
}
'';
};
};
config = lib.mkIf (cfg != { }) {
systemd = {
paths = {
"nixos-sysfs@" = {
description = "/%I attribute watcher";
pathConfig.PathExistsGlob = "/%I";
unitConfig.DefaultDependencies = false;
};
}
// listToAttrs (
mapAttrsToListRecursive (
p: v:
if v == null then
[ ]
else
nameValuePair "nixos-sysfs@${escapeSystemdPath (mkPath p)}" {
overrideStrategy = "asDropin";
wantedBy = [ "sysinit.target" ];
before = [ "sysinit.target" ];
}
) cfg
);
services."nixos-sysfs@" = {
description = "/%I attribute setter";
unitConfig = {
DefaultDependencies = false;
AssertPathIsMountPoint = "/sys";
AssertPathExistsGlob = "/%I";
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
# while we could be tempted to use simple shell script to set the
# sysfs attributes specified by the path or glob pattern, it is
# almost impossible to properly escape a glob pattern so that it
# can be used safely in a shell script
ExecStart = "${lib.getExe' config.systemd.package "systemd-tmpfiles"} --prefix=/sys --create ${tmpfiles}/%i.conf";
# hardening may be overkill for such a simple and shortlived
# service, the following settings would however be suitable to deny
# access to anything but /sys
#ProtectProc = "noaccess";
#ProcSubset = "pid";
#ProtectSystem = "strict";
#PrivateDevices = true;
#SystemCallErrorNumber = "EPERM";
#SystemCallFilter = [
# "@basic-io"
# "@file-system"
#];
};
};
};
warnings = mapAttrsToListRecursive (
p: v:
if hasGlob p then
"Attribute path \"${concatStringsSep "." p}\" contains glob patterns. Please ensure that it does not overlap with other paths."
else
[ ]
) cfg;
assertions = mapAttrsToListRecursive (p: v: {
assertion = all (n: match ''(\.\.?|.*/.*)'' n == null) p;
message = "Attribute path \"${concatStringsSep "." p}\" has invalid components.";
}) cfg;
};
meta.maintainers = with lib.maintainers; [ mvs ];
}

View File

@@ -0,0 +1,112 @@
# This module defines a system-wide environment that will be
# initialised by pam_env (that is, not only in shells).
{
config,
lib,
options,
pkgs,
...
}:
let
cfg = config.environment;
in
{
options = {
environment.sessionVariables = lib.mkOption {
default = { };
description = ''
A set of environment variables used in the global environment.
These variables will be set by PAM early in the login process.
The value of each session variable can be either a string or a
list of strings. The latter is concatenated, interspersed with
colon characters.
Setting a variable to `null` does nothing. You can override a
variable set by another module to `null` to unset it.
Note, due to limitations in the PAM format values may not
contain the `"` character.
Also, these variables are merged into
[](#opt-environment.variables) and it is
therefore not possible to use PAM style variables such as
`@{HOME}`.
'';
inherit (options.environment.variables) type apply;
};
environment.profileRelativeSessionVariables = lib.mkOption {
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
example = {
PATH = [ "/bin" ];
MANPATH = [
"/man"
"/share/man"
];
};
description = ''
Attribute set of environment variable used in the global
environment. These variables will be set by PAM early in the
login process.
Variable substitution is available as described in
{manpage}`pam_env.conf(5)`.
Each attribute maps to a list of relative paths. Each relative
path is appended to the each profile of
{option}`environment.profiles` to form the content of
the corresponding environment variable.
Also, these variables are merged into
[](#opt-environment.profileRelativeEnvVars) and it is
therefore not possible to use PAM style variables such as
`@{HOME}`.
'';
};
};
config = {
environment.etc."pam/environment".text =
let
suffixedVariables = lib.flip lib.mapAttrs cfg.profileRelativeSessionVariables (
envVar: suffixes:
lib.flip lib.concatMap cfg.profiles (profile: map (suffix: "${profile}${suffix}") suffixes)
);
# We're trying to use the same syntax for PAM variables and env variables.
# That means we need to map the env variables that people might use to their
# equivalent PAM variable.
replaceEnvVars = lib.replaceStrings [ "$HOME" "$USER" ] [ "@{HOME}" "@{PAM_USER}" ];
pamVariable =
n: v: ''${n} DEFAULT="${lib.concatStringsSep ":" (map replaceEnvVars (lib.toList v))}"'';
pamVariables = lib.concatStringsSep "\n" (
lib.mapAttrsToList pamVariable (
lib.zipAttrsWith (n: lib.concatLists) [
# Make sure security wrappers are prioritized without polluting
# shell environments with an extra entry. Sessions which depend on
# pam for its environment will otherwise have eg. broken sudo. In
# particular Gnome Shell sometimes fails to source a proper
# environment from a shell.
{ PATH = [ config.security.wrapperDir ]; }
(lib.mapAttrs (n: lib.toList) cfg.sessionVariables)
suffixedVariables
]
)
);
in
''
${pamVariables}
'';
};
}

View File

@@ -0,0 +1,227 @@
# This module defines the packages that appear in
# /run/current-system/sw.
{
config,
lib,
pkgs,
...
}:
let
corePackageNames = [
"acl"
"attr"
"bashInteractive" # bash with ncurses support
"bzip2"
"coreutils-full"
"cpio"
"curl"
"diffutils"
"findutils"
"gawk"
"getent"
"getconf"
"gnugrep"
"gnupatch"
"gnused"
"gnutar"
"gzip"
"xz"
"less"
"libcap"
"ncurses"
"netcat"
"mkpasswd"
"procps"
"su"
"time"
"util-linux"
"which"
"zstd"
];
corePackages =
(map (
n:
let
pkg = pkgs.${n};
in
lib.setPrio ((pkg.meta.priority or lib.meta.defaultPriority) + 3) pkg
) corePackageNames)
++ [ pkgs.stdenv.cc.libc ];
corePackagesText = "[ ${lib.concatMapStringsSep " " (n: "pkgs.${n}") corePackageNames} ]";
defaultPackageNames = [
"perl"
"rsync"
"strace"
];
defaultPackages = map (
n:
let
pkg = pkgs.${n};
in
lib.setPrio ((pkg.meta.priority or lib.meta.defaultPriority) + 3) pkg
) defaultPackageNames;
defaultPackagesText = "[ ${lib.concatMapStringsSep " " (n: "pkgs.${n}") defaultPackageNames} ]";
in
{
options = {
environment = {
systemPackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
example = lib.literalExpression "[ pkgs.firefox pkgs.thunderbird ]";
description = ''
The set of packages that appear in
/run/current-system/sw. These packages are
automatically available to all users, and are
automatically updated every time you rebuild the system
configuration. (The latter is the main difference with
installing them in the default profile,
{file}`/nix/var/nix/profiles/default`.
'';
};
corePackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
defaultText = lib.literalMD ''
these packages, with their `meta.priority` numerically increased
(thus lowering their installation priority):
${corePackagesText}
'';
example = [ ];
description = ''
Set of core packages for a normal interactive system.
Only change this if you know what you're doing!
Like with systemPackages, packages are installed to
{file}`/run/current-system/sw`. They are
automatically available to all users, and are
automatically updated every time you rebuild the system
configuration.
'';
};
defaultPackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = defaultPackages;
defaultText = lib.literalMD ''
these packages, with their `meta.priority` numerically increased
(thus lowering their installation priority):
${defaultPackagesText}
'';
example = [ ];
description = ''
Set of default packages that aren't strictly necessary
for a running system, entries can be removed for a more
minimal NixOS installation.
Like with systemPackages, packages are installed to
{file}`/run/current-system/sw`. They are
automatically available to all users, and are
automatically updated every time you rebuild the system
configuration.
'';
};
pathsToLink = lib.mkOption {
type = lib.types.listOf lib.types.str;
# Note: We need `/lib' to be among `pathsToLink' for NSS modules
# to work.
default = [ ];
example = [ "/" ];
description = "List of directories to be symlinked in {file}`/run/current-system/sw`.";
};
extraOutputsToInstall = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [
"dev"
"info"
];
description = ''
Entries listed here will be appended to the `meta.outputsToInstall` attribute for each package in `environment.systemPackages`, and the files from the corresponding derivation outputs symlinked into {file}`/run/current-system/sw`.
For example, this can be used to install the `dev` and `info` outputs for all packages in the system environment, if they are available.
To use specific outputs instead of configuring them globally, select the corresponding attribute on the package derivation, e.g. `libxml2.dev` or `coreutils.info`.
'';
};
extraSetup = lib.mkOption {
type = lib.types.lines;
default = "";
description = "Shell fragments to be run after the system environment has been created. This should only be used for things that need to modify the internals of the environment, e.g. generating MIME caches. The environment being built can be accessed at $out.";
};
};
system = {
path = lib.mkOption {
internal = true;
description = ''
The packages you want in the boot environment.
'';
};
};
};
config = {
# Set this here so that it has the right priority and allows ergonomic
# merging.
environment.corePackages = corePackages;
environment.systemPackages = config.environment.corePackages ++ config.environment.defaultPackages;
environment.pathsToLink = [
"/bin"
"/etc/xdg"
"/etc/gtk-2.0"
"/etc/gtk-3.0"
"/lib" # FIXME: remove and update debug-info.nix
"/sbin"
"/share/emacs"
"/share/hunspell"
"/share/org"
"/share/themes"
"/share/vulkan"
"/share/kservices5"
"/share/kservicetypes5"
"/share/kxmlgui5"
"/share/systemd"
"/share/thumbnailers"
];
system.path = pkgs.buildEnv {
name = "system-path";
paths = config.environment.systemPackages;
inherit (config.environment) pathsToLink extraOutputsToInstall;
ignoreCollisions = true;
# !!! Hacky, should modularise.
# outputs TODO: note that the tools will often not be linked by default
postBuild = ''
# Remove wrapped binaries, they shouldn't be accessible via PATH.
find $out/bin -maxdepth 1 -name ".*-wrapped" -type l -delete
if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then
$out/bin/glib-compile-schemas $out/share/glib-2.0/schemas
fi
${config.environment.extraSetup}
'';
};
};
}

View File

@@ -0,0 +1,93 @@
# This module manages the terminfo database
# and its integration in the system.
{
config,
lib,
pkgs,
...
}:
{
options = {
environment.enableAllTerminfo = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Whether to install all terminfo outputs
'';
};
security.sudo.keepTerminfo = lib.mkOption {
default = true;
type = lib.types.bool;
description = ''
Whether to preserve the `TERMINFO` and `TERMINFO_DIRS`
environment variables, for `root` and the `wheel` group.
'';
};
};
config = {
# This should not contain packages that are broken or can't build, since it
# will break this expression
#
# can be generated with:
# lib.attrNames (lib.filterAttrs
# (_: drv: (builtins.tryEval (lib.isDerivation drv && drv ? terminfo)).value)
# pkgs)
environment.systemPackages = lib.mkIf config.environment.enableAllTerminfo (
map (x: x.terminfo) (
with pkgs.pkgsBuildBuild;
[
alacritty
contour
foot
ghostty
kitty
mtm
rio
rxvt-unicode-unwrapped
rxvt-unicode-unwrapped-emoji
st
termite
tmux
wezterm
yaft
]
)
);
environment.pathsToLink = [
"/share/terminfo"
];
environment.etc.terminfo = {
source = "${config.system.path}/share/terminfo";
};
environment.profileRelativeSessionVariables = {
TERMINFO_DIRS = [ "/share/terminfo" ];
};
environment.extraInit = ''
# reset TERM with new TERMINFO available (if any)
export TERM=$TERM
'';
security =
let
extraConfig = ''
# Keep terminfo database for root and %wheel.
Defaults:root,%wheel env_keep+=TERMINFO_DIRS
Defaults:root,%wheel env_keep+=TERMINFO
'';
in
lib.mkIf config.security.sudo.keepTerminfo {
sudo = { inherit extraConfig; };
sudo-rs = { inherit extraConfig; };
};
};
}

View File

@@ -0,0 +1,38 @@
{ config, lib, ... }:
# unixODBC drivers (this solution is not perfect.. Because the user has to
# ask the admin to add a driver.. but it's simple and works
let
iniDescription = pkg: ''
[${pkg.fancyName}]
Description = ${pkg.meta.description}
Driver = ${pkg}/${pkg.driver}
'';
in
{
###### interface
options = {
environment.unixODBCDrivers = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
example = lib.literalExpression "with pkgs.unixODBCDrivers; [ sqlite psql ]";
description = ''
Specifies Unix ODBC drivers to be registered in
{file}`/etc/odbcinst.ini`. You may also want to
add `pkgs.unixODBC` to the system path to get
a command line client to connect to ODBC databases.
'';
};
};
###### implementation
config = lib.mkIf (config.environment.unixODBCDrivers != [ ]) {
environment.etc."odbcinst.ini".text =
lib.concatMapStringsSep "\n" iniDescription
config.environment.unixODBCDrivers;
};
}

View File

@@ -0,0 +1,382 @@
use strict;
use warnings;
use File::Path qw(make_path);
use File::Slurp;
use Getopt::Long;
use JSON;
use Time::Piece;
# 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)) : {};
my $is_dry = ($ENV{'NIXOS_ACTION'} // "") eq "dry-activate";
GetOptions("dry-activate" => \$is_dry);
make_path("/var/lib/nixos", { mode => 0755 }) unless $is_dry;
sub updateFile {
my ($path, $contents, $perms) = @_;
return if $is_dry;
write_file($path, { atomic => 1, binmode => ':utf8', perms => $perms // 0644 }, $contents) or die;
}
# Converts an ISO date to number of days since 1970-01-01
sub dateToDays {
my ($date) = @_;
my $time = Time::Piece->strptime($date, "%Y-%m-%d");
return $time->epoch / 60 / 60 / 24;
}
sub nscdInvalidate {
system("nscd", "--invalidate", $_[0]) unless $is_dry;
}
sub hashPassword {
my ($password) = @_;
my $salt = "";
my @chars = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
$salt .= $chars[rand 64] for (1..8);
return crypt($password, '$6$' . $salt . '$');
}
sub dry_print {
if ($is_dry) {
print STDERR ("$_[1] $_[2]\n")
} else {
print STDERR ("$_[0] $_[2]\n")
}
}
# Functions for allocating free GIDs/UIDs. FIXME: respect ID ranges in
# /etc/login.defs.
sub allocId {
my ($used, $prevUsed, $idMin, $idMax, $delta, $getid) = @_;
my $id = $delta > 0 ? $idMin : $idMax;
while ($id >= $idMin && $id <= $idMax) {
if (!$used->{$id} && !$prevUsed->{$id} && !defined &$getid($id)) {
$used->{$id} = 1;
return $id;
}
$id += $delta;
}
die "$0: out of free UIDs or GIDs\n";
}
my (%gidsUsed, %uidsUsed, %gidsPrevUsed, %uidsPrevUsed);
sub allocGid {
my ($name) = @_;
my $prevGid = $gidMap->{$name};
if (defined $prevGid && !defined $gidsUsed{$prevGid}) {
dry_print("reviving", "would revive", "group '$name' with GID $prevGid");
$gidsUsed{$prevGid} = 1;
return $prevGid;
}
return allocId(\%gidsUsed, \%gidsPrevUsed, 400, 999, -1, sub { my ($gid) = @_; getgrgid($gid) });
}
sub allocUid {
my ($name, $isSystemUser) = @_;
my ($min, $max, $delta) = $isSystemUser ? (400, 999, -1) : (1000, 29999, 1);
my $prevUid = $uidMap->{$name};
if (defined $prevUid && $prevUid >= $min && $prevUid <= $max && !defined $uidsUsed{$prevUid}) {
dry_print("reviving", "would revive", "user '$name' with UID $prevUid");
$uidsUsed{$prevUid} = 1;
return $prevUid;
}
return allocId(\%uidsUsed, \%uidsPrevUsed, $min, $max, $delta, 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 manually assigned.
foreach my $g (@{$spec->{groups}}) {
$gidsUsed{$g->{gid}} = 1 if defined $g->{gid};
}
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;
my @f = split(':', $_, -4);
my $gid = $f[2] eq "" ? undef : int($f[2]);
$gidsUsed{$gid} = 1 if defined $gid;
return ($f[0], { name => $f[0], password => $f[1], gid => $gid, members => $f[3] });
}
my %groupsCur = -f "/etc/group" ? map { parseGroup } read_file("/etc/group", { binmode => ":utf8" }) : ();
# Read the current /etc/passwd.
sub parseUser {
chomp;
my @f = split(':', $_, -7);
my $uid = $f[2] eq "" ? undef : int($f[2]);
$uidsUsed{$uid} = 1 if defined $uid;
return ($f[0], { name => $f[0], fakePassword => $f[1], uid => $uid,
gid => $f[3], description => $f[4], home => $f[5], shell => $f[6] });
}
my %usersCur = -f "/etc/passwd" ? map { parseUser } read_file("/etc/passwd", { binmode => ":utf8" }) : ();
# Read the groups that were created declaratively (i.e. not by groups)
# in the past. These must be removed if they are no longer in the
# current spec.
my $declGroupsFile = "/var/lib/nixos/declarative-groups";
my %declGroups;
$declGroups{$_} = 1 foreach split / /, -e $declGroupsFile ? read_file($declGroupsFile, { binmode => ":utf8" }) : "";
# Idem for the users.
my $declUsersFile = "/var/lib/nixos/declarative-users";
my %declUsers;
$declUsers{$_} = 1 foreach split / /, -e $declUsersFile ? read_file($declUsersFile, { binmode => ":utf8" }) : "";
# Generate a new /etc/group containing the declared groups.
my %groupsOut;
foreach my $g (@{$spec->{groups}}) {
my $name = $g->{name};
my $existing = $groupsCur{$name};
my %members = map { ($_, 1) } @{$g->{members}};
if (defined $existing) {
$g->{gid} = $existing->{gid} if !defined $g->{gid};
if ($g->{gid} != $existing->{gid}) {
dry_print("warning: not applying", "warning: would not apply", "GID change of group $name ($existing->{gid} -> $g->{gid}) in /etc/group");
$g->{gid} = $existing->{gid};
}
$g->{password} = $existing->{password}; # do we want this?
if ($spec->{mutableUsers}) {
# Merge in non-declarative group members.
foreach my $uname (split /,/, $existing->{members} // "") {
$members{$uname} = 1 if !defined $declUsers{$uname};
}
}
} else {
$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.
updateFile($declGroupsFile, join(" ", sort(keys %groupsOut)));
# Merge in the existing /etc/group.
foreach my $name (keys %groupsCur) {
my $g = $groupsCur{$name};
next if defined $groupsOut{$name};
if (!$spec->{mutableUsers} || defined $declGroups{$name}) {
dry_print("removing group", "would remove group", "$name");
} else {
$groupsOut{$name} = $g;
}
}
# Rewrite /etc/group. FIXME: acquire lock.
my @lines = map { join(":", $_->{name}, $_->{password}, $_->{gid}, $_->{members}) . "\n" }
(sort { $a->{gid} <=> $b->{gid} } values(%groupsOut));
updateFile($gidMapFile, to_json($gidMap, {canonical => 1}));
updateFile("/etc/group", \@lines);
nscdInvalidate("group");
# Generate a new /etc/passwd containing the declared users.
my %usersOut;
foreach my $u (@{$spec->{users}}) {
my $name = $u->{name};
# Resolve the gid of the user.
if ($u->{group} =~ /^[0-9]$/) {
$u->{gid} = $u->{group};
} elsif (defined $groupsOut{$u->{group}}) {
$u->{gid} = $groupsOut{$u->{group}}->{gid} // die;
} else {
warn "warning: user $name has unknown group $u->{group}\n";
$u->{gid} = 65534;
}
my $existing = $usersCur{$name};
if (defined $existing) {
$u->{uid} = $existing->{uid} if !defined $u->{uid};
if ($u->{uid} != $existing->{uid}) {
dry_print("warning: not applying", "warning: would not apply", "UID change of user $name ($existing->{uid} -> $u->{uid}) in /etc/passwd");
$u->{uid} = $existing->{uid};
}
} else {
$u->{uid} = allocUid($name, $u->{isSystemUser}) if !defined $u->{uid};
if (!defined $u->{hashedPassword}) {
if (defined $u->{initialPassword}) {
$u->{hashedPassword} = hashPassword($u->{initialPassword});
} elsif (defined $u->{initialHashedPassword}) {
$u->{hashedPassword} = $u->{initialHashedPassword};
}
}
}
# Ensure home directory incl. ownership and permissions.
if ($u->{createHome} and !$is_dry) {
make_path($u->{home}, { mode => 0755 }) if ! -e $u->{home};
chown $u->{uid}, $u->{gid}, $u->{home};
chmod oct($u->{homeMode}), $u->{home};
}
if (defined $u->{hashedPasswordFile}) {
if (-e $u->{hashedPasswordFile}) {
$u->{hashedPassword} = read_file($u->{hashedPasswordFile});
chomp $u->{hashedPassword};
} else {
warn "warning: password file $u->{hashedPasswordFile} does not exist\n";
}
} elsif (defined $u->{password}) {
$u->{hashedPassword} = hashPassword($u->{password});
}
if (!defined $u->{shell}) {
if (defined $existing) {
$u->{shell} = $existing->{shell};
} else {
warn "warning: no declarative or previous shell for $name, setting shell to nologin\n";
$u->{shell} = "/run/current-system/sw/bin/nologin";
}
}
$u->{fakePassword} = $existing->{fakePassword} // "x";
$usersOut{$name} = $u;
$uidMap->{$name} = $u->{uid};
}
# Update the persistent list of declarative users.
updateFile($declUsersFile, join(" ", sort(keys %usersOut)));
# Merge in the existing /etc/passwd.
foreach my $name (keys %usersCur) {
my $u = $usersCur{$name};
next if defined $usersOut{$name};
if (!$spec->{mutableUsers} || defined $declUsers{$name}) {
dry_print("removing user", "would remove user", "$name");
} else {
$usersOut{$name} = $u;
}
}
# Rewrite /etc/passwd. FIXME: acquire lock.
@lines = map { join(":", $_->{name}, $_->{fakePassword}, $_->{uid}, $_->{gid}, $_->{description}, $_->{home}, $_->{shell}) . "\n" }
(sort { $a->{uid} <=> $b->{uid} } (values %usersOut));
updateFile($uidMapFile, to_json($uidMap, {canonical => 1}));
updateFile("/etc/passwd", \@lines);
nscdInvalidate("passwd");
# Rewrite /etc/shadow to add new accounts or remove dead ones.
my @shadowNew;
my %shadowSeen;
foreach my $line (-f "/etc/shadow" ? read_file("/etc/shadow", { binmode => ":utf8" }) : ()) {
chomp $line;
# struct name copied from `man 3 shadow`
my ($sp_namp, $sp_pwdp, $sp_lstch, $sp_min, $sp_max, $sp_warn, $sp_inact, $sp_expire, $sp_flag) = split(':', $line, -9);
my $u = $usersOut{$sp_namp};;
next if !defined $u;
$sp_pwdp = "!" if !$spec->{mutableUsers};
$sp_pwdp = $u->{hashedPassword} if defined $u->{hashedPassword} && !$spec->{mutableUsers}; # FIXME
$sp_expire = dateToDays($u->{expires}) if defined $u->{expires};
chomp $sp_pwdp;
push @shadowNew, join(":", $sp_namp, $sp_pwdp, $sp_lstch, $sp_min, $sp_max, $sp_warn, $sp_inact, $sp_expire, $sp_flag) . "\n";
$shadowSeen{$sp_namp} = 1;
}
foreach my $u (values %usersOut) {
next if defined $shadowSeen{$u->{name}};
my $hashedPassword = "!";
$hashedPassword = $u->{hashedPassword} if defined $u->{hashedPassword};
my $expires = "";
$expires = dateToDays($u->{expires}) if defined $u->{expires};
# FIXME: set correct value for sp_lstchg.
push @shadowNew, join(":", $u->{name}, $hashedPassword, "1::::", $expires, "") . "\n";
}
updateFile("/etc/shadow", \@shadowNew, 0640);
{
my $uid = getpwnam "root";
my $gid = getgrnam "shadow";
my $path = "/etc/shadow";
(chown($uid, $gid, $path) || die "Failed to change ownership of $path: $!") unless $is_dry;
}
# Rewrite /etc/subuid & /etc/subgid to include default container mappings
my $subUidMapFile = "/var/lib/nixos/auto-subuid-map";
my $subUidMap = -e $subUidMapFile ? decode_json(read_file($subUidMapFile)) : {};
my (%subUidsUsed, %subUidsPrevUsed);
$subUidsPrevUsed{$_} = 1 foreach values %{$subUidMap};
sub allocSubUid {
my ($name, @rest) = @_;
# TODO: No upper bounds?
my ($min, $max, $delta) = (100000, 100000 + 100 * 65536, 65536);
my $prevId = $subUidMap->{$name};
if (defined $prevId && !defined $subUidsUsed{$prevId}) {
$subUidsUsed{$prevId} = 1;
return $prevId;
}
return allocId(\%subUidsUsed, \%subUidsPrevUsed, $min, $max, $delta, sub { undef });
}
my @subGids;
my @subUids;
foreach my $u (values %usersOut) {
my $name = $u->{name};
foreach my $range (@{$u->{subUidRanges}}) {
my $value = join(":", ($name, $range->{startUid}, $range->{count}));
push @subUids, $value;
}
foreach my $range (@{$u->{subGidRanges}}) {
my $value = join(":", ($name, $range->{startGid}, $range->{count}));
push @subGids, $value;
}
if($u->{autoSubUidGidRange}) {
my $subordinate = allocSubUid($name);
if (defined $subUidMap->{$name} && $subUidMap->{$name} != $subordinate) {
print STDERR "warning: The subuids for '$name' changed, as they coincided with the subuids of a different user (see /etc/subuid). "
. "The range now starts with $subordinate instead of $subUidMap->{$name}. "
. "If the subuids were used (e.g. with rootless container managers like podman), please change the ownership of affected files accordingly. "
. "Alternatively, to keep the old overlapping ranges, add this to the system configuration: "
. "users.users.$name.subUidRanges = [{startUid = $subUidMap->{$name}; count = 65536;}]; "
. "users.users.$name.subGidRanges = [{startGid = $subUidMap->{$name}; count = 65536;}];\n";
}
$subUidMap->{$name} = $subordinate;
my $value = join(":", ($name, $subordinate, 65536));
push @subUids, $value;
push @subGids, $value;
}
}
updateFile("/etc/subuid", join("\n", @subUids) . "\n");
updateFile("/etc/subgid", join("\n", @subGids) . "\n");
updateFile($subUidMapFile, to_json($subUidMap) . "\n");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
{
config,
pkgs,
lib,
...
}:
let
vteInitSnippet = ''
# Show current working directory in VTE terminals window title.
# Supports both bash and zsh, requires interactive shell.
. ${pkgs.vte.override { gtkVersion = null; }}/etc/profile.d/vte.sh
'';
in
{
meta = {
maintainers = lib.teams.gnome.members;
};
options = {
programs.bash.vteIntegration = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Whether to enable Bash integration for VTE terminals.
This allows it to preserve the current directory of the shell
across terminals.
'';
};
programs.zsh.vteIntegration = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Whether to enable Zsh integration for VTE terminals.
This allows it to preserve the current directory of the shell
across terminals.
'';
};
};
config = lib.mkMerge [
(lib.mkIf config.programs.bash.vteIntegration {
programs.bash.interactiveShellInit = lib.mkBefore vteInitSnippet;
})
(lib.mkIf config.programs.zsh.vteIntegration {
programs.zsh.interactiveShellInit = vteInitSnippet;
})
];
}

View File

@@ -0,0 +1,24 @@
{ config, lib, ... }:
{
meta = {
maintainers = lib.teams.freedesktop.members;
};
options = {
xdg.autostart.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to install files to support the
[XDG Autostart specification](https://specifications.freedesktop.org/autostart-spec/latest).
'';
};
};
config = lib.mkIf config.xdg.autostart.enable {
environment.pathsToLink = [
"/etc/xdg/autostart"
];
};
}

View File

@@ -0,0 +1,69 @@
{
config,
lib,
pkgs,
...
}:
{
meta = {
maintainers = lib.teams.freedesktop.members;
};
options = {
xdg.icons.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to install files to support the
[XDG Icon Theme specification](https://specifications.freedesktop.org/icon-theme-spec/latest).
'';
};
xdg.icons.fallbackCursorThemes = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = ''
Names of the fallback cursor themes, in order of preference, to be used when no other icon source can be found.
Set to `[]` to disable the fallback entirely.
'';
};
};
config = lib.mkIf config.xdg.icons.enable {
environment.pathsToLink = [
"/share/icons"
"/share/pixmaps"
];
environment.systemPackages = [
# Empty icon theme that contains index.theme file describing directories
# where toolkits should look for icons installed by apps.
pkgs.hicolor-icon-theme
]
++ lib.optionals (config.xdg.icons.fallbackCursorThemes != [ ]) [
(pkgs.writeTextFile {
name = "fallback-cursor-theme";
text = ''
[Icon Theme]
Inherits=${lib.concatStringsSep "," config.xdg.icons.fallbackCursorThemes}
'';
destination = "/share/icons/default/index.theme";
})
];
# libXcursor looks for cursors in XCURSOR_PATH
# it mostly follows the spec for icons
# See: https://www.x.org/releases/current/doc/man/man3/Xcursor.3.xhtml Themes
# These are preferred so they come first in the list
environment.sessionVariables.XCURSOR_PATH = [
"$HOME/.icons"
"$HOME/.local/share/icons"
];
environment.profileRelativeSessionVariables.XCURSOR_PATH = [
"/share/icons"
"/share/pixmaps"
];
};
}

View File

@@ -0,0 +1,27 @@
{ config, lib, ... }:
{
meta = {
maintainers = lib.teams.freedesktop.members;
};
options = {
xdg.menus.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to install files to support the
[XDG Desktop Menu specification](https://specifications.freedesktop.org/menu-spec/latest).
'';
};
};
config = lib.mkIf config.xdg.menus.enable {
environment.pathsToLink = [
"/share/applications"
"/share/desktop-directories"
"/etc/xdg/menus"
"/etc/xdg/menus/applications-merged"
];
};
}

View File

@@ -0,0 +1,112 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.xdg.mime;
associationOptions =
with lib.types;
attrsOf (coercedTo (either (listOf str) str) (x: lib.concatStringsSep ";" (lib.toList x)) str);
in
{
meta = {
maintainers = lib.teams.freedesktop.members ++ (with lib.maintainers; [ figsoda ]);
};
options = {
xdg.mime.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to install files to support the
[XDG Shared MIME-info specification](https://specifications.freedesktop.org/shared-mime-info-spec/latest) and the
[XDG MIME Applications specification](https://specifications.freedesktop.org/mime-apps-spec/latest).
'';
};
xdg.mime.addedAssociations = lib.mkOption {
type = associationOptions;
default = { };
example = {
"application/pdf" = "firefox.desktop";
"text/xml" = [
"nvim.desktop"
"codium.desktop"
];
};
description = ''
Adds associations between mimetypes and applications. See the
[
specifications](https://specifications.freedesktop.org/mime-apps-spec/latest/associations) for more information.
'';
};
xdg.mime.defaultApplications = lib.mkOption {
type = associationOptions;
default = { };
example = {
"application/pdf" = "firefox.desktop";
"image/png" = [
"sxiv.desktop"
"gimp.desktop"
];
};
description = ''
Sets the default applications for given mimetypes. See the
[
specifications](https://specifications.freedesktop.org/mime-apps-spec/latest/default) for more information.
'';
};
xdg.mime.removedAssociations = lib.mkOption {
type = associationOptions;
default = { };
example = {
"audio/mp3" = [
"mpv.desktop"
"umpv.desktop"
];
"inode/directory" = "codium.desktop";
};
description = ''
Removes associations between mimetypes and applications. See the
[
specifications](https://specifications.freedesktop.org/mime-apps-spec/latest/associations) for more information.
'';
};
};
config = lib.mkIf cfg.enable {
environment.etc."xdg/mimeapps.list" =
lib.mkIf
(cfg.addedAssociations != { } || cfg.defaultApplications != { } || cfg.removedAssociations != { })
{
text = lib.generators.toINI { } {
"Added Associations" = cfg.addedAssociations;
"Default Applications" = cfg.defaultApplications;
"Removed Associations" = cfg.removedAssociations;
};
};
environment.pathsToLink = [ "/share/mime" ];
environment.systemPackages = [
# this package also installs some useful data, as well as its utilities
pkgs.shared-mime-info
];
environment.extraSetup = ''
if [ -w $out/share/mime ] && [ -d $out/share/mime/packages ]; then
XDG_DATA_DIRS=$out/share PKGSYSTEM_ENABLE_FSYNC=0 ${pkgs.buildPackages.shared-mime-info}/bin/update-mime-database -V $out/share/mime > /dev/null
fi
if [ -w $out/share/applications ]; then
${pkgs.buildPackages.desktop-file-utils}/bin/update-desktop-database $out/share/applications
fi
'';
};
}

View File

@@ -0,0 +1,167 @@
{
config,
pkgs,
lib,
...
}:
let
inherit (lib)
mkEnableOption
mkIf
mkOption
mkRenamedOptionModule
mkRemovedOptionModule
teams
types
;
associationOptions =
with types;
attrsOf (coercedTo (either (listOf str) str) (x: lib.concatStringsSep ";" (lib.toList x)) str);
in
{
imports = [
(mkRenamedOptionModule [ "services" "flatpak" "extraPortals" ] [ "xdg" "portal" "extraPortals" ])
(mkRemovedOptionModule [
"xdg"
"portal"
"gtkUsePortal"
] "This option has been removed due to being unsupported and discouraged by the GTK developers.")
];
meta = {
maintainers = teams.freedesktop.members;
};
options.xdg.portal = {
enable =
mkEnableOption ''[xdg desktop integration](https://github.com/flatpak/xdg-desktop-portal)''
// {
default = false;
};
extraPortals = mkOption {
type = types.listOf types.package;
default = [ ];
description = ''
List of additional portals to add to path. Portals allow interaction
with system, like choosing files or taking screenshots. At minimum,
a desktop portal implementation should be listed. GNOME and KDE already
adds `xdg-desktop-portal-gtk`; and
`xdg-desktop-portal-kde` respectively. On other desktop
environments you probably want to add them yourself.
'';
};
xdgOpenUsePortal = mkOption {
type = types.bool;
default = false;
description = ''
Sets environment variable `NIXOS_XDG_OPEN_USE_PORTAL` to `1`
This will make `xdg-open` use the portal to open programs, which resolves bugs involving
programs opening inside FHS envs or with unexpected env vars set from wrappers.
See [#160923](https://github.com/NixOS/nixpkgs/issues/160923) for more info.
'';
};
config = mkOption {
type = types.attrsOf associationOptions;
default = { };
example = {
x-cinnamon = {
default = [
"xapp"
"gtk"
];
};
pantheon = {
default = [
"pantheon"
"gtk"
];
"org.freedesktop.impl.portal.Secret" = [ "gnome-keyring" ];
};
common = {
default = [ "gtk" ];
};
};
description = ''
Sets which portal backend should be used to provide the implementation
for the requested interface. For details check {manpage}`portals.conf(5)`.
Configs will be linked to `/etc/xdg/xdg-desktop-portal/` with the name `$desktop-portals.conf`
for `xdg.portal.config.$desktop` and `portals.conf` for `xdg.portal.config.common`
as an exception.
'';
};
configPackages = mkOption {
type = types.listOf types.package;
default = [ ];
example = lib.literalExpression "[ pkgs.gnome-session ]";
description = ''
List of packages that provide XDG desktop portal configuration, usually in
the form of `share/xdg-desktop-portal/$desktop-portals.conf`.
Note that configs in `xdg.portal.config` will be preferred if set.
'';
};
};
config =
let
cfg = config.xdg.portal;
packages = [ pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
in
mkIf cfg.enable {
warnings = lib.optional (cfg.configPackages == [ ] && cfg.config == { }) ''
xdg-desktop-portal 1.17 reworked how portal implementations are loaded, you
should either set `xdg.portal.config` or `xdg.portal.configPackages`
to specify which portal backend to use for the requested interface.
https://github.com/flatpak/xdg-desktop-portal/blob/1.18.1/doc/portals.conf.rst.in
If you simply want to keep the behaviour in < 1.17, which uses the first
portal implementation found in lexicographical order, use the following:
xdg.portal.config.common.default = "*";
'';
assertions = [
{
assertion = cfg.extraPortals != [ ];
message = "Setting xdg.portal.enable to true requires a portal implementation in xdg.portal.extraPortals such as xdg-desktop-portal-gtk or xdg-desktop-portal-kde.";
}
];
services.dbus.packages = packages;
systemd.packages = packages;
environment = {
systemPackages = packages ++ cfg.configPackages;
pathsToLink = [
# Portal definitions and upstream desktop environment portal configurations.
"/share/xdg-desktop-portal"
# .desktop files to register fallback icon and app name.
"/share/applications"
];
sessionVariables = {
NIXOS_XDG_OPEN_USE_PORTAL = mkIf cfg.xdgOpenUsePortal "1";
NIX_XDG_DESKTOP_PORTAL_DIR = "/run/current-system/sw/share/xdg-desktop-portal/portals";
};
etc = lib.concatMapAttrs (
desktop: conf:
lib.optionalAttrs (conf != { }) {
"xdg/xdg-desktop-portal/${
lib.optionalString (desktop != "common") "${desktop}-"
}portals.conf".text =
lib.generators.toINI { } { preferred = conf; };
}
) cfg.config;
};
};
}

View File

@@ -0,0 +1,52 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.xdg.portal.lxqt;
in
{
meta = {
maintainers = lib.teams.lxqt.members;
};
options.xdg.portal.lxqt = {
enable = lib.mkEnableOption ''
the desktop portal for the LXQt desktop environment.
This will add the `lxqt.xdg-desktop-portal-lxqt`
package (with the extra Qt styles) into the
{option}`xdg.portal.extraPortals` option
'';
styles = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
example = lib.literalExpression ''
[
pkgs.libsForQt5.qtstyleplugin-kvantum
pkgs.breeze-qt5
pkgs.qtcurve
];
'';
description = ''
Extra Qt styles that will be available to the
`lxqt.xdg-desktop-portal-lxqt`.
'';
};
};
config = lib.mkIf cfg.enable {
xdg.portal = {
enable = true;
extraPortals = [
(pkgs.lxqt.xdg-desktop-portal-lxqt.override { extraQtStyles = cfg.styles; })
];
};
environment.systemPackages = cfg.styles;
};
}

View File

@@ -0,0 +1,69 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.xdg.portal.wlr;
package = pkgs.xdg-desktop-portal-wlr;
settingsFormat = pkgs.formats.ini { };
configFile = settingsFormat.generate "xdg-desktop-portal-wlr.ini" cfg.settings;
in
{
meta = {
maintainers = with lib.maintainers; [ minijackson ];
};
options.xdg.portal.wlr = {
enable = lib.mkEnableOption ''
desktop portal for wlroots-based desktops.
This will add the `xdg-desktop-portal-wlr` package into
the {option}`xdg.portal.extraPortals` option, and provide the
configuration file
'';
settings = lib.mkOption {
description = ''
Configuration for `xdg-desktop-portal-wlr`.
See {manpage}`xdg-desktop-portal-wlr(5)` for supported
values.
'';
type = lib.types.submodule {
freeformType = settingsFormat.type;
};
default = { };
# Example taken from the manpage
example = lib.literalExpression ''
{
screencast = {
output_name = "HDMI-A-1";
max_fps = 30;
exec_before = "disable_notifications.sh";
exec_after = "enable_notifications.sh";
chooser_type = "simple";
chooser_cmd = "''${pkgs.slurp}/bin/slurp -f %o -or";
};
}
'';
};
};
config = lib.mkIf cfg.enable {
xdg.portal = {
enable = true;
extraPortals = [ package ];
};
systemd.user.services.xdg-desktop-portal-wlr.serviceConfig.ExecStart = [
# Empty ExecStart value to override the field
""
"${package}/libexec/xdg-desktop-portal-wlr --config=${configFile}"
];
};
}

View File

@@ -0,0 +1,33 @@
{
config,
lib,
pkgs,
...
}:
{
meta = {
maintainers = lib.teams.freedesktop.members;
};
options = {
xdg.sounds.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to install files to support the
[XDG Sound Theme specification](https://www.freedesktop.org/wiki/Specifications/sound-theme-spec/).
'';
};
};
config = lib.mkIf config.xdg.sounds.enable {
environment.systemPackages = [
pkgs.sound-theme-freedesktop
];
environment.pathsToLink = [
"/share/sounds"
];
};
}

View File

@@ -0,0 +1,63 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.xdg.terminal-exec;
inherit (lib)
mkIf
mkEnableOption
mkOption
mkPackageOption
types
;
in
{
meta.maintainers = with lib.maintainers; [ Cryolitia ];
###### interface
options = {
xdg.terminal-exec = {
enable = mkEnableOption "xdg-terminal-exec, the [proposed](https://gitlab.freedesktop.org/xdg/xdg-specs/-/merge_requests/46) Default Terminal Execution Specification";
package = mkPackageOption pkgs "xdg-terminal-exec" { };
settings = mkOption {
type = with types; attrsOf (listOf str);
default = { };
description = ''
Configuration options for the Default Terminal Execution Specification.
The keys are the desktop environments that are matched (case-insensitively) against `$XDG_CURRENT_DESKTOP`,
or `default` which is used when the current desktop environment is not found in the configuration.
The values are a list of terminals' [desktop file IDs](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s02.html#desktop-file-id) to try in order of decreasing priority.
'';
example = {
default = [ "kitty.desktop" ];
GNOME = [
"com.raggesilver.BlackBox.desktop"
"org.gnome.Terminal.desktop"
];
};
};
};
};
###### implementation
config = mkIf cfg.enable {
environment = {
systemPackages = [ cfg.package ];
etc = lib.mapAttrs' (
desktop: terminals:
# map desktop name such as GNOME to `xdg/gnome-xdg-terminals.list`, default to `xdg/xdg-terminals.list`
lib.nameValuePair "xdg/${
if desktop == "default" then "" else "${lib.toLower desktop}-"
}xdg-terminals.list" { text = lib.concatLines terminals; }
) cfg.settings;
};
};
}

View File

@@ -0,0 +1,149 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.zramSwap;
devices = map (nr: "zram${toString nr}") (lib.range 0 (cfg.swapDevices - 1));
in
{
imports = [
(lib.mkRemovedOptionModule [
"zramSwap"
"numDevices"
] "Using ZRAM devices as general purpose ephemeral block devices is no longer supported")
];
###### interface
options = {
zramSwap = {
enable = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Enable in-memory compressed devices and swap space provided by the zram
kernel module.
See [
https://www.kernel.org/doc/Documentation/blockdev/zram.txt
](https://www.kernel.org/doc/Documentation/blockdev/zram.txt).
'';
};
swapDevices = lib.mkOption {
default = 1;
type = lib.types.int;
description = ''
Number of zram devices to be used as swap, recommended is 1.
'';
};
memoryPercent = lib.mkOption {
default = 50;
type = lib.types.ints.positive;
description = ''
Maximum total amount of memory that can be stored in the zram swap devices
(as a percentage of your total memory). Defaults to 1/2 of your total
RAM. Run `zramctl` to check how good memory is compressed.
This doesn't define how much memory will be used by the zram swap devices.
'';
};
memoryMax = lib.mkOption {
default = null;
type = with lib.types; nullOr int;
description = ''
Maximum total amount of memory (in bytes) that can be stored in the zram
swap devices. If set, the smaller one of this option and memoryPercent would
be used.
This doesn't define how much memory will be used by the zram swap devices.
'';
};
priority = lib.mkOption {
default = 5;
type = lib.types.int;
description = ''
Priority of the zram swap devices. It should be a number higher than
the priority of your disk-based swap devices (so that the system will
fill the zram swap devices before falling back to disk swap).
'';
};
algorithm = lib.mkOption {
default = "zstd";
example = "lz4";
type =
with lib.types;
either (enum [
"842"
"lzo"
"lzo-rle"
"lz4"
"lz4hc"
"zstd"
]) str;
description = ''
Compression algorithm. `lzo` has good compression,
but is slow. `lz4` has bad compression, but is fast.
`zstd` is both good compression and fast, but requires newer kernel.
You can check what other algorithms are supported by your zram device with
{command}`cat /sys/class/block/zram*/comp_algorithm`
'';
};
writebackDevice = lib.mkOption {
default = null;
example = "/dev/zvol/tarta-zoot/swap-writeback";
type = lib.types.nullOr lib.types.path;
description = ''
Write incompressible pages to this device,
as there's no gain from keeping them in RAM.
'';
};
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.writebackDevice == null || cfg.swapDevices <= 1;
message = "A single writeback device cannot be shared among multiple zram devices";
}
];
services.zram-generator.enable = true;
services.zram-generator.settings = lib.listToAttrs (
builtins.map (dev: {
name = dev;
value =
let
size = "${toString cfg.memoryPercent} / 100 * ram";
in
{
zram-size =
if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
compression-algorithm = cfg.algorithm;
swap-priority = cfg.priority;
}
// lib.optionalAttrs (cfg.writebackDevice != null) {
writeback-device = cfg.writebackDevice;
};
}) devices
);
};
}

View File

@@ -0,0 +1,28 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.acpilight;
in
{
options = {
hardware.acpilight = {
enable = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Enable acpilight.
This will allow brightness control via xbacklight from users in the video group
'';
};
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = with pkgs; [ acpilight ];
services.udev.packages = with pkgs; [ acpilight ];
};
}

View File

@@ -0,0 +1,104 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware;
in
{
imports = [
(lib.mkRenamedOptionModule
[ "networking" "enableRT73Firmware" ]
[ "hardware" "enableRedistributableFirmware" ]
)
(lib.mkRenamedOptionModule
[ "networking" "enableIntel3945ABGFirmware" ]
[ "hardware" "enableRedistributableFirmware" ]
)
(lib.mkRenamedOptionModule
[ "networking" "enableIntel2100BGFirmware" ]
[ "hardware" "enableRedistributableFirmware" ]
)
(lib.mkRenamedOptionModule
[ "networking" "enableRalinkFirmware" ]
[ "hardware" "enableRedistributableFirmware" ]
)
(lib.mkRenamedOptionModule
[ "networking" "enableRTL8192cFirmware" ]
[ "hardware" "enableRedistributableFirmware" ]
)
];
###### interface
options = {
hardware.enableAllFirmware = lib.mkEnableOption "all firmware regardless of license";
hardware.enableRedistributableFirmware =
lib.mkEnableOption "firmware with a license allowing redistribution"
// {
default = config.hardware.enableAllFirmware;
defaultText = lib.literalExpression "config.hardware.enableAllFirmware";
};
hardware.wirelessRegulatoryDatabase =
lib.mkEnableOption "loading the wireless regulatory database at boot"
// {
default = cfg.enableRedistributableFirmware || cfg.enableAllFirmware;
defaultText = lib.literalMD "Enabled if proprietary firmware is allowed via {option}`enableRedistributableFirmware` or {option}`enableAllFirmware`.";
};
};
###### implementation
config = lib.mkMerge [
(lib.mkIf (cfg.enableAllFirmware || cfg.enableRedistributableFirmware) {
hardware.firmware =
with pkgs;
[
linux-firmware
intel2200BGFirmware
rtl8192su-firmware
rt5677-firmware
rtl8761b-firmware
zd1211fw
alsa-firmware
sof-firmware
libreelec-dvb-firmware
]
++ lib.optional pkgs.stdenv.hostPlatform.isAarch raspberrypiWirelessFirmware;
})
(lib.mkIf cfg.enableAllFirmware {
assertions = [
{
assertion = !cfg.enableAllFirmware || pkgs.config.allowUnfree;
message = ''
the list of hardware.enableAllFirmware contains non-redistributable licensed firmware files.
This requires nixpkgs.config.allowUnfree to be true.
An alternative is to use the hardware.enableRedistributableFirmware option.
'';
}
];
hardware.firmware =
with pkgs;
[
broadcom-bt-firmware
b43Firmware_5_1_138
b43Firmware_6_30_163_46
xow_dongle-firmware
]
++ lib.optionals pkgs.stdenv.hostPlatform.isx86 [
facetimehd-calibration
facetimehd-firmware
];
})
(lib.mkIf cfg.wirelessRegulatoryDatabase {
hardware.firmware = [ pkgs.wireless-regdb ];
})
];
}

View File

@@ -0,0 +1,173 @@
# This module enables all hardware supported by NixOS: i.e., all
# firmware is included, and all devices from which one may boot are
# enabled in the initrd. Its primary use is in the NixOS installation
# CDs.
{
config,
lib,
pkgs,
...
}:
let
platform = pkgs.stdenv.hostPlatform;
in
{
options = {
hardware.enableAllHardware = lib.mkEnableOption "Enable support for most hardware";
};
config = lib.mkIf config.hardware.enableAllHardware {
# The initrd has to contain any module that might be necessary for
# supporting the most important parts of HW like drives.
boot.initrd.availableKernelModules = [
# SATA/PATA support.
"ahci"
"ata_piix"
"sata_inic162x"
"sata_nv"
"sata_promise"
"sata_qstor"
"sata_sil"
"sata_sil24"
"sata_sis"
"sata_svw"
"sata_sx4"
"sata_uli"
"sata_via"
"sata_vsc"
"pata_ali"
"pata_amd"
"pata_artop"
"pata_atiixp"
"pata_efar"
"pata_hpt366"
"pata_hpt37x"
"pata_hpt3x2n"
"pata_hpt3x3"
"pata_it8213"
"pata_it821x"
"pata_jmicron"
"pata_marvell"
"pata_mpiix"
"pata_netcell"
"pata_ns87410"
"pata_oldpiix"
"pata_pcmcia"
"pata_pdc2027x"
"pata_qdi"
"pata_rz1000"
"pata_serverworks"
"pata_sil680"
"pata_sis"
"pata_sl82c105"
"pata_triflex"
"pata_via"
"pata_winbond"
# SCSI support (incomplete).
"3w-9xxx"
"3w-xxxx"
"aic79xx"
"aic7xxx"
"arcmsr"
"hpsa"
# USB support, especially for booting from USB CD-ROM
# drives.
"uas"
# SD cards.
"sdhci_pci"
# NVMe drives
"nvme"
# Firewire support. Not tested.
"ohci1394"
"sbp2"
# Virtio (QEMU, KVM etc.) support.
"virtio_net"
"virtio_pci"
"virtio_mmio"
"virtio_blk"
"virtio_scsi"
"virtio_balloon"
"virtio_console"
# VMware support.
"mptspi"
"vmxnet3"
"vsock"
]
++ lib.optional platform.isx86 "vmw_balloon"
++ lib.optionals (pkgs.stdenv.hostPlatform.isi686 || pkgs.stdenv.hostPlatform.isx86_64) [
"vmw_vmci"
"vmwgfx"
"vmw_vsock_vmci_transport"
# Hyper-V support.
"hv_storvsc"
]
++ lib.optionals pkgs.stdenv.hostPlatform.isAarch [
# Allwinner support
# Required for early KMS
"sun4i-drm"
"sun8i-mixer" # Audio, but required for kms
# PWM for the backlight
"pwm-sun4i"
# Broadcom
"vc4"
]
++ lib.optionals pkgs.stdenv.hostPlatform.isAarch64 [
# Most of the following falls into two categories:
# - early KMS / early display
# - early storage (e.g. USB) support
# Broadcom
"pcie-brcmstb"
# Rockchip
"dw-hdmi"
"dw-mipi-dsi"
"rockchipdrm"
"rockchip-rga"
"phy-rockchip-pcie"
"pcie-rockchip-host"
# Misc. uncategorized hardware
# Used for some platform's integrated displays
"panel-simple"
"pwm-bl"
# Power supply drivers, some platforms need them for USB
"axp20x-ac-power"
"axp20x-battery"
"pinctrl-axp209"
"mp8859"
# USB drivers
"xhci-pci-renesas"
# Reset controllers
"reset-raspberrypi" # Triggers USB chip firmware load.
# Misc "weak" dependencies
"analogix-dp"
"analogix-anx6345" # For DP or eDP (e.g. integrated display)
];
# Include lots of firmware.
hardware.enableRedistributableFirmware = true;
};
}

View File

@@ -0,0 +1,43 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.apple.touchBar;
format = pkgs.formats.toml { };
cfgFile = format.generate "config.toml" cfg.settings;
in
{
options.hardware.apple.touchBar = {
enable = lib.mkEnableOption "support for the Touch Bar on some Apple laptops using tiny-dfr";
package = lib.mkPackageOption pkgs "tiny-dfr" { };
settings = lib.mkOption {
type = format.type;
default = { };
description = ''
Configuration for tiny-dfr. See [example configuration][1] for available options.
[1]: https://github.com/WhatAmISupposedToPutHere/tiny-dfr/blob/master/share/tiny-dfr/config.toml
'';
example = lib.literalExpression ''
{
MediaLayerDefault = true;
ShowButtonOutlines = false;
EnablePixelShift = true;
}
'';
};
};
config = lib.mkIf cfg.enable {
systemd.packages = [ cfg.package ];
services.udev.packages = [ cfg.package ];
environment.etc."tiny-dfr/config.toml".source = cfgFile;
systemd.services.tiny-dfr.restartTriggers = [ cfgFile ];
};
}

View File

@@ -0,0 +1,30 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.bladeRF;
in
{
options.hardware.bladeRF = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enables udev rules for BladeRF devices. By default grants access
to users in the "bladerf" group. You may want to install the
libbladeRF package.
'';
};
};
config = lib.mkIf cfg.enable {
services.udev.packages = [ pkgs.libbladeRF ];
users.groups.bladerf = { };
};
}

View File

@@ -0,0 +1,24 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.brillo;
in
{
options = {
hardware.brillo = {
enable = lib.mkEnableOption ''
brillo in userspace.
This will allow brightness control from users in the video group
'';
};
};
config = lib.mkIf cfg.enable {
services.udev.packages = [ pkgs.brillo ];
environment.systemPackages = [ pkgs.brillo ];
};
}

View File

@@ -0,0 +1,50 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.ckb-next;
in
{
imports = [
(lib.mkRenamedOptionModule [ "hardware" "ckb" "enable" ] [ "hardware" "ckb-next" "enable" ])
(lib.mkRenamedOptionModule [ "hardware" "ckb" "package" ] [ "hardware" "ckb-next" "package" ])
];
options.hardware.ckb-next = {
enable = lib.mkEnableOption "the Corsair keyboard/mouse driver";
gid = lib.mkOption {
type = lib.types.nullOr lib.types.int;
default = null;
example = 100;
description = ''
Limit access to the ckb daemon to a particular group.
'';
};
package = lib.mkPackageOption pkgs "ckb-next" { };
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
systemd.services.ckb-next = {
description = "Corsair Keyboards and Mice Daemon";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/ckb-next-daemon ${
lib.optionalString (cfg.gid != null) "--gid=${builtins.toString cfg.gid}"
}";
Restart = "on-failure";
};
};
};
meta = {
maintainers = [ ];
};
}

View File

@@ -0,0 +1,38 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
mkEnableOption
mkIf
mkMerge
;
cfg = config.hardware.coral;
in
{
options.hardware.coral = {
usb.enable = mkEnableOption "Coral USB support";
pcie.enable = mkEnableOption "Coral PCIe support";
};
config = mkMerge [
(mkIf (cfg.usb.enable || cfg.pcie.enable) {
users.groups.coral = { };
})
(mkIf cfg.usb.enable {
services.udev.packages = with pkgs; [ libedgetpu ];
})
(mkIf cfg.pcie.enable {
boot.extraModulePackages = with config.boot.kernelPackages; [ gasket ];
services.udev.extraRules = ''
SUBSYSTEM=="apex",MODE="0660",GROUP="coral"
'';
})
];
}

View File

@@ -0,0 +1,63 @@
{
config,
pkgs,
lib,
...
}:
let
inherit (lib)
mkEnableOption
mkIf
mkPackageOption
;
cfg = config.programs.corectrl;
in
{
imports = [
(lib.mkRenamedOptionModule
[ "programs" "corectrl" "gpuOverclock" "enable" ]
[ "hardware" "amdgpu" "overdrive" "enable" ]
)
(lib.mkRenamedOptionModule
[ "programs" "corectrl" "gpuOverclock" "ppfeaturemask" ]
[ "hardware" "amdgpu" "overdrive" "ppfeaturemask" ]
)
];
options.programs.corectrl = {
enable = mkEnableOption ''
CoreCtrl, a tool to overclock amd graphics cards and processors.
Add your user to the corectrl group to run corectrl without needing to enter your password
'';
package = mkPackageOption pkgs "corectrl" {
extraDescription = "Useful for overriding the configuration options used for the package.";
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
services.dbus.packages = [ cfg.package ];
users.groups.corectrl = { };
security.polkit.extraConfig = ''
polkit.addRule(function(action, subject) {
if ((action.id == "org.corectrl.helper.init" ||
action.id == "org.corectrl.helperkiller.init") &&
subject.local == true &&
subject.active == true &&
subject.isInGroup("corectrl")) {
return polkit.Result.YES;
}
});
'';
};
meta.maintainers = with lib.maintainers; [
artturin
Scrumplex
];
}

View File

@@ -0,0 +1,32 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.cpu.amd;
in
{
###### interface
options = {
hardware.cpu.amd.updateMicrocode = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Update the CPU microcode for AMD processors.
'';
};
hardware.cpu.amd.microcodePackage = lib.mkPackageOption pkgs "microcode-amd" { };
};
###### implementation
config = lib.mkIf config.hardware.cpu.amd.updateMicrocode {
# Microcode updates must be the first item prepended in the initrd
boot.initrd.prepend = lib.mkOrder 1 [ "${cfg.microcodePackage}/amd-ucode.img" ];
};
}

View File

@@ -0,0 +1,30 @@
{
config,
lib,
...
}:
let
inherit (lib) mkEnableOption mkIf;
cfg = config.hardware.cpu.amd.ryzen-smu;
ryzen-smu = config.boot.kernelPackages.ryzen-smu;
in
{
options.hardware.cpu.amd.ryzen-smu = {
enable = mkEnableOption ''
ryzen_smu, a linux kernel driver that exposes access to the SMU (System Management Unit) for certain AMD Ryzen Processors.
WARNING: Damage cause by use of your AMD processor outside of official AMD specifications or outside of factory settings are not covered under any AMD product warranty and may not be covered by your board or system manufacturer's warranty
'';
};
config = mkIf cfg.enable {
boot.kernelModules = [ "ryzen-smu" ];
boot.extraModulePackages = [ ryzen-smu ];
environment.systemPackages = [ ryzen-smu ];
};
meta.maintainers = with lib.maintainers; [
Cryolitia
phdyellow
];
}

View File

@@ -0,0 +1,91 @@
{
config,
options,
lib,
...
}:
let
cfgSev = config.hardware.cpu.amd.sev;
cfgSevGuest = config.hardware.cpu.amd.sevGuest;
optionsFor = device: group: {
enable = lib.mkEnableOption "access to the AMD ${device} device";
user = lib.mkOption {
description = "Owner to assign to the ${device} device.";
type = lib.types.str;
default = "root";
};
group = lib.mkOption {
description = "Group to assign to the ${device} device.";
type = lib.types.str;
default = group;
};
mode = lib.mkOption {
description = "Mode to set for the ${device} device.";
type = lib.types.str;
default = "0660";
};
};
in
with lib;
{
options.hardware.cpu.amd.sev = optionsFor "SEV" "sev";
options.hardware.cpu.amd.sevGuest = optionsFor "SEV guest" "sev-guest";
config = lib.mkMerge [
# /dev/sev
(lib.mkIf cfgSev.enable {
assertions = [
{
assertion = lib.hasAttr cfgSev.user config.users.users;
message = "Given user does not exist";
}
{
assertion =
(cfgSev.group == options.hardware.cpu.amd.sev.group.default)
|| (lib.hasAttr cfgSev.group config.users.groups);
message = "Given group does not exist";
}
];
boot.extraModprobeConfig = ''
options kvm_amd sev=1
'';
users.groups = lib.optionalAttrs (cfgSev.group == options.hardware.cpu.amd.sev.group.default) {
"${cfgSev.group}" = { };
};
services.udev.extraRules = with cfgSev; ''
KERNEL=="sev", OWNER="${user}", GROUP="${group}", MODE="${mode}"
'';
})
# /dev/sev-guest
(lib.mkIf cfgSevGuest.enable {
assertions = [
{
assertion = lib.hasAttr cfgSevGuest.user config.users.users;
message = "Given user does not exist";
}
{
assertion =
(cfgSevGuest.group == options.hardware.cpu.amd.sevGuest.group.default)
|| (lib.hasAttr cfgSevGuest.group config.users.groups);
message = "Given group does not exist";
}
];
users.groups =
lib.optionalAttrs (cfgSevGuest.group == options.hardware.cpu.amd.sevGuest.group.default)
{
"${cfgSevGuest.group}" = { };
};
services.udev.extraRules = with cfgSevGuest; ''
KERNEL=="sev-guest", OWNER="${user}", GROUP="${group}", MODE="${mode}"
'';
})
];
}

View File

@@ -0,0 +1,27 @@
{
config,
lib,
pkgs,
...
}:
{
###### interface
options = {
hardware.cpu.intel.updateMicrocode = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Update the CPU microcode for Intel processors.
'';
};
};
###### implementation
config = lib.mkIf config.hardware.cpu.intel.updateMicrocode {
# Microcode updates must be the first item prepended in the initrd
boot.initrd.prepend = lib.mkOrder 1 [ "${pkgs.microcode-intel}/intel-ucode.img" ];
};
}

View File

@@ -0,0 +1,69 @@
{ config, lib, ... }:
let
cfg = config.hardware.cpu.intel.sgx;
defaultPrvGroup = "sgx_prv";
in
{
options.hardware.cpu.intel.sgx.enableDcapCompat = lib.mkOption {
description = ''
Whether to enable backward compatibility for SGX software build for the
out-of-tree Intel SGX DCAP driver.
Creates symbolic links for the SGX devices `/dev/sgx_enclave`
and `/dev/sgx_provision` to make them available as
`/dev/sgx/enclave` and `/dev/sgx/provision`,
respectively.
'';
type = lib.types.bool;
default = true;
};
options.hardware.cpu.intel.sgx.provision = {
enable = lib.mkEnableOption "access to the Intel SGX provisioning device";
user = lib.mkOption {
description = "Owner to assign to the SGX provisioning device.";
type = lib.types.str;
default = "root";
};
group = lib.mkOption {
description = "Group to assign to the SGX provisioning device.";
type = lib.types.str;
default = defaultPrvGroup;
};
mode = lib.mkOption {
description = "Mode to set for the SGX provisioning device.";
type = lib.types.str;
default = "0660";
};
};
config = lib.mkMerge [
(lib.mkIf cfg.provision.enable {
assertions = [
{
assertion = lib.hasAttr cfg.provision.user config.users.users;
message = "Given user does not exist";
}
{
assertion =
(cfg.provision.group == defaultPrvGroup) || (lib.hasAttr cfg.provision.group config.users.groups);
message = "Given group does not exist";
}
];
users.groups = lib.optionalAttrs (cfg.provision.group == defaultPrvGroup) {
"${cfg.provision.group}" = { };
};
services.udev.extraRules = with cfg.provision; ''
SUBSYSTEM=="misc", KERNEL=="sgx_provision", OWNER="${user}", GROUP="${group}", MODE="${mode}"
'';
})
(lib.mkIf cfg.enableDcapCompat {
services.udev.extraRules = ''
SUBSYSTEM=="misc", KERNEL=="sgx_enclave", SYMLINK+="sgx/enclave"
SUBSYSTEM=="misc", KERNEL=="sgx_provision", SYMLINK+="sgx/provision"
'';
})
];
}

View File

@@ -0,0 +1,113 @@
{
lib,
config,
options,
...
}:
let
inherit (builtins) hasAttr;
inherit (lib) mkIf;
cfg = config.hardware.cpu.x86.msr;
opt = options.hardware.cpu.x86.msr;
defaultGroup = "msr";
isDefaultGroup = cfg.group == defaultGroup;
set = "to set for devices of the `msr` kernel subsystem.";
# Generates `foo=bar` parameters to pass to the kernel.
# If `module = baz` is passed, generates `baz.foo=bar`.
# Adds double quotes on demand to handle `foo="bar baz"`.
kernelParam =
{
module ? null,
}:
name: value:
assert lib.asserts.assertMsg (
!lib.strings.hasInfix "=" name
) "kernel parameter cannot have '=' in name";
let
key = (if module == null then "" else module + ".") + name;
valueString = lib.generators.mkValueStringDefault { } value;
quotedValueString =
if lib.strings.hasInfix " " valueString then
lib.strings.escape [ "\"" ] valueString
else
valueString;
in
"${key}=${quotedValueString}";
msrKernelParam = kernelParam { module = "msr"; };
in
{
options.hardware.cpu.x86.msr =
with lib.options;
with lib.types;
{
enable = mkEnableOption "the `msr` (Model-Specific Registers) kernel module and configure `udev` rules for its devices (usually `/dev/cpu/*/msr`)";
owner = mkOption {
type = str;
default = "root";
example = "nobody";
description = "Owner ${set}";
};
group = mkOption {
type = str;
default = defaultGroup;
example = "nobody";
description = "Group ${set}";
};
mode = mkOption {
type = str;
default = "0640";
example = "0660";
description = "Mode ${set}";
};
settings = mkOption {
type = submodule {
freeformType = attrsOf (oneOf [
bool
int
str
]);
options.allow-writes = mkOption {
type = nullOr (enum [
"on"
"off"
]);
default = null;
description = "Whether to allow writes to MSRs (`\"on\"`) or not (`\"off\"`).";
};
};
default = { };
description = "Parameters for the `msr` kernel module.";
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = hasAttr cfg.owner config.users.users;
message = "Owner '${cfg.owner}' set in `${opt.owner}` is not configured via `${options.users.users}.\"${cfg.owner}\"`.";
}
{
assertion = isDefaultGroup || (hasAttr cfg.group config.users.groups);
message = "Group '${cfg.group}' set in `${opt.group}` is not configured via `${options.users.groups}.\"${cfg.group}\"`.";
}
];
boot = {
kernelModules = [ "msr" ];
kernelParams = lib.attrsets.mapAttrsToList msrKernelParam (
lib.attrsets.filterAttrs (_: value: value != null) cfg.settings
);
};
users.groups.${cfg.group} = mkIf isDefaultGroup { };
services.udev.extraRules = ''
SUBSYSTEM=="msr", OWNER="${cfg.owner}", GROUP="${cfg.group}", MODE="${cfg.mode}"
'';
};
meta = with lib; {
maintainers = with maintainers; [ lorenzleutgeb ];
};
}

View File

@@ -0,0 +1,26 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.decklink;
kernelPackages = config.boot.kernelPackages;
in
{
options.hardware.decklink.enable = lib.mkEnableOption "hardware support for the Blackmagic Design Decklink audio/video interfaces";
config = lib.mkIf cfg.enable {
# snd_blackmagic-io can cause issues with pipewire,
# so we do not enable it by default
boot.kernelModules = [
"blackmagic"
"blackmagic-io"
];
boot.extraModulePackages = [ kernelPackages.decklink ];
systemd.packages = [ pkgs.blackmagic-desktop-video ];
systemd.services.DesktopVideoHelper.wantedBy = [ "multi-user.target" ];
};
}

View File

@@ -0,0 +1,252 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.deviceTree;
overlayType = lib.types.submodule {
options = {
name = lib.mkOption {
type = lib.types.str;
description = ''
Name of this overlay
'';
};
filter = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "*rpi*.dtb";
description = ''
Only apply to .dtb files matching glob expression.
'';
};
dtsFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
description = ''
Path to .dts overlay file, overlay is applied to
each .dtb file matching "compatible" of the overlay.
'';
default = null;
example = lib.literalExpression "./dts/overlays.dts";
};
dtsText = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Literal DTS contents, overlay is applied to
each .dtb file matching "compatible" of the overlay.
'';
example = ''
/dts-v1/;
/plugin/;
/ {
compatible = "raspberrypi";
};
&{/soc} {
pps {
compatible = "pps-gpio";
status = "okay";
};
};
'';
};
dtboFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
Path to .dtbo compiled overlay file.
'';
};
};
};
filterDTBs =
src:
if cfg.filter == null then
src
else
pkgs.runCommand "dtbs-filtered" { } ''
mkdir -p $out
cd ${src}
find . -type f -name '${cfg.filter}' -print0 \
| xargs -0 cp -v --no-preserve=mode --target-directory $out --parents
'';
filteredDTBs = filterDTBs cfg.dtbSource;
# Fill in `dtboFile` for each overlay if not set already.
# Existence of one of these is guarded by assertion below
withDTBOs =
xs:
lib.flip map xs (
o:
o
// {
dtboFile =
let
includePaths = [
"${lib.getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes"
]
++ cfg.dtboBuildExtraIncludePaths;
extraPreprocessorFlags = cfg.dtboBuildExtraPreprocessorFlags;
in
if o.dtboFile == null then
let
dtsFile = if o.dtsFile == null then (pkgs.writeText "dts" o.dtsText) else o.dtsFile;
in
pkgs.deviceTree.compileDTS {
name = "${o.name}-dtbo";
inherit includePaths extraPreprocessorFlags dtsFile;
}
else
o.dtboFile;
}
);
in
{
imports = [
(lib.mkRemovedOptionModule [
"hardware"
"deviceTree"
"base"
] "Use hardware.deviceTree.kernelPackage instead")
];
options = {
hardware.deviceTree = {
enable = lib.mkOption {
default = pkgs.stdenv.hostPlatform.linux-kernel.DTB or false;
type = lib.types.bool;
description = ''
Build device tree files. These are used to describe the
non-discoverable hardware of a system.
'';
};
kernelPackage = lib.mkOption {
default = config.boot.kernelPackages.kernel;
defaultText = lib.literalExpression "config.boot.kernelPackages.kernel";
example = lib.literalExpression "pkgs.linux_latest";
type = lib.types.path;
description = ''
Kernel package where device tree include directory is from. Also used as default source of dtb package to apply overlays to
'';
};
dtboBuildExtraPreprocessorFlags = lib.mkOption {
default = [ ];
example = lib.literalExpression "[ \"-DMY_DTB_DEFINE\" ]";
type = lib.types.listOf lib.types.str;
description = ''
Additional flags to pass to the preprocessor during dtbo compilations
'';
};
dtboBuildExtraIncludePaths = lib.mkOption {
default = [ ];
example = lib.literalExpression ''
[
./my_custom_include_dir_1
./custom_include_dir_2
]
'';
type = lib.types.listOf lib.types.path;
description = ''
Additional include paths that will be passed to the preprocessor when creating the final .dts to compile into .dtbo
'';
};
dtbSource = lib.mkOption {
default = "${cfg.kernelPackage}/dtbs";
defaultText = lib.literalExpression "\${cfg.kernelPackage}/dtbs";
type = lib.types.path;
description = ''
Path to dtb directory that overlays and other processing will be applied to. Uses
device trees bundled with the Linux kernel by default.
'';
};
name = lib.mkOption {
default = null;
example = "some-dtb.dtb";
type = lib.types.nullOr lib.types.str;
description = ''
The name of an explicit dtb to be loaded, relative to the dtb base.
Useful in extlinux scenarios if the bootloader doesn't pick the
right .dtb file from FDTDIR.
'';
};
filter = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "*rpi*.dtb";
description = ''
Only include .dtb files matching glob expression.
'';
};
overlays = lib.mkOption {
default = [ ];
example = lib.literalExpression ''
[
{ name = "pps"; dtsFile = ./dts/pps.dts; }
{ name = "spi";
dtsText = "...";
}
{ name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
]
'';
type = lib.types.listOf (
lib.types.coercedTo lib.types.path (path: {
name = baseNameOf path;
filter = null;
dtboFile = path;
}) overlayType
);
description = ''
List of overlays to apply to base device-tree (.dtb) files.
'';
};
package = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.path;
internal = true;
description = ''
A path containing the result of applying `overlays` to `kernelPackage`.
'';
};
};
};
config = lib.mkIf (cfg.enable) {
assertions =
let
invalidOverlay = o: (o.dtsFile == null) && (o.dtsText == null) && (o.dtboFile == null);
in
lib.singleton {
assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
message = ''
deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
Offending overlay(s):
${toString (map (o: o.name) (builtins.filter invalidOverlay cfg.overlays))}
'';
};
hardware.deviceTree.package =
if (cfg.overlays != [ ]) then
pkgs.deviceTree.applyOverlays filteredDTBs (withDTBOs cfg.overlays)
else
filteredDTBs;
};
}

View File

@@ -0,0 +1,31 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.digitalbitbox;
in
{
options.hardware.digitalbitbox = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enables udev rules for Digital Bitbox devices.
'';
};
package = lib.mkPackageOption pkgs "digitalbitbox" {
extraDescription = ''
This can be used to install a package with udev rules that differ from the defaults.
'';
};
};
config = lib.mkIf cfg.enable {
services.udev.packages = [ cfg.package ];
};
}

View File

@@ -0,0 +1,20 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.flipperzero;
in
{
options.hardware.flipperzero.enable = lib.mkEnableOption "udev rules and software for Flipper Zero devices";
config = lib.mkIf cfg.enable {
environment.systemPackages = [ pkgs.qFlipper ];
services.udev.packages = [ pkgs.qFlipper ];
};
}

View File

@@ -0,0 +1,17 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.flirc;
in
{
options.hardware.flirc.enable = lib.mkEnableOption "software to configure a Flirc USB device";
config = lib.mkIf cfg.enable {
environment.systemPackages = [ pkgs.flirc ];
services.udev.packages = [ pkgs.flirc ];
};
}

View File

@@ -0,0 +1,130 @@
{
config,
lib,
pkgs,
...
}:
let
configFormat = pkgs.formats.json { };
cfg = config.hardware.fw-fanctrl;
in
{
options.hardware.fw-fanctrl = {
enable = lib.mkEnableOption "the fw-fanctrl systemd service and install the needed packages";
package = lib.mkPackageOption pkgs "fw-fanctrl" { };
ectoolPackage = lib.mkPackageOption pkgs "fw-ectool" { };
disableBatteryTempCheck = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Disable checking battery temperature sensor
'';
};
config = lib.mkOption {
default = { };
description = ''
Additional config entries for the fw-fanctrl service (documentation: <https://github.com/TamtamHero/fw-fanctrl/blob/main/doc/configuration.md>)
'';
type = lib.types.submodule {
freeformType = lib.types.attrsOf configFormat.type;
options = {
defaultStrategy = lib.mkOption {
type = lib.types.str;
default = "lazy";
description = "Default strategy to use";
};
strategyOnDischarging = lib.mkOption {
type = lib.types.str;
default = "";
description = "Default strategy on discharging";
};
strategies = lib.mkOption {
default = { };
description = ''
Additional strategies which can be used by fw-fanctrl
'';
type = lib.types.attrsOf (
lib.types.submodule {
options = {
fanSpeedUpdateFrequency = lib.mkOption {
type = lib.types.ints.unsigned;
default = 5;
description = "How often the fan speed should be updated in seconds";
};
movingAverageInterval = lib.mkOption {
type = lib.types.ints.unsigned;
default = 25;
description = "Interval (seconds) of the last temperatures to use to calculate the average temperature";
};
speedCurve = lib.mkOption {
default = [ ];
description = "How should the speed curve look like";
type = lib.types.listOf (
lib.types.submodule {
options = {
temp = lib.mkOption {
type = lib.types.int;
default = 0;
description = "Temperature in °C at which the fan speed should be changed";
};
speed = lib.mkOption {
type = lib.types.ints.between 0 100;
default = 0;
description = "Percent how fast the fan should run at";
};
};
}
);
};
};
}
);
};
};
};
};
};
config =
let
defaultConfig = builtins.fromJSON (builtins.readFile "${cfg.package}/share/fw-fanctrl/config.json");
finalConfig = lib.attrsets.recursiveUpdate defaultConfig cfg.config;
configFile = configFormat.generate "custom.json" finalConfig;
in
lib.mkIf cfg.enable {
environment.systemPackages = [
cfg.package
cfg.ectoolPackage
];
systemd.services.fw-fanctrl = {
description = "Framework Fan Controller";
after = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
Restart = "always";
ExecStart = "${lib.getExe cfg.package} --output-format JSON run --config ${configFile} --silent ${lib.optionalString cfg.disableBatteryTempCheck "--no-battery-sensors"}";
ExecStopPost = "${lib.getExe cfg.ectoolPackage} autofanctrl";
};
wantedBy = [ "multi-user.target" ];
};
# Create suspend config
environment.etc."systemd/system-sleep/fw-fanctrl-suspend.sh".source =
"${cfg.package}/share/fw-fanctrl/fw-fanctrl-suspend";
};
meta = {
maintainers = [ lib.maintainers.Svenum ];
};
}

View File

@@ -0,0 +1,28 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.glasgow;
in
{
options.hardware.glasgow = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enables Glasgow udev rules and ensures 'plugdev' group exists.
This is a prerequisite to using Glasgow without being root.
'';
};
};
config = lib.mkIf cfg.enable {
services.udev.packages = [ pkgs.glasgow ];
users.groups.plugdev = { };
};
}

View File

@@ -0,0 +1,41 @@
{
config,
lib,
pkgs,
...
}:
let
# gnupg's manual describes how to setup ccid udev rules:
# https://www.gnupg.org/howtos/card-howto/en/ch02s03.html
# gnupg folks advised me (https://dev.gnupg.org/T5409) to look at debian's rules:
# https://salsa.debian.org/debian/gnupg2/-/blob/debian/main/debian/scdaemon.udev
# the latest rev of the entire debian gnupg2 repo as of 2021-04-28
# the scdaemon.udev file was last committed on 2021-01-05 (7817a03):
scdaemonUdevRev = "01898735a015541e3ffb43c7245ac1e612f40836";
scdaemonRules = pkgs.fetchurl {
url = "https://salsa.debian.org/debian/gnupg2/-/raw/${scdaemonUdevRev}/debian/scdaemon.udev";
sha256 = "08v0vp6950bz7galvc92zdss89y9vcwbinmbfcdldy8x72w6rqr3";
};
# per debian's udev deb hook (https://man7.org/linux/man-pages/man1/dh_installudev.1.html)
destination = "60-scdaemon.rules";
scdaemonUdevRulesPkg = pkgs.runCommand "scdaemon-udev-rules" { } ''
loc="$out/lib/udev/rules.d/"
mkdir -p "''${loc}"
cp "${scdaemonRules}" "''${loc}/${destination}"
'';
cfg = config.hardware.gpgSmartcards;
in
{
options.hardware.gpgSmartcards = {
enable = lib.mkEnableOption "udev rules for gnupg smart cards";
};
config = lib.mkIf cfg.enable {
services.udev.packages = [ scdaemonUdevRulesPkg ];
};
}

View File

@@ -0,0 +1,147 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.graphics;
driversEnv = pkgs.buildEnv {
name = "graphics-drivers";
paths = [ cfg.package ] ++ cfg.extraPackages;
};
driversEnv32 = pkgs.buildEnv {
name = "graphics-drivers-32bit";
paths = [ cfg.package32 ] ++ cfg.extraPackages32;
};
in
{
imports = [
(lib.mkRenamedOptionModule
[ "services" "xserver" "vaapiDrivers" ]
[ "hardware" "graphics" "extraPackages" ]
)
(lib.mkRemovedOptionModule [
"hardware"
"opengl"
"s3tcSupport"
] "S3TC support is now always enabled in Mesa.")
(lib.mkRemovedOptionModule [ "hardware" "opengl" "driSupport" ] "The setting can be removed.")
(lib.mkRenamedOptionModule [ "hardware" "opengl" "enable" ] [ "hardware" "graphics" "enable" ])
(lib.mkRenamedOptionModule
[ "hardware" "opengl" "driSupport32Bit" ]
[ "hardware" "graphics" "enable32Bit" ]
)
(lib.mkRenamedOptionModule [ "hardware" "opengl" "package" ] [ "hardware" "graphics" "package" ])
(lib.mkRenamedOptionModule
[ "hardware" "opengl" "package32" ]
[ "hardware" "graphics" "package32" ]
)
(lib.mkRenamedOptionModule
[ "hardware" "opengl" "extraPackages" ]
[ "hardware" "graphics" "extraPackages" ]
)
(lib.mkRenamedOptionModule
[ "hardware" "opengl" "extraPackages32" ]
[ "hardware" "graphics" "extraPackages32" ]
)
];
options.hardware.graphics = {
enable = lib.mkOption {
description = ''
Whether to enable hardware accelerated graphics drivers.
This is required to allow most graphical applications and
environments to use hardware rendering, video encode/decode
acceleration, etc.
This option should be enabled by default by the corresponding modules,
so you do not usually have to set it yourself.
'';
type = lib.types.bool;
default = false;
};
enable32Bit = lib.mkOption {
description = ''
On 64-bit systems, whether to also install 32-bit drivers for
32-bit applications (such as Wine).
'';
type = lib.types.bool;
default = false;
};
package = lib.mkOption {
description = ''
The package that provides the default driver set.
'';
type = lib.types.package;
};
package32 = lib.mkOption {
description = ''
The package that provides the 32-bit driver set. Used when {option}`enable32Bit` is enabled.
'';
type = lib.types.package;
};
extraPackages = lib.mkOption {
description = ''
Additional packages to add to the default graphics driver lookup path.
This can be used to add OpenCL drivers, VA-API/VDPAU drivers, etc.
::: {.note}
intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
:::
'';
type = lib.types.listOf lib.types.package;
default = [ ];
example = lib.literalExpression "with pkgs; [ intel-media-driver intel-ocl intel-vaapi-driver ]";
};
extraPackages32 = lib.mkOption {
description = ''
Additional packages to add to 32-bit graphics driver lookup path on 64-bit systems.
Used when {option}`enable32Bit` is set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers, etc.
::: {.note}
intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
:::
'';
type = lib.types.listOf lib.types.package;
default = [ ];
example = lib.literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver intel-vaapi-driver ]";
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.enable32Bit -> pkgs.stdenv.hostPlatform.isx86_64;
message = "`hardware.graphics.enable32Bit` is only supported on an x86_64 system.";
}
{
assertion = cfg.enable32Bit -> (config.boot.kernelPackages.kernel.features.ia32Emulation or false);
message = "`hardware.graphics.enable32Bit` requires a kernel that supports 32-bit emulation";
}
];
systemd.tmpfiles.settings.graphics-driver = {
"/run/opengl-driver"."L+".argument = toString driversEnv;
"/run/opengl-driver-32" =
if pkgs.stdenv.hostPlatform.isi686 then
{ "L+".argument = "opengl-driver"; }
else if cfg.enable32Bit then
{ "L+".argument = toString driversEnv32; }
else
{ "r" = { }; };
};
hardware.graphics.package = lib.mkDefault pkgs.mesa;
hardware.graphics.package32 = lib.mkDefault pkgs.pkgsi686Linux.mesa;
};
}

View File

@@ -0,0 +1,29 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.hackrf;
in
{
options.hardware.hackrf = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enables hackrf udev rules and ensures 'plugdev' group exists.
This is a prerequisite to using HackRF devices without being root, since HackRF USB descriptors will be owned by plugdev through udev.
Ensure your user is a member of the 'plugdev' group after enabling.
'';
};
};
config = lib.mkIf cfg.enable {
services.udev.packages = [ pkgs.hackrf ];
users.groups.plugdev = { };
};
}

View File

@@ -0,0 +1,51 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.i2c;
in
{
options.hardware.i2c = {
enable = lib.mkEnableOption ''
i2c devices support. By default access is granted to users in the "i2c"
group (will be created if non-existent) and any user with a seat, meaning
logged on the computer locally
'';
group = lib.mkOption {
type = lib.types.str;
default = "i2c";
description = ''
Grant access to i2c devices (/dev/i2c-*) to users in this group.
'';
};
};
config = lib.mkIf cfg.enable {
boot.kernelModules = [ "i2c-dev" ];
users.groups = lib.mkIf (cfg.group == "i2c") {
i2c = { };
};
services.udev.packages = lib.singleton (
pkgs.writeTextFile {
name = "i2c-udev-rules";
text = ''
# allow group ${cfg.group} and users with a seat use of i2c devices
ACTION=="add", KERNEL=="i2c-[0-9]*", TAG+="uaccess", GROUP="${cfg.group}", MODE="660"
'';
destination = "/etc/udev/rules.d/70-i2c.rules";
}
);
};
meta.maintainers = [ lib.maintainers.rnhmjoj ];
}

View File

@@ -0,0 +1,72 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.infiniband;
opensm-services = {
"opensm@" = {
enable = true;
description = "Starts OpenSM Infiniband fabric Subnet Managers";
before = [ "network.target" ];
unitConfig = {
ConditionPathExists = "/sys/class/infiniband_mad/abi_version";
};
serviceConfig = {
Type = "simple";
ExecStart = "${pkgs.opensm}/bin/opensm --guid %I --log_file /var/log/opensm.%I.log";
};
};
}
// (builtins.listToAttrs (
map (guid: {
name = "opensm@${guid}";
value = {
enable = true;
wantedBy = [ "machines.target" ];
overrideStrategy = "asDropin";
};
}) cfg.guids
));
in
{
options.hardware.infiniband = {
enable = lib.mkEnableOption "Infiniband support";
guids = lib.mkOption {
type = with lib.types; listOf str;
default = [ ];
example = [ "0xe8ebd30000eee2e1" ];
description = ''
A list of infiniband port guids on the system. This is discoverable using `ibstat -p`
'';
};
};
config = lib.mkIf cfg.enable {
boot.initrd.kernelModules = [
"mlx5_core"
"mlx5_ib"
"ib_cm"
"rdma_cm"
"rdma_ucm"
"rpcrdma"
"ib_ipoib"
"ib_isert"
"ib_umad"
"ib_uverbs"
];
# rdma-core exposes ibstat, mstflint exposes mstconfig (which can be needed for
# setting link configurations), qperf needed to affirm link speeds
environment.systemPackages = with pkgs; [
rdma-core
mstflint
qperf
];
systemd.services = opensm-services;
};
}

View File

@@ -0,0 +1,15 @@
{
config,
lib,
pkgs,
...
}:
{
options.hardware.inputmodule.enable = lib.mkEnableOption ''Support for Framework input modules'';
config = lib.mkIf config.hardware.inputmodule.enable {
environment.systemPackages = [ pkgs.inputmodule-control ];
services.udev.packages = [ pkgs.inputmodule-control ];
};
}

View File

@@ -0,0 +1,189 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
concatLines
concatStringsSep
mapAttrsToList
optional
optionals
mkIf
mkOption
types
;
cfg = config.hardware.block;
escape = lib.strings.escape [ ''"'' ];
udevValue = types.addCheck types.nonEmptyStr (x: builtins.match "[^\n\r]*" x != null) // {
name = "udevValue";
description = "udev rule value";
descriptionClass = "noun";
};
udevRule =
{
rotational ? null,
include ? null,
exclude ? null,
scheduler,
}:
concatStringsSep ", " (
[
''SUBSYSTEM=="block"''
''ACTION=="add|change"''
''TEST=="queue/scheduler"''
]
++ optionals (rotational != null) [
''ATTR{queue/rotational}=="${if rotational then "1" else "0"}"''
]
++ optionals (include != null) [
''KERNEL=="${escape include}"''
]
++ optionals (exclude != null) [
''KERNEL!="${escape exclude}"''
]
++ [
''ATTR{queue/scheduler}="${escape scheduler}"''
]
);
in
{
options.hardware.block = {
defaultScheduler = mkOption {
type = types.nullOr udevValue;
default = null;
description = ''
Default block I/O scheduler.
Unless `null`, the value is assigned through a udev rule matching all
block devices.
'';
example = "kyber";
};
defaultSchedulerRotational = mkOption {
type = types.nullOr udevValue;
default = null;
description = ''
Default block I/O scheduler for rotational drives (e.g. hard disks).
Unless `null`, the value is assigned through a udev rule matching all
rotational block devices.
This option takes precedence over
{option}`config.hardware.block.defaultScheduler`.
'';
example = "bfq";
};
defaultSchedulerExclude = mkOption {
type = types.nullOr udevValue;
default = "loop[0-9]*";
description = ''
Device name pattern to exclude from default scheduler assignment
through {option}`config.hardware.block.defaultScheduler` and
{option}`config.hardware.block.defaultSchedulerRotational`.
By default this excludes loop devices which generally do not benefit
from extra I/O scheduling in addition to the scheduling already
performed for their backing devices.
This setting does not affect {option}`config.hardware.block.scheduler`.
'';
};
scheduler = mkOption {
type = types.attrsOf udevValue;
default = { };
description = ''
Assign block I/O scheduler by device name pattern.
Names are matched using the {manpage}`udev(7)` pattern syntax:
`*`
: Matches zero or more characters.
`?`
: Matches any single character.
`[]`
: Matches any single character specified in the brackets. Ranges are
supported via the `-` character.
`|`
: Separates alternative patterns.
Please note that overlapping patterns may produce unexpected results.
More complex configurations requiring these should instead be specified
directly through custom udev rules, for example via
[{option}`config.services.udev.extraRules`](#opt-services.udev.extraRules),
to ensure correct ordering.
Available schedulers depend on the kernel configuration but modern
Linux systems typically support:
`none`
: Nooperation scheduler with no reordering of requests. Suitable
for devices with fast random I/O such as NVMe SSDs.
[`mq-deadline`](https://www.kernel.org/doc/html/latest/block/deadline-iosched.html)
: Simple latencyoriented generalpurpose scheduler.
[`kyber`](https://www.kernel.org/doc/html/latest/block/kyber-iosched.html)
: Simple latencyoriented scheduler for fast multiqueue devices
like NVMe SSDs.
[`bfq`](https://www.kernel.org/doc/html/latest/block/bfq-iosched.html)
: Complex fairnessoriented scheduler. Higher processing overhead,
but good interactive response, especially with slower devices.
Schedulers assigned through this option take precedence over
{option}`config.hardware.block.defaultScheduler` and
{option}`config.hardware.block.defaultSchedulerRotational` but may be
overridden by other udev rules.
'';
example = {
"mmcblk[0-9]*" = "bfq";
"nvme[0-9]*" = "kyber";
};
};
};
config =
mkIf
(cfg.defaultScheduler != null || cfg.defaultSchedulerRotational != null || cfg.scheduler != { })
{
services.udev.packages = [
(pkgs.writeTextDir "etc/udev/rules.d/98-block-io-scheduler.rules" (
concatLines (
optional (cfg.defaultScheduler != null) (udevRule {
exclude = cfg.defaultSchedulerExclude;
scheduler = cfg.defaultScheduler;
})
++ optional (cfg.defaultSchedulerRotational != null) (udevRule {
rotational = true;
exclude = cfg.defaultSchedulerExclude;
scheduler = cfg.defaultSchedulerRotational;
})
++ mapAttrsToList (
include: scheduler:
udevRule {
inherit include scheduler;
}
) cfg.scheduler
)
))
];
};
meta.maintainers = with lib.maintainers; [ mvs ];
}

View File

@@ -0,0 +1,22 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.keyboard.qmk;
inherit (lib) mkEnableOption mkIf;
in
{
options.hardware.keyboard.qmk = {
enable = mkEnableOption "non-root access to the firmware of QMK keyboards";
};
config = mkIf cfg.enable {
services.udev.packages = [ pkgs.qmk-udev-rules ];
users.groups.plugdev = { };
};
}

View File

@@ -0,0 +1,21 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.keyboard.teck;
inherit (lib) mkEnableOption mkIf;
in
{
options.hardware.keyboard.teck = {
enable = mkEnableOption "non-root access to the firmware of TECK keyboards";
};
config = mkIf cfg.enable {
services.udev.packages = [ pkgs.teck-udev-rules ];
};
}

View File

@@ -0,0 +1,27 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.keyboard.uhk;
inherit (lib) mkEnableOption mkIf;
in
{
options.hardware.keyboard.uhk = {
enable = mkEnableOption ''
non-root access to the firmware of UHK keyboards.
You need it when you want to flash a new firmware on the keyboard.
Access to the keyboard is granted to users in the "input" group.
You may want to install the uhk-agent package
'';
};
config = mkIf cfg.enable {
services.udev.packages = [ pkgs.uhk-udev-rules ];
};
}

View File

@@ -0,0 +1,26 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.keyboard.zsa;
inherit (lib) mkEnableOption mkIf;
in
{
options.hardware.keyboard.zsa = {
enable = mkEnableOption ''
udev rules for keyboards from ZSA like the ErgoDox EZ, Planck EZ and Moonlander Mark I.
You need it when you want to flash a new configuration on the keyboard
or use their live training in the browser.
You may want to install the wally-cli package
'';
};
config = mkIf cfg.enable {
services.udev.packages = [ pkgs.zsa-udev-rules ];
};
}

View File

@@ -0,0 +1,33 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.kryoflux;
in
{
options.hardware.kryoflux = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enables kryoflux udev rules, ensures 'floppy' group exists. This is a
prerequisite to using devices supported by kryoflux without being root,
since kryoflux device descriptors will be owned by floppy through udev.
'';
};
package = lib.mkPackageOption pkgs "kryoflux" { };
};
config = lib.mkIf cfg.enable {
services.udev.packages = [ cfg.package ];
environment.systemPackages = [ cfg.package ];
users.groups.floppy = { };
};
meta.maintainers = with lib.maintainers; [ matthewcroughan ];
}

View File

@@ -0,0 +1,35 @@
{ config, lib, ... }:
let
cfg = config.hardware.ksm;
in
{
imports = [
(lib.mkRenamedOptionModule [ "hardware" "enableKSM" ] [ "hardware" "ksm" "enable" ])
];
options.hardware.ksm = {
enable = lib.mkEnableOption "Linux kernel Same-Page Merging";
sleep = lib.mkOption {
type = lib.types.nullOr lib.types.int;
default = null;
description = ''
How many milliseconds ksmd should sleep between scans.
Setting it to `null` uses the kernel's default time.
'';
};
};
config = lib.mkIf cfg.enable {
systemd.services.enable-ksm = {
description = "Enable Kernel Same-Page Merging";
wantedBy = [ "multi-user.target" ];
script = ''
echo 1 > /sys/kernel/mm/ksm/run
''
+ lib.optionalString (cfg.sleep != null) ''
echo ${toString cfg.sleep} > /sys/kernel/mm/ksm/sleep_millisecs
'';
};
};
}

View File

@@ -0,0 +1,17 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.ledger;
in
{
options.hardware.ledger.enable = lib.mkEnableOption "udev rules for Ledger devices";
config = lib.mkIf cfg.enable {
services.udev.packages = [ pkgs.ledger-udev-rules ];
};
}

View File

@@ -0,0 +1,23 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.libftdi;
in
{
options.hardware.libftdi = {
enable = lib.mkEnableOption "udev rules for devices supported by libftdi";
package = lib.mkPackageOption pkgs "libftdi1" { };
};
config = lib.mkIf cfg.enable {
users.groups.ftdi = { };
services.udev.packages = [ cfg.package ];
};
meta.maintainers = with lib.maintainers; [ felixsinger ];
}

View File

@@ -0,0 +1,27 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.libjaylink;
in
{
options.hardware.libjaylink = {
enable = lib.mkEnableOption ''
udev rules for devices supported by libjaylink.
Add users to the `jlink` group in order to grant
them access
'';
package = lib.mkPackageOption pkgs "libjaylink" { };
};
config = lib.mkIf cfg.enable {
users.groups.jlink = { };
services.udev.packages = [ cfg.package ];
};
meta.maintainers = with lib.maintainers; [ felixsinger ];
}

View File

@@ -0,0 +1,112 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.logitech;
vendor = "046d";
daemon = "g15daemon";
in
{
imports = [
(lib.mkRenamedOptionModule
[ "hardware" "logitech" "enable" ]
[ "hardware" "logitech" "wireless" "enable" ]
)
(lib.mkRenamedOptionModule
[ "hardware" "logitech" "enableGraphical" ]
[ "hardware" "logitech" "wireless" "enableGraphical" ]
)
];
options.hardware.logitech = {
lcd = {
enable = lib.mkEnableOption "support for Logitech LCD Devices";
startWhenNeeded = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Only run the service when an actual supported device is plugged.
'';
};
devices = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"0a07"
"c222"
"c225"
"c227"
"c251"
];
description = ''
List of USB device ids supported by g15daemon.
You most likely do not need to change this.
'';
};
};
wireless = {
enable = lib.mkEnableOption "support for Logitech Wireless Devices";
enableGraphical = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable graphical support applications.";
};
};
};
config = lib.mkIf (cfg.wireless.enable || cfg.lcd.enable) {
environment.systemPackages =
[ ]
++ lib.optional cfg.wireless.enable pkgs.ltunify
++ lib.optional cfg.wireless.enableGraphical pkgs.solaar;
services.udev = {
# ltunifi and solaar both provide udev rules but the most up-to-date have been split
# out into a dedicated derivation
packages =
[ ]
++ lib.optional cfg.wireless.enable pkgs.logitech-udev-rules
++ lib.optional cfg.lcd.enable pkgs.g15daemon;
extraRules = ''
# nixos: hardware.logitech.lcd
''
+ lib.concatMapStringsSep "\n" (
dev:
''ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="${vendor}", ATTRS{idProduct}=="${dev}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="${daemon}.service"''
) cfg.lcd.devices;
};
systemd.services."${daemon}" = lib.mkIf cfg.lcd.enable {
description = "Logitech LCD Support Daemon";
documentation = [ "man:g15daemon(1)" ];
wantedBy = lib.mkIf (!cfg.lcd.startWhenNeeded) "multi-user.target";
serviceConfig = {
Type = "forking";
ExecStart = "${pkgs.g15daemon}/bin/g15daemon";
# we patch it to write to /run/g15daemon/g15daemon.pid instead of
# /run/g15daemon.pid so systemd will do the cleanup for us.
PIDFile = "/run/${daemon}/g15daemon.pid";
PrivateTmp = true;
PrivateNetwork = true;
ProtectHome = "tmpfs";
ProtectSystem = "full"; # strict doesn't work
RuntimeDirectory = daemon;
Restart = "on-failure";
};
};
};
}

View File

@@ -0,0 +1,37 @@
{
config,
lib,
pkgs,
...
}:
{
meta.maintainers = with lib.maintainers; [ grahamc ];
options = {
hardware.mcelog = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable the Machine Check Exception logger.
'';
};
};
};
config = lib.mkIf config.hardware.mcelog.enable {
systemd = {
packages = [ pkgs.mcelog ];
services.mcelog = {
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ProtectHome = true;
PrivateNetwork = true;
PrivateTmp = true;
};
};
};
};
}

View File

@@ -0,0 +1,34 @@
{
config,
lib,
pkgs,
...
}:
let
kernelVersion = config.boot.kernelPackages.kernel.version;
linuxKernelMinVersion = "5.8";
kernelPatch = pkgs.kernelPatches.ath_regd_optional // {
extraConfig = ''
ATH_USER_REGD y
'';
};
in
{
options.networking.wireless.athUserRegulatoryDomain = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
If enabled, sets the ATH_USER_REGD kernel config switch to true to
disable the enforcement of EEPROM regulatory restrictions for ath
drivers. Requires at least Linux ${linuxKernelMinVersion}.
'';
};
config = lib.mkIf config.networking.wireless.athUserRegulatoryDomain {
assertions = lib.singleton {
assertion = lib.lessThan 0 (builtins.compareVersions kernelVersion linuxKernelMinVersion);
message = "ATH_USER_REGD patch for kernels older than ${linuxKernelMinVersion} not ported yet!";
};
boot.kernelPatches = [ kernelPatch ];
};
}

View File

@@ -0,0 +1,33 @@
{
config,
lib,
pkgs,
...
}:
let
kernelVersion = config.boot.kernelPackages.kernel.version;
in
{
###### interface
options = {
networking.enableB43Firmware = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Turn on this option if you want firmware for the NICs supported by the b43 module.
'';
};
};
###### implementation
config = lib.mkIf config.networking.enableB43Firmware {
hardware.firmware = [ pkgs.b43Firmware_5_1_138 ];
};
}

View File

@@ -0,0 +1,3 @@
{
hardware.enableRedistributableFirmware = true;
}

View File

@@ -0,0 +1,27 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkEnableOption mkIf mkPackageOption;
cfg = config.services.eg25-manager;
in
{
options.services.eg25-manager = {
enable = mkEnableOption "Quectel EG25 modem manager service";
package = mkPackageOption pkgs "eg25-manager" { };
};
config = mkIf cfg.enable {
systemd.packages = [ cfg.package ];
services.udev.packages = [ cfg.package ];
systemd.services.eg25-manager.wantedBy = [ "multi-user.target" ];
};
meta = {
maintainers = with lib.maintainers; [ Luflosi ];
};
}

View File

@@ -0,0 +1,34 @@
{
config,
pkgs,
lib,
...
}:
{
###### interface
options = {
networking.enableIntel2200BGFirmware = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Turn on this option if you want firmware for the Intel
PRO/Wireless 2200BG to be loaded automatically. This is
required if you want to use this device.
'';
};
};
###### implementation
config = lib.mkIf config.networking.enableIntel2200BGFirmware {
hardware.firmware = [ pkgs.intel2200BGFirmware ];
};
}

View File

@@ -0,0 +1,9 @@
{ lib, ... }:
{
hardware = {
pcmcia = {
firmware = [ (lib.cleanSource ./firmware) ];
};
};
}

View File

@@ -0,0 +1,8 @@
vers_1 5.0, "SMC", "SMC2632W", "Version 01.02", ""
manfid 0x0156, 0x0002
funcid network_adapter
cftable_entry 0x01 [default]
Vcc Vmin 3000mV Vmax 3300mV Iavg 300mA Ipeak 300mA
Idown 10mA
io 0x0000-0x003f [lines=6] [16bit]
irq mask 0xffff [level] [pulse]

View File

@@ -0,0 +1,5 @@
{ pkgs, ... }:
{
hardware.firmware = [ pkgs.zd1211fw ];
}

View File

@@ -0,0 +1,32 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.hardware.new-lg4ff;
kernelPackages = config.boot.kernelPackages;
in
{
options.hardware.new-lg4ff = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enables improved Linux module drivers for Logitech driving wheels.
This will replace the existing in-kernel hid-logitech modules.
Works most notably on the Logitech G25, G27, G29 and Driving Force (GT).
'';
};
};
config = lib.mkIf cfg.enable {
boot = {
extraModulePackages = [ kernelPackages.new-lg4ff ];
kernelModules = [ "hid-logitech-new" ];
};
};
meta.maintainers = with lib.maintainers; [ matthiasbenaets ];
}

Some files were not shown because too many files have changed in this diff Show More