0
0
Fork 0
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-07-13 21:50:33 +03:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Googlebot 2022-03-21 11:24:33 -04:00
commit 4007aa201b
1574 changed files with 34933 additions and 29657 deletions

View file

@ -39,11 +39,6 @@ let
defaultXFonts =
[ (if hasHidpi then fontcursormisc_hidpi else pkgs.xorg.fontcursormisc)
pkgs.xorg.fontmiscmisc
] ++ optionals (config.nixpkgs.config.allowUnfree or false)
[ # these are unfree, and will make usage with xserver fail
pkgs.xorg.fontbhlucidatypewriter100dpi
pkgs.xorg.fontbhlucidatypewriter75dpi
pkgs.xorg.fontbh100dpi
];
in

View file

@ -289,8 +289,14 @@ in
environment.etc."egl/egl_external_platform.d".source =
"/run/opengl-driver/share/egl/egl_external_platform.d/";
hardware.opengl.extraPackages = [ nvidia_x11.out ];
hardware.opengl.extraPackages32 = [ nvidia_x11.lib32 ];
hardware.opengl.extraPackages = [
nvidia_x11.out
pkgs.nvidia-vaapi-driver
];
hardware.opengl.extraPackages32 = [
nvidia_x11.lib32
pkgs.pkgsi686Linux.nvidia-vaapi-driver
];
environment.systemPackages = [ nvidia_x11.bin ]
++ optionals cfg.nvidiaSettings [ nvidia_x11.settings ]

View file

@ -63,32 +63,32 @@ mount --rbind /sys "$mountPoint/sys"
# modified from https://github.com/archlinux/arch-install-scripts/blob/bb04ab435a5a89cd5e5ee821783477bc80db797f/arch-chroot.in#L26-L52
chroot_add_resolv_conf() {
local chrootdir=$1 resolv_conf=$1/etc/resolv.conf
local chrootDir="$1" resolvConf="$1/etc/resolv.conf"
[[ -e /etc/resolv.conf ]] || return 0
# Handle resolv.conf as a symlink to somewhere else.
if [[ -L $chrootdir/etc/resolv.conf ]]; then
if [[ -L "$resolvConf" ]]; then
# readlink(1) should always give us *something* since we know at this point
# it's a symlink. For simplicity, ignore the case of nested symlinks.
# We also ignore the possibility if `../`s escaping the root.
resolv_conf=$(readlink "$chrootdir/etc/resolv.conf")
if [[ $resolv_conf = /* ]]; then
resolv_conf=$chrootdir$resolv_conf
# We also ignore the possibility of `../`s escaping the root.
resolvConf="$(readlink "$resolvConf")"
if [[ "$resolvConf" = /* ]]; then
resolvConf="$chrootDir$resolvConf"
else
resolv_conf=$chrootdir/etc/$resolv_conf
resolvConf="$chrootDir/etc/$resolvConf"
fi
fi
# ensure file exists to bind mount over
if [[ ! -f $resolv_conf ]]; then
install -Dm644 /dev/null "$resolv_conf" || return 1
if [[ ! -f "$resolvConf" ]]; then
install -Dm644 /dev/null "$resolvConf" || return 1
fi
mount --bind /etc/resolv.conf "$resolv_conf"
mount --bind /etc/resolv.conf "$resolvConf"
}
chroot_add_resolv_conf "$mountPoint" || print "ERROR: failed to set up resolv.conf"
chroot_add_resolv_conf "$mountPoint" || echo "$0: failed to set up resolv.conf" >&2
(
# If silent, write both stdout and stderr of activation script to /dev/null

View file

@ -183,7 +183,11 @@ in
pruneNames = mkOption {
type = listOf str;
default = [ ".bzr" ".cache" ".git" ".hg" ".svn" ];
default = lib.optionals (!isFindutils) [ ".bzr" ".cache" ".git" ".hg" ".svn" ];
defaultText = literalDocBook ''
<literal>[ ".bzr" ".cache" ".git" ".hg" ".svn" ]</literal>, if
supported by the locate implementation (i.e. mlocate or plocate).
'';
description = ''
Directory components which should exclude paths containing them from indexing
'';

View file

@ -8,8 +8,12 @@ let
concatStringsSep mapAttrsToList toLower
literalExpression mkRenamedOptionModule mkDefault mkOption trivial types;
needsEscaping = s: null != builtins.match "[a-zA-Z0-9]+" s;
escapeIfNeccessary = s: if needsEscaping s then s else ''"${lib.escape [ "\$" "\"" "\\" "\`" ] s}"'';
attrsToText = attrs:
concatStringsSep "\n" (mapAttrsToList (n: v: ''${n}="${toString v}"'') attrs);
concatStringsSep "\n" (
mapAttrsToList (n: v: ''${n}=${escapeIfNeccessary (toString v)}'') attrs
);
in
{

View file

@ -180,6 +180,7 @@
./programs/msmtp.nix
./programs/mtr.nix
./programs/nano.nix
./programs/nbd.nix
./programs/neovim.nix
./programs/nm-applet.nix
./programs/npm.nix
@ -301,6 +302,7 @@
./services/backup/znapzend.nix
./services/blockchain/ethereum/geth.nix
./services/backup/zrepl.nix
./services/cluster/corosync/default.nix
./services/cluster/hadoop/default.nix
./services/cluster/k3s/default.nix
./services/cluster/kubernetes/addons/dns.nix
@ -313,6 +315,7 @@
./services/cluster/kubernetes/pki.nix
./services/cluster/kubernetes/proxy.nix
./services/cluster/kubernetes/scheduler.nix
./services/cluster/pacemaker/default.nix
./services/cluster/spark/default.nix
./services/computing/boinc/client.nix
./services/computing/foldingathome/client.nix
@ -819,6 +822,7 @@
./services/networking/nar-serve.nix
./services/networking/nat.nix
./services/networking/nats.nix
./services/networking/nbd.nix
./services/networking/ndppd.nix
./services/networking/nebula.nix
./services/networking/networkmanager.nix
@ -985,6 +989,7 @@
./services/system/nscd.nix
./services/system/saslauthd.nix
./services/system/self-deploy.nix
./services/system/systembus-notify.nix
./services/system/uptimed.nix
./services/torrent/deluge.nix
./services/torrent/flexget.nix
@ -1163,7 +1168,12 @@
./system/boot/stage-1.nix
./system/boot/stage-2.nix
./system/boot/systemd.nix
./system/boot/systemd-nspawn.nix
./system/boot/systemd/coredump.nix
./system/boot/systemd/journald.nix
./system/boot/systemd/logind.nix
./system/boot/systemd/nspawn.nix
./system/boot/systemd/tmpfiles.nix
./system/boot/systemd/user.nix
./system/boot/timesyncd.nix
./system/boot/tmp.nix
./system/etc/etc-activation.nix

View file

@ -1,8 +1,12 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.captive-browser;
inherit (lib)
concatStringsSep escapeShellArgs optionalString
literalExpression mkEnableOption mkIf mkOption mkOptionDefault types;
browserDefault = chromium: concatStringsSep " " [
''env XDG_CONFIG_HOME="$PREV_CONFIG_HOME"''
''${chromium}/bin/chromium''
@ -15,6 +19,15 @@ let
''-no-default-browser-check''
''http://cache.nixos.org/''
];
desktopItem = pkgs.makeDesktopItem {
name = "captive-browser";
desktopName = "Captive Portal Browser";
exec = "/run/wrappers/bin/captive-browser";
icon = "nix-snowflake";
categories = [ "Network" ];
};
in
{
###### interface
@ -84,6 +97,11 @@ in
###### implementation
config = mkIf cfg.enable {
environment.systemPackages = [
(pkgs.runCommandNoCC "captive-browser-desktop-item" { } ''
install -Dm444 -t $out/share/applications ${desktopItem}/share/applications/*.desktop
'')
];
programs.captive-browser.dhcp-dns =
let

View file

@ -40,13 +40,15 @@ in
KDEDIRS = [ "" ];
QT_PLUGIN_PATH = [ "/lib/qt4/plugins" "/lib/kde4/plugins" ];
QTWEBKIT_PLUGIN_PATH = [ "/lib/mozilla/plugins/" ];
GTK_PATH = [ "/lib/gtk-2.0" "/lib/gtk-3.0" ];
GTK_PATH = [ "/lib/gtk-2.0" "/lib/gtk-3.0" "/lib/gtk-4.0" ];
XDG_CONFIG_DIRS = [ "/etc/xdg" ];
XDG_DATA_DIRS = [ "/share" ];
MOZ_PLUGIN_PATH = [ "/lib/mozilla/plugins" ];
LIBEXEC_PATH = [ "/lib/libexec" ];
};
environment.pathsToLink = [ "/lib/gtk-2.0" "/lib/gtk-3.0" "/lib/gtk-4.0" ];
environment.extraInit =
''
unset ASPELL_CONF

View file

@ -0,0 +1,19 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.nbd;
in
{
options = {
programs.nbd = {
enable = mkEnableOption "Network Block Device (nbd) support";
};
};
config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [ nbd ];
boot.kernelModules = [ "nbd" ];
};
}

View file

@ -1,50 +1,46 @@
{ config, lib, pkgs, ... }:
with lib;
let
inherit (lib) mkEnableOption mkIf mkOption optionalString types;
dataDir = "/var/lib/squeezelite";
cfg = config.services.squeezelite;
pkg = if cfg.pulseAudio then pkgs.squeezelite-pulse else pkgs.squeezelite;
bin = "${pkg}/bin/${pkg.pname}";
in {
in
{
###### interface
options = {
options.services.squeezelite = {
enable = mkEnableOption "Squeezelite, a software Squeezebox emulator";
services.squeezelite= {
enable = mkEnableOption "Squeezelite, a software Squeezebox emulator";
extraArguments = mkOption {
default = "";
type = types.str;
description = ''
Additional command line arguments to pass to Squeezelite.
'';
};
pulseAudio = mkEnableOption "pulseaudio support";
extraArguments = mkOption {
default = "";
type = types.str;
description = ''
Additional command line arguments to pass to Squeezelite.
'';
};
};
###### implementation
config = mkIf cfg.enable {
systemd.services.squeezelite= {
systemd.services.squeezelite = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "sound.target" ];
description = "Software Squeezebox emulator";
serviceConfig = {
DynamicUser = true;
ExecStart = "${pkgs.squeezelite}/bin/squeezelite -N ${dataDir}/player-name ${cfg.extraArguments}";
ExecStart = "${bin} -N ${dataDir}/player-name ${cfg.extraArguments}";
StateDirectory = builtins.baseNameOf dataDir;
SupplementaryGroups = "audio";
};
};
};
}

View file

@ -0,0 +1,112 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.corosync;
in
{
# interface
options.services.corosync = {
enable = mkEnableOption "corosync";
package = mkOption {
type = types.package;
default = pkgs.corosync;
defaultText = literalExpression "pkgs.corosync";
description = "Package that should be used for corosync.";
};
clusterName = mkOption {
type = types.str;
default = "nixcluster";
description = "Name of the corosync cluster.";
};
extraOptions = mkOption {
type = with types; listOf str;
default = [];
description = "Additional options with which to start corosync.";
};
nodelist = mkOption {
description = "Corosync nodelist: all cluster members.";
default = [];
type = with types; listOf (submodule {
options = {
nodeid = mkOption {
type = int;
description = "Node ID number";
};
name = mkOption {
type = str;
description = "Node name";
};
ring_addrs = mkOption {
type = listOf str;
description = "List of addresses, one for each ring.";
};
};
});
};
};
# implementation
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
environment.etc."corosync/corosync.conf".text = ''
totem {
version: 2
secauth: on
cluster_name: ${cfg.clusterName}
transport: knet
}
nodelist {
${concatMapStrings ({ nodeid, name, ring_addrs }: ''
node {
nodeid: ${toString nodeid}
name: ${name}
${concatStrings (imap0 (i: addr: ''
ring${toString i}_addr: ${addr}
'') ring_addrs)}
}
'') cfg.nodelist}
}
quorum {
# only corosync_votequorum is supported
provider: corosync_votequorum
wait_for_all: 0
${optionalString (builtins.length cfg.nodelist < 3) ''
two_node: 1
''}
}
logging {
to_syslog: yes
}
'';
environment.etc."corosync/uidgid.d/root".text = ''
# allow pacemaker connection by root
uidgid {
uid: 0
gid: 0
}
'';
systemd.packages = [ cfg.package ];
systemd.services.corosync = {
wantedBy = [ "multi-user.target" ];
serviceConfig = {
StateDirectory = "corosync";
StateDirectoryMode = "0700";
};
};
environment.etc."sysconfig/corosync".text = lib.optionalString (cfg.extraOptions != []) ''
COROSYNC_OPTIONS="${lib.escapeShellArgs cfg.extraOptions}"
'';
};
}

View file

@ -1,6 +1,6 @@
{ cfg, pkgs, lib }:
let
propertyXml = name: value: ''
propertyXml = name: value: lib.optionalString (value != null) ''
<property>
<name>${name}</name>
<value>${builtins.toString value}</value>
@ -29,16 +29,16 @@ let
export HADOOP_LOG_DIR=/tmp/hadoop/$USER
'';
in
pkgs.runCommand "hadoop-conf" {} ''
pkgs.runCommand "hadoop-conf" {} (with cfg; ''
mkdir -p $out/
cp ${siteXml "core-site.xml" cfg.coreSite}/* $out/
cp ${siteXml "hdfs-site.xml" cfg.hdfsSite}/* $out/
cp ${siteXml "mapred-site.xml" cfg.mapredSite}/* $out/
cp ${siteXml "yarn-site.xml" cfg.yarnSite}/* $out/
cp ${siteXml "httpfs-site.xml" cfg.httpfsSite}/* $out/
cp ${cfgFile "container-executor.cfg" cfg.containerExecutorCfg}/* $out/
cp ${siteXml "core-site.xml" (coreSite // coreSiteInternal)}/* $out/
cp ${siteXml "hdfs-site.xml" (hdfsSiteDefault // hdfsSite // hdfsSiteInternal)}/* $out/
cp ${siteXml "mapred-site.xml" (mapredSiteDefault // mapredSite)}/* $out/
cp ${siteXml "yarn-site.xml" (yarnSiteDefault // yarnSite // yarnSiteInternal)}/* $out/
cp ${siteXml "httpfs-site.xml" httpfsSite}/* $out/
cp ${cfgFile "container-executor.cfg" containerExecutorCfg}/* $out/
cp ${pkgs.writeTextDir "hadoop-user-functions.sh" userFunctions}/* $out/
cp ${pkgs.writeTextDir "hadoop-env.sh" hadoopEnv}/* $out/
cp ${cfg.log4jProperties} $out/log4j.properties
${lib.concatMapStringsSep "\n" (dir: "cp -r ${dir}/* $out/") cfg.extraConfDirs}
''
cp ${log4jProperties} $out/log4j.properties
${lib.concatMapStringsSep "\n" (dir: "cp -r ${dir}/* $out/") extraConfDirs}
'')

View file

@ -21,24 +21,50 @@ with lib;
<link xlink:href="https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/core-default.xml"/>
'';
};
coreSiteInternal = mkOption {
default = {};
type = types.attrsOf types.anything;
internal = true;
description = ''
Internal option to add configs to core-site.xml based on module options
'';
};
hdfsSite = mkOption {
hdfsSiteDefault = mkOption {
default = {
"dfs.namenode.rpc-bind-host" = "0.0.0.0";
"dfs.namenode.http-address" = "0.0.0.0:9870";
"dfs.namenode.servicerpc-bind-host" = "0.0.0.0";
"dfs.namenode.http-bind-host" = "0.0.0.0";
};
type = types.attrsOf types.anything;
description = ''
Default options for hdfs-site.xml
'';
};
hdfsSite = mkOption {
default = {};
type = types.attrsOf types.anything;
example = literalExpression ''
{
"dfs.nameservices" = "namenode1";
}
'';
description = ''
Hadoop hdfs-site.xml definition
Additional options and overrides for hdfs-site.xml
<link xlink:href="https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml"/>
'';
};
hdfsSiteInternal = mkOption {
default = {};
type = types.attrsOf types.anything;
internal = true;
description = ''
Internal option to add configs to hdfs-site.xml based on module options
'';
};
mapredSite = mkOption {
mapredSiteDefault = mkOption {
default = {
"mapreduce.framework.name" = "yarn";
"yarn.app.mapreduce.am.env" = "HADOOP_MAPRED_HOME=${cfg.package}/lib/${cfg.package.untarDir}";
@ -54,18 +80,25 @@ with lib;
}
'';
type = types.attrsOf types.anything;
description = ''
Default options for mapred-site.xml
'';
};
mapredSite = mkOption {
default = {};
type = types.attrsOf types.anything;
example = literalExpression ''
options.services.hadoop.mapredSite.default // {
{
"mapreduce.map.java.opts" = "-Xmx900m -XX:+UseParallelGC";
}
'';
description = ''
Hadoop mapred-site.xml definition
Additional options and overrides for mapred-site.xml
<link xlink:href="https://hadoop.apache.org/docs/current/hadoop-mapreduce-client/hadoop-mapreduce-client-core/mapred-default.xml"/>
'';
};
yarnSite = mkOption {
yarnSiteDefault = mkOption {
default = {
"yarn.nodemanager.admin-env" = "PATH=$PATH";
"yarn.nodemanager.aux-services" = "mapreduce_shuffle";
@ -77,19 +110,34 @@ with lib;
"yarn.nodemanager.linux-container-executor.path" = "/run/wrappers/yarn-nodemanager/bin/container-executor";
"yarn.nodemanager.log-dirs" = "/var/log/hadoop/yarn/nodemanager";
"yarn.resourcemanager.bind-host" = "0.0.0.0";
"yarn.resourcemanager.scheduler.class" = "org.apache.hadoop.yarn.server.resourcemanager.scheduler.fifo.FifoScheduler";
"yarn.resourcemanager.scheduler.class" = "org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler";
};
type = types.attrsOf types.anything;
description = ''
Default options for yarn-site.xml
'';
};
yarnSite = mkOption {
default = {};
type = types.attrsOf types.anything;
example = literalExpression ''
options.services.hadoop.yarnSite.default // {
{
"yarn.resourcemanager.hostname" = "''${config.networking.hostName}";
}
'';
description = ''
Hadoop yarn-site.xml definition
Additional options and overrides for yarn-site.xml
<link xlink:href="https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-common/yarn-default.xml"/>
'';
};
yarnSiteInternal = mkOption {
default = {};
type = types.attrsOf types.anything;
internal = true;
description = ''
Internal option to add configs to yarn-site.xml based on module options
'';
};
httpfsSite = mkOption {
default = { };
@ -123,6 +171,7 @@ with lib;
"yarn.nodemanager.linux-container-executor.group"="hadoop";
"min.user.id"=1000;
"feature.terminal.enabled"=1;
"feature.mount-cgroup.enabled" = 1;
};
type = types.attrsOf types.anything;
example = literalExpression ''
@ -148,6 +197,8 @@ with lib;
description = "Directories containing additional config files to be added to HADOOP_CONF_DIR";
};
gatewayRole.enable = mkEnableOption "gateway role for deploying hadoop configs";
package = mkOption {
type = types.package;
default = pkgs.hadoop;
@ -157,20 +208,16 @@ with lib;
};
config = mkMerge [
(mkIf (builtins.hasAttr "yarn" config.users.users ||
builtins.hasAttr "hdfs" config.users.users ||
builtins.hasAttr "httpfs" config.users.users) {
users.groups.hadoop = {
gid = config.ids.gids.hadoop;
};
environment = {
systemPackages = [ cfg.package ];
etc."hadoop-conf".source = let
hadoopConf = "${import ./conf.nix { inherit cfg pkgs lib; }}/";
in "${hadoopConf}";
};
})
];
config = mkIf cfg.gatewayRole.enable {
users.groups.hadoop = {
gid = config.ids.gids.hadoop;
};
environment = {
systemPackages = [ cfg.package ];
etc."hadoop-conf".source = let
hadoopConf = "${import ./conf.nix { inherit cfg pkgs lib; }}/";
in "${hadoopConf}";
variables.HADOOP_CONF_DIR = "/etc/hadoop-conf/";
};
};
}

View file

@ -1,191 +1,191 @@
{ config, lib, pkgs, ...}:
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.hadoop;
# Config files for hadoop services
hadoopConf = "${import ./conf.nix { inherit cfg pkgs lib; }}/";
restartIfChanged = mkOption {
type = types.bool;
description = ''
Automatically restart the service on config change.
This can be set to false to defer restarts on clusters running critical applications.
Please consider the security implications of inadvertently running an older version,
and the possibility of unexpected behavior caused by inconsistent versions across a cluster when disabling this option.
'';
default = false;
};
# Generator for HDFS service options
hadoopServiceOption = { serviceName, firewallOption ? true, extraOpts ? null }: {
enable = mkEnableOption serviceName;
restartIfChanged = mkOption {
type = types.bool;
description = ''
Automatically restart the service on config change.
This can be set to false to defer restarts on clusters running critical applications.
Please consider the security implications of inadvertently running an older version,
and the possibility of unexpected behavior caused by inconsistent versions across a cluster when disabling this option.
'';
default = false;
};
extraFlags = mkOption{
type = with types; listOf str;
default = [];
description = "Extra command line flags to pass to ${serviceName}";
example = [
"-Dcom.sun.management.jmxremote"
"-Dcom.sun.management.jmxremote.port=8010"
];
};
extraEnv = mkOption{
type = with types; attrsOf str;
default = {};
description = "Extra environment variables for ${serviceName}";
};
} // (optionalAttrs firewallOption {
openFirewall = mkOption {
type = types.bool;
default = false;
description = "Open firewall ports for ${serviceName}.";
};
}) // (optionalAttrs (extraOpts != null) extraOpts);
# Generator for HDFS service configs
hadoopServiceConfig =
{ name
, serviceOptions ? cfg.hdfs."${toLower name}"
, description ? "Hadoop HDFS ${name}"
, User ? "hdfs"
, allowedTCPPorts ? [ ]
, preStart ? ""
, environment ? { }
, extraConfig ? { }
}: (
mkIf serviceOptions.enable ( mkMerge [{
systemd.services."hdfs-${toLower name}" = {
inherit description preStart;
environment = environment // serviceOptions.extraEnv;
wantedBy = [ "multi-user.target" ];
inherit (serviceOptions) restartIfChanged;
serviceConfig = {
inherit User;
SyslogIdentifier = "hdfs-${toLower name}";
ExecStart = "${cfg.package}/bin/hdfs --config ${hadoopConf} ${toLower name} ${escapeShellArgs serviceOptions.extraFlags}";
Restart = "always";
};
};
services.hadoop.gatewayRole.enable = true;
networking.firewall.allowedTCPPorts = mkIf
((builtins.hasAttr "openFirewall" serviceOptions) && serviceOptions.openFirewall)
allowedTCPPorts;
} extraConfig])
);
in
{
options.services.hadoop.hdfs = {
namenode = {
enable = mkEnableOption "Whether to run the HDFS NameNode";
namenode = hadoopServiceOption { serviceName = "HDFS NameNode"; } // {
formatOnInit = mkOption {
type = types.bool;
default = false;
description = ''
Format HDFS namenode on first start. This is useful for quickly spinning up ephemeral HDFS clusters with a single namenode.
For HA clusters, initialization involves multiple steps across multiple nodes. Follow [this guide](https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.html)
to initialize an HA cluster manually.
'';
};
inherit restartIfChanged;
openFirewall = mkOption {
type = types.bool;
default = true;
description = ''
Open firewall ports for namenode
Format HDFS namenode on first start. This is useful for quickly spinning up
ephemeral HDFS clusters with a single namenode.
For HA clusters, initialization involves multiple steps across multiple nodes.
Follow this guide to initialize an HA cluster manually:
<link xlink:href="https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.html"/>
'';
};
};
datanode = {
enable = mkEnableOption "Whether to run the HDFS DataNode";
inherit restartIfChanged;
openFirewall = mkOption {
type = types.bool;
default = true;
description = ''
Open firewall ports for datanode
'';
datanode = hadoopServiceOption { serviceName = "HDFS DataNode"; } // {
dataDirs = mkOption {
default = null;
description = "Tier and path definitions for datanode storage.";
type = with types; nullOr (listOf (submodule {
options = {
type = mkOption {
type = enum [ "SSD" "DISK" "ARCHIVE" "RAM_DISK" ];
description = ''
Storage types ([SSD]/[DISK]/[ARCHIVE]/[RAM_DISK]) for HDFS storage policies.
'';
};
path = mkOption {
type = path;
example = [ "/var/lib/hadoop/hdfs/dn" ];
description = "Determines where on the local filesystem a data node should store its blocks.";
};
};
}));
};
};
journalnode = {
enable = mkEnableOption "Whether to run the HDFS JournalNode";
inherit restartIfChanged;
openFirewall = mkOption {
type = types.bool;
default = true;
description = ''
Open firewall ports for journalnode
'';
};
journalnode = hadoopServiceOption { serviceName = "HDFS JournalNode"; };
zkfc = hadoopServiceOption {
serviceName = "HDFS ZooKeeper failover controller";
firewallOption = false;
};
zkfc = {
enable = mkEnableOption "Whether to run the HDFS ZooKeeper failover controller";
inherit restartIfChanged;
};
httpfs = {
enable = mkEnableOption "Whether to run the HDFS HTTPfs server";
httpfs = hadoopServiceOption { serviceName = "HDFS JournalNode"; } // {
tempPath = mkOption {
type = types.path;
default = "/tmp/hadoop/httpfs";
description = ''
HTTPFS_TEMP path used by HTTPFS
'';
};
inherit restartIfChanged;
openFirewall = mkOption {
type = types.bool;
default = true;
description = ''
Open firewall ports for HTTPFS
'';
description = "HTTPFS_TEMP path used by HTTPFS";
};
};
};
config = mkMerge [
(mkIf cfg.hdfs.namenode.enable {
systemd.services.hdfs-namenode = {
description = "Hadoop HDFS NameNode";
wantedBy = [ "multi-user.target" ];
inherit (cfg.hdfs.namenode) restartIfChanged;
preStart = (mkIf cfg.hdfs.namenode.formatOnInit ''
${cfg.package}/bin/hdfs --config ${hadoopConf} namenode -format -nonInteractive || true
'');
serviceConfig = {
User = "hdfs";
SyslogIdentifier = "hdfs-namenode";
ExecStart = "${cfg.package}/bin/hdfs --config ${hadoopConf} namenode";
Restart = "always";
};
};
networking.firewall.allowedTCPPorts = (mkIf cfg.hdfs.namenode.openFirewall [
(hadoopServiceConfig {
name = "NameNode";
allowedTCPPorts = [
9870 # namenode.http-address
8020 # namenode.rpc-address
8022 # namenode. servicerpc-address
]);
8022 # namenode.servicerpc-address
8019 # dfs.ha.zkfc.port
];
preStart = (mkIf cfg.hdfs.namenode.formatOnInit
"${cfg.package}/bin/hdfs --config ${hadoopConf} namenode -format -nonInteractive || true"
);
})
(mkIf cfg.hdfs.datanode.enable {
systemd.services.hdfs-datanode = {
description = "Hadoop HDFS DataNode";
wantedBy = [ "multi-user.target" ];
inherit (cfg.hdfs.datanode) restartIfChanged;
serviceConfig = {
User = "hdfs";
SyslogIdentifier = "hdfs-datanode";
ExecStart = "${cfg.package}/bin/hdfs --config ${hadoopConf} datanode";
Restart = "always";
};
};
networking.firewall.allowedTCPPorts = (mkIf cfg.hdfs.datanode.openFirewall [
(hadoopServiceConfig {
name = "DataNode";
# port numbers for datanode changed between hadoop 2 and 3
allowedTCPPorts = if versionAtLeast cfg.package.version "3" then [
9864 # datanode.http.address
9866 # datanode.address
9867 # datanode.ipc.address
]);
] else [
50075 # datanode.http.address
50010 # datanode.address
50020 # datanode.ipc.address
];
extraConfig.services.hadoop.hdfsSiteInternal."dfs.datanode.data.dir" = let d = cfg.hdfs.datanode.dataDirs; in
if (d!= null) then (concatMapStringsSep "," (x: "["+x.type+"]file://"+x.path) cfg.hdfs.datanode.dataDirs) else d;
})
(mkIf cfg.hdfs.journalnode.enable {
systemd.services.hdfs-journalnode = {
description = "Hadoop HDFS JournalNode";
wantedBy = [ "multi-user.target" ];
inherit (cfg.hdfs.journalnode) restartIfChanged;
serviceConfig = {
User = "hdfs";
SyslogIdentifier = "hdfs-journalnode";
ExecStart = "${cfg.package}/bin/hdfs --config ${hadoopConf} journalnode";
Restart = "always";
};
};
networking.firewall.allowedTCPPorts = (mkIf cfg.hdfs.journalnode.openFirewall [
(hadoopServiceConfig {
name = "JournalNode";
allowedTCPPorts = [
8480 # dfs.journalnode.http-address
8485 # dfs.journalnode.rpc-address
]);
];
})
(mkIf cfg.hdfs.zkfc.enable {
systemd.services.hdfs-zkfc = {
description = "Hadoop HDFS ZooKeeper failover controller";
wantedBy = [ "multi-user.target" ];
inherit (cfg.hdfs.zkfc) restartIfChanged;
serviceConfig = {
User = "hdfs";
SyslogIdentifier = "hdfs-zkfc";
ExecStart = "${cfg.package}/bin/hdfs --config ${hadoopConf} zkfc";
Restart = "always";
};
};
(hadoopServiceConfig {
name = "zkfc";
description = "Hadoop HDFS ZooKeeper failover controller";
})
(mkIf cfg.hdfs.httpfs.enable {
systemd.services.hdfs-httpfs = {
description = "Hadoop httpfs";
wantedBy = [ "multi-user.target" ];
inherit (cfg.hdfs.httpfs) restartIfChanged;
environment.HTTPFS_TEMP = cfg.hdfs.httpfs.tempPath;
preStart = ''
mkdir -p $HTTPFS_TEMP
'';
serviceConfig = {
User = "httpfs";
SyslogIdentifier = "hdfs-httpfs";
ExecStart = "${cfg.package}/bin/hdfs --config ${hadoopConf} httpfs";
Restart = "always";
};
};
networking.firewall.allowedTCPPorts = (mkIf cfg.hdfs.httpfs.openFirewall [
(hadoopServiceConfig {
name = "HTTPFS";
environment.HTTPFS_TEMP = cfg.hdfs.httpfs.tempPath;
preStart = "mkdir -p $HTTPFS_TEMP";
User = "httpfs";
allowedTCPPorts = [
14000 # httpfs.http.port
]);
];
})
(mkIf (
cfg.hdfs.namenode.enable || cfg.hdfs.datanode.enable || cfg.hdfs.journalnode.enable || cfg.hdfs.zkfc.enable
) {
(mkIf cfg.gatewayRole.enable {
users.users.hdfs = {
description = "Hadoop HDFS user";
group = "hadoop";
@ -199,5 +199,6 @@ in
isSystemUser = true;
};
})
];
}

View file

@ -13,23 +13,77 @@ let
'';
default = false;
};
extraFlags = mkOption{
type = with types; listOf str;
default = [];
description = "Extra command line flags to pass to the service";
example = [
"-Dcom.sun.management.jmxremote"
"-Dcom.sun.management.jmxremote.port=8010"
];
};
extraEnv = mkOption{
type = with types; attrsOf str;
default = {};
description = "Extra environment variables";
};
in
{
options.services.hadoop.yarn = {
resourcemanager = {
enable = mkEnableOption "Whether to run the Hadoop YARN ResourceManager";
inherit restartIfChanged;
enable = mkEnableOption "Hadoop YARN ResourceManager";
inherit restartIfChanged extraFlags extraEnv;
openFirewall = mkOption {
type = types.bool;
default = true;
default = false;
description = ''
Open firewall ports for resourcemanager
'';
};
};
nodemanager = {
enable = mkEnableOption "Whether to run the Hadoop YARN NodeManager";
inherit restartIfChanged;
enable = mkEnableOption "Hadoop YARN NodeManager";
inherit restartIfChanged extraFlags extraEnv;
resource = {
cpuVCores = mkOption {
description = "Number of vcores that can be allocated for containers.";
type = with types; nullOr ints.positive;
default = null;
};
maximumAllocationVCores = mkOption {
description = "The maximum virtual CPU cores any container can be allocated.";
type = with types; nullOr ints.positive;
default = null;
};
memoryMB = mkOption {
description = "Amount of physical memory, in MB, that can be allocated for containers.";
type = with types; nullOr ints.positive;
default = null;
};
maximumAllocationMB = mkOption {
description = "The maximum physical memory any container can be allocated.";
type = with types; nullOr ints.positive;
default = null;
};
};
useCGroups = mkOption {
type = types.bool;
default = true;
description = ''
Use cgroups to enforce resource limits on containers
'';
};
localDir = mkOption {
description = "List of directories to store localized files in.";
type = with types; nullOr (listOf path);
example = [ "/var/lib/hadoop/yarn/nm" ];
default = null;
};
addBinBash = mkOption {
type = types.bool;
default = true;
@ -39,7 +93,7 @@ in
};
openFirewall = mkOption {
type = types.bool;
default = true;
default = false;
description = ''
Open firewall ports for nodemanager.
Because containers can listen on any ephemeral port, TCP ports 102465535 will be opened.
@ -49,10 +103,7 @@ in
};
config = mkMerge [
(mkIf (
cfg.yarn.resourcemanager.enable || cfg.yarn.nodemanager.enable
) {
(mkIf cfg.gatewayRole.enable {
users.users.yarn = {
description = "Hadoop YARN user";
group = "hadoop";
@ -65,15 +116,19 @@ in
description = "Hadoop YARN ResourceManager";
wantedBy = [ "multi-user.target" ];
inherit (cfg.yarn.resourcemanager) restartIfChanged;
environment = cfg.yarn.resourcemanager.extraEnv;
serviceConfig = {
User = "yarn";
SyslogIdentifier = "yarn-resourcemanager";
ExecStart = "${cfg.package}/bin/yarn --config ${hadoopConf} " +
" resourcemanager";
" resourcemanager ${escapeShellArgs cfg.yarn.resourcemanager.extraFlags}";
Restart = "always";
};
};
services.hadoop.gatewayRole.enable = true;
networking.firewall.allowedTCPPorts = (mkIf cfg.yarn.resourcemanager.openFirewall [
8088 # resourcemanager.webapp.address
8030 # resourcemanager.scheduler.address
@ -94,6 +149,7 @@ in
description = "Hadoop YARN NodeManager";
wantedBy = [ "multi-user.target" ];
inherit (cfg.yarn.nodemanager) restartIfChanged;
environment = cfg.yarn.nodemanager.extraEnv;
preStart = ''
# create log dir
@ -101,8 +157,9 @@ in
chown yarn:hadoop /var/log/hadoop/yarn/nodemanager
# set up setuid container executor binary
umount /run/wrappers/yarn-nodemanager/cgroup/cpu || true
rm -rf /run/wrappers/yarn-nodemanager/ || true
mkdir -p /run/wrappers/yarn-nodemanager/{bin,etc/hadoop}
mkdir -p /run/wrappers/yarn-nodemanager/{bin,etc/hadoop,cgroup/cpu}
cp ${cfg.package}/lib/${cfg.package.untarDir}/bin/container-executor /run/wrappers/yarn-nodemanager/bin/
chgrp hadoop /run/wrappers/yarn-nodemanager/bin/container-executor
chmod 6050 /run/wrappers/yarn-nodemanager/bin/container-executor
@ -114,11 +171,26 @@ in
SyslogIdentifier = "yarn-nodemanager";
PermissionsStartOnly = true;
ExecStart = "${cfg.package}/bin/yarn --config ${hadoopConf} " +
" nodemanager";
" nodemanager ${escapeShellArgs cfg.yarn.nodemanager.extraFlags}";
Restart = "always";
};
};
services.hadoop.gatewayRole.enable = true;
services.hadoop.yarnSiteInternal = with cfg.yarn.nodemanager; {
"yarn.nodemanager.local-dirs" = localDir;
"yarn.scheduler.maximum-allocation-vcores" = resource.maximumAllocationVCores;
"yarn.scheduler.maximum-allocation-mb" = resource.maximumAllocationMB;
"yarn.nodemanager.resource.cpu-vcores" = resource.cpuVCores;
"yarn.nodemanager.resource.memory-mb" = resource.memoryMB;
} // mkIf useCGroups {
"yarn.nodemanager.linux-container-executor.cgroups.hierarchy" = "/hadoop-yarn";
"yarn.nodemanager.linux-container-executor.resources-handler.class" = "org.apache.hadoop.yarn.server.nodemanager.util.CgroupsLCEResourcesHandler";
"yarn.nodemanager.linux-container-executor.cgroups.mount" = "true";
"yarn.nodemanager.linux-container-executor.cgroups.mount-path" = "/run/wrappers/yarn-nodemanager/cgroup";
};
networking.firewall.allowedTCPPortRanges = [
(mkIf (cfg.yarn.nodemanager.openFirewall) {from = 1024; to = 65535;})
];

View file

@ -0,0 +1,52 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.pacemaker;
in
{
# interface
options.services.pacemaker = {
enable = mkEnableOption "pacemaker";
package = mkOption {
type = types.package;
default = pkgs.pacemaker;
defaultText = literalExpression "pkgs.pacemaker";
description = "Package that should be used for pacemaker.";
};
};
# implementation
config = mkIf cfg.enable {
assertions = [ {
assertion = config.services.corosync.enable;
message = ''
Enabling services.pacemaker requires a services.corosync configuration.
'';
} ];
environment.systemPackages = [ cfg.package ];
# required by pacemaker
users.users.hacluster = {
isSystemUser = true;
group = "pacemaker";
home = "/var/lib/pacemaker";
};
users.groups.pacemaker = {};
systemd.tmpfiles.rules = [
"d /var/log/pacemaker 0700 hacluster pacemaker -"
];
systemd.packages = [ cfg.package ];
systemd.services.pacemaker = {
wantedBy = [ "multi-user.target" ];
serviceConfig = {
StateDirectory = "pacemaker";
StateDirectoryMode = "0700";
};
};
};
}

View file

@ -34,6 +34,14 @@ in
Repository to add the runner to.
Changing this option triggers a new runner registration.
IMPORTANT: If your token is org-wide (not per repository), you need to
provide a github org link, not a single repository, so do it like this
<literal>https://github.com/nixos</literal>, not like this
<literal>https://github.com/nixos/nixpkgs</literal>.
Otherwise, you are going to get a <literal>404 NotFound</literal>
from <literal>POST https://api.github.com/actions/runner-registration</literal>
in the configure script.
'';
example = "https://github.com/nixos/nixpkgs";
};

View file

@ -119,7 +119,7 @@ in {
<link linkend="opt-services.matrix-synapse.settings.listeners">listeners</link> = [
{
<link linkend="opt-services.matrix-synapse.settings.listeners._.port">port</link> = 8008;
<link linkend="opt-services.matrix-synapse.settings.listeners._.bind_addresses">bind_address</link> = [ "::1" ];
<link linkend="opt-services.matrix-synapse.settings.listeners._.bind_addresses">bind_addresses</link> = [ "::1" ];
<link linkend="opt-services.matrix-synapse.settings.listeners._.type">type</link> = "http";
<link linkend="opt-services.matrix-synapse.settings.listeners._.tls">tls</link> = false;
<link linkend="opt-services.matrix-synapse.settings.listeners._.x_forwarded">x_forwarded</link> = true;
@ -152,10 +152,10 @@ in {
<para>
If you want to run a server with public registration by anybody, you can
then enable <literal><link linkend="opt-services.matrix-synapse.settings.enable_registration">services.matrix-synapse.enable_registration</link> =
then enable <literal><link linkend="opt-services.matrix-synapse.settings.enable_registration">services.matrix-synapse.settings.enable_registration</link> =
true;</literal>. Otherwise, or you can generate a registration secret with
<command>pwgen -s 64 1</command> and set it with
<option><link linkend="opt-services.matrix-synapse.settings.registration_shared_secret">services.matrix-synapse.registration_shared_secret</link></option>.
<option><link linkend="opt-services.matrix-synapse.settings.registration_shared_secret">services.matrix-synapse.settings.registration_shared_secret</link></option>.
To create a new user or admin, run the following after you have set the secret
and have rebuilt NixOS:
<screen>

View file

@ -70,10 +70,12 @@ in
LockPersonality = true;
PrivateTmp = true;
PrivateDevices = true;
# Disabled to allow Jellyfin to access hw accel devices endpoints
# PrivateDevices = true;
PrivateUsers = true;
ProtectClock = true;
# Disabled as it does not allow Jellyfin to interface with CUDA devices
# ProtectClock = true;
ProtectControlGroups = true;
ProtectHostname = true;
ProtectKernelLogs = true;
@ -84,7 +86,7 @@ in
RestrictNamespaces = true;
# AF_NETLINK needed because Jellyfin monitors the network connection
RestrictAddressFamilies = [ "AF_NETLINK" "AF_INET" "AF_INET6" ];
RestrictAddressFamilies = [ "AF_NETLINK" "AF_INET" "AF_INET6" "AF_UNIX" ];
RestrictRealtime = true;
RestrictSUIDSGID = true;

View file

@ -112,11 +112,11 @@ in
{
imports = [
(mkRenamedOptionModule [ "nix" "useChroot" ] [ "nix" "useSandbox" ])
(mkRenamedOptionModule [ "nix" "chrootDirs" ] [ "nix" "sandboxPaths" ])
(mkRenamedOptionModule [ "nix" "daemonIONiceLevel" ] [ "nix" "daemonIOSchedPriority" ])
(mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "useChroot" ]; to = [ "nix" "useSandbox" ]; })
(mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "chrootDirs" ]; to = [ "nix" "sandboxPaths" ]; })
(mkRenamedOptionModuleWith { sinceRelease = 2205; from = [ "nix" "daemonIONiceLevel" ]; to = [ "nix" "daemonIOSchedPriority" ]; })
(mkRemovedOptionModule [ "nix" "daemonNiceLevel" ] "Consider nix.daemonCPUSchedPolicy instead.")
] ++ mapAttrsToList (oldConf: newConf: mkRenamedOptionModule [ "nix" oldConf ] [ "nix" "settings" newConf ]) legacyConfMappings;
] ++ mapAttrsToList (oldConf: newConf: mkRenamedOptionModuleWith { sinceRelease = 2205; from = [ "nix" oldConf ]; to = [ "nix" "settings" newConf ]; }) legacyConfMappings;
###### interface

View file

@ -214,6 +214,8 @@ in
User = cfg.user;
ExecStart = "${cfg.package}/bin/paperless-ng qcluster";
Restart = "on-failure";
# The `mbind` syscall is needed for running the classifier.
SystemCallFilter = defaultServiceConfig.SystemCallFilter ++ [ "mbind" ];
};
environment = env;
wantedBy = [ "multi-user.target" ];

View file

@ -29,6 +29,7 @@ let
"blackbox"
"buildkite-agent"
"collectd"
"dmarc"
"dnsmasq"
"domain"
"dovecot"
@ -55,6 +56,7 @@ let
"postfix"
"postgres"
"process"
"pve"
"py-air-control"
"redis"
"rspamd"

View file

@ -0,0 +1,117 @@
{ config, lib, pkgs, options }:
with lib;
let
cfg = config.services.prometheus.exporters.dmarc;
json = builtins.toJSON {
inherit (cfg) folders port;
listen_addr = cfg.listenAddress;
storage_path = "$STATE_DIRECTORY";
imap = (builtins.removeAttrs cfg.imap [ "passwordFile" ]) // { password = "$IMAP_PASSWORD"; use_ssl = true; };
poll_interval_seconds = cfg.pollIntervalSeconds;
deduplication_max_seconds = cfg.deduplicationMaxSeconds;
logging = {
version = 1;
disable_existing_loggers = false;
};
};
in {
port = 9797;
extraOpts = {
imap = {
host = mkOption {
type = types.str;
default = "localhost";
description = ''
Hostname of IMAP server to connect to.
'';
};
port = mkOption {
type = types.port;
default = 993;
description = ''
Port of the IMAP server to connect to.
'';
};
username = mkOption {
type = types.str;
example = "postmaster@example.org";
description = ''
Login username for the IMAP connection.
'';
};
passwordFile = mkOption {
type = types.str;
example = "/run/secrets/dovecot_pw";
description = ''
File containing the login password for the IMAP connection.
'';
};
};
folders = {
inbox = mkOption {
type = types.str;
default = "INBOX";
description = ''
IMAP mailbox that is checked for incoming DMARC aggregate reports
'';
};
done = mkOption {
type = types.str;
default = "Archive";
description = ''
IMAP mailbox that successfully processed reports are moved to.
'';
};
error = mkOption {
type = types.str;
default = "Invalid";
description = ''
IMAP mailbox that emails are moved to that could not be processed.
'';
};
};
pollIntervalSeconds = mkOption {
type = types.ints.unsigned;
default = 60;
description = ''
How often to poll the IMAP server in seconds.
'';
};
deduplicationMaxSeconds = mkOption {
type = types.ints.unsigned;
default = 604800;
defaultText = "7 days (in seconds)";
description = ''
How long individual report IDs will be remembered to avoid
counting double delivered reports twice.
'';
};
debug = mkOption {
type = types.bool;
default = false;
description = ''
Whether to declare enable <literal>--debug</literal>.
'';
};
};
serviceOpts = {
path = with pkgs; [ envsubst coreutils ];
serviceConfig = {
StateDirectory = "prometheus-dmarc-exporter";
WorkingDirectory = "/var/lib/prometheus-dmarc-exporter";
ExecStart = "${pkgs.writeShellScript "setup-cfg" ''
export IMAP_PASSWORD="$(<${cfg.imap.passwordFile})"
envsubst \
-i ${pkgs.writeText "dmarc-exporter.json.template" json} \
-o ''${STATE_DIRECTORY}/dmarc-exporter.json
exec ${pkgs.prometheus-dmarc-exporter}/bin/prometheus-dmarc-exporter \
--configuration /var/lib/prometheus-dmarc-exporter/dmarc-exporter.json \
${optionalString cfg.debug "--debug"}
''}";
};
};
}

View file

@ -0,0 +1,118 @@
{ config, lib, pkgs, options }:
with lib;
let
cfg = config.services.prometheus.exporters.pve;
# pve exporter requires a config file so create an empty one if configFile is not provided
emptyConfigFile = pkgs.writeTextFile {
name = "pve.yml";
text = "default:";
};
computedConfigFile = "${if cfg.configFile == null then emptyConfigFile else cfg.configFile}";
in
{
port = 9221;
extraOpts = {
package = mkOption {
type = types.package;
default = pkgs.prometheus-pve-exporter;
defaultText = literalExpression "pkgs.prometheus-pve-exporter";
example = literalExpression "pkgs.prometheus-pve-exporter";
description = ''
The package to use for prometheus-pve-exporter
'';
};
environmentFile = mkOption {
type = with types; nullOr path;
default = null;
example = "/etc/prometheus-pve-exporter/pve.env";
description = ''
Path to the service's environment file. This path can either be a computed path in /nix/store or a path in the local filesystem.
The environment file should NOT be stored in /nix/store as it contains passwords and/or keys in plain text.
Environment reference: https://github.com/prometheus-pve/prometheus-pve-exporter#authentication
'';
};
configFile = mkOption {
type = with types; nullOr path;
default = null;
example = "/etc/prometheus-pve-exporter/pve.yml";
description = ''
Path to the service's config file. This path can either be a computed path in /nix/store or a path in the local filesystem.
The config file should NOT be stored in /nix/store as it will contain passwords and/or keys in plain text.
If both configFile and environmentFile are provided, the configFile option will be ignored.
Configuration reference: https://github.com/prometheus-pve/prometheus-pve-exporter/#authentication
'';
};
collectors = {
status = mkOption {
type = types.bool;
default = true;
description = ''
Collect Node/VM/CT status
'';
};
version = mkOption {
type = types.bool;
default = true;
description = ''
Collect PVE version info
'';
};
node = mkOption {
type = types.bool;
default = true;
description = ''
Collect PVE node info
'';
};
cluster = mkOption {
type = types.bool;
default = true;
description = ''
Collect PVE cluster info
'';
};
resources = mkOption {
type = types.bool;
default = true;
description = ''
Collect PVE resources info
'';
};
config = mkOption {
type = types.bool;
default = true;
description = ''
Collect PVE onboot status
'';
};
};
};
serviceOpts = {
serviceConfig = {
ExecStart = ''
${cfg.package}/bin/pve_exporter \
--${if cfg.collectors.status == true then "" else "no-"}collector.status \
--${if cfg.collectors.version == true then "" else "no-"}collector.version \
--${if cfg.collectors.node == true then "" else "no-"}collector.node \
--${if cfg.collectors.cluster == true then "" else "no-"}collector.cluster \
--${if cfg.collectors.resources == true then "" else "no-"}collector.resources \
--${if cfg.collectors.config == true then "" else "no-"}collector.config \
${computedConfigFile} \
${toString cfg.port} ${cfg.listenAddress}
'';
} // optionalAttrs (cfg.environmentFile != null) {
EnvironmentFile = cfg.environmentFile;
};
};
}

View file

@ -11,7 +11,7 @@ in {
serviceConfig = {
ExecStart = ''
${pkgs.prometheus-systemd-exporter}/bin/systemd_exporter \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port}
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} ${concatStringsSep " " cfg.extraFlags}
'';
RestrictAddressFamilies = [
# Need AF_UNIX to collect data

View file

@ -76,7 +76,7 @@ in
script = ''
${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${user} \
-c 'HOME="${cfg.dataDir}" ${pkgs.amuleDaemon}/bin/amuled'
-c 'HOME="${cfg.dataDir}" ${pkgs.amule-daemon}/bin/amuled'
'';
};
};

View file

@ -0,0 +1,146 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.nbd;
configFormat = pkgs.formats.ini { };
iniFields = with types; attrsOf (oneOf [ bool int float str ]);
serverConfig = configFormat.generate "nbd-server-config"
({
generic =
(cfg.server.extraOptions // {
user = "root";
group = "root";
port = cfg.server.listenPort;
} // (optionalAttrs (cfg.server.listenAddress != null) {
listenaddr = cfg.server.listenAddress;
}));
}
// (mapAttrs
(_: { path, allowAddresses, extraOptions }:
extraOptions // {
exportname = path;
} // (optionalAttrs (allowAddresses != null) {
authfile = pkgs.writeText "authfile" (concatStringsSep "\n" allowAddresses);
}))
cfg.server.exports)
);
splitLists =
partition
(path: hasPrefix "/dev/" path)
(mapAttrsToList (_: { path, ... }: path) cfg.server.exports);
allowedDevices = splitLists.right;
boundPaths = splitLists.wrong;
in
{
options = {
services.nbd = {
server = {
enable = mkEnableOption "the Network Block Device (nbd) server";
listenPort = mkOption {
type = types.port;
default = 10809;
description = "Port to listen on. The port is NOT automatically opened in the firewall.";
};
extraOptions = mkOption {
type = iniFields;
default = {
allowlist = false;
};
description = ''
Extra options for the server. See
<citerefentry><refentrytitle>nbd-server</refentrytitle>
<manvolnum>5</manvolnum></citerefentry>.
'';
};
exports = mkOption {
description = "Files or block devices to make available over the network.";
default = { };
type = with types; attrsOf
(submodule {
options = {
path = mkOption {
type = str;
description = "File or block device to export.";
example = "/dev/sdb1";
};
allowAddresses = mkOption {
type = nullOr (listOf str);
default = null;
example = [ "10.10.0.0/24" "127.0.0.1" ];
description = "IPs and subnets that are authorized to connect for this device. If not specified, the server will allow all connections.";
};
extraOptions = mkOption {
type = iniFields;
default = {
flush = true;
fua = true;
};
description = ''
Extra options for this export. See
<citerefentry><refentrytitle>nbd-server</refentrytitle>
<manvolnum>5</manvolnum></citerefentry>.
'';
};
};
});
};
listenAddress = mkOption {
type = with types; nullOr str;
description = "Address to listen on. If not specified, the server will listen on all interfaces.";
default = null;
example = "10.10.0.1";
};
};
};
};
config = mkIf cfg.server.enable {
boot.kernelModules = [ "nbd" ];
systemd.services.nbd-server = {
after = [ "network-online.target" ];
before = [ "multi-user.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.nbd}/bin/nbd-server -C ${serverConfig}";
Type = "forking";
DeviceAllow = map (path: "${path} rw") allowedDevices;
BindPaths = boundPaths;
CapabilityBoundingSet = "";
DevicePolicy = "closed";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = false;
PrivateMounts = true;
PrivateTmp = true;
PrivateUsers = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "noaccess";
ProtectSystem = "strict";
RestrictAddressFamilies = "AF_INET AF_INET6";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
UMask = "0077";
};
};
};
}

View file

@ -194,19 +194,8 @@ let
zone.children
);
# fighting infinite recursion
zoneOptions = zoneOptionsRaw // childConfig zoneOptions1 true;
zoneOptions1 = zoneOptionsRaw // childConfig zoneOptions2 false;
zoneOptions2 = zoneOptionsRaw // childConfig zoneOptions3 false;
zoneOptions3 = zoneOptionsRaw // childConfig zoneOptions4 false;
zoneOptions4 = zoneOptionsRaw // childConfig zoneOptions5 false;
zoneOptions5 = zoneOptionsRaw // childConfig zoneOptions6 false;
zoneOptions6 = zoneOptionsRaw // childConfig null false;
childConfig = x: v: { options.children = { type = types.attrsOf x; visible = v; }; };
# options are ordered alphanumerically
zoneOptionsRaw = types.submodule {
zoneOptions = types.submodule {
options = {
allowAXFRFallback = mkOption {
@ -246,6 +235,13 @@ let
};
children = mkOption {
# TODO: This relies on the fact that `types.anything` doesn't set any
# values of its own to any defaults, because in the above zoneConfigs',
# values from children override ones from parents, but only if the
# attributes are defined. Because of this, we can't replace the element
# type here with `zoneConfigs`, since that would set all the attributes
# to default values, breaking the parent inheriting function.
type = types.attrsOf types.anything;
default = {};
description = ''
Children zones inherit all options of their parents. Attributes

View file

@ -1,6 +1,7 @@
{ config, options, lib, pkgs, stdenv, ... }:
let
cfg = config.services.pleroma;
cookieFile = "/var/lib/pleroma/.cookie";
in {
options = {
services.pleroma = with lib; {
@ -8,7 +9,7 @@ in {
package = mkOption {
type = types.package;
default = pkgs.pleroma;
default = pkgs.pleroma.override { inherit cookieFile; };
defaultText = literalExpression "pkgs.pleroma";
description = "Pleroma package to use.";
};
@ -100,7 +101,6 @@ in {
after = [ "network-online.target" "postgresql.service" ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [ config.environment.etc."/pleroma/config.exs".source ];
environment.RELEASE_COOKIE = "/var/lib/pleroma/.cookie";
serviceConfig = {
User = cfg.user;
Group = cfg.group;
@ -118,10 +118,10 @@ in {
# Better be safe than sorry migration-wise.
ExecStartPre =
let preScript = pkgs.writers.writeBashBin "pleromaStartPre" ''
if [ ! -f /var/lib/pleroma/.cookie ]
if [ ! -f "${cookieFile}" ] || [ ! -s "${cookieFile}" ]
then
echo "Creating cookie file"
dd if=/dev/urandom bs=1 count=16 | hexdump -e '16/1 "%02x"' > /var/lib/pleroma/.cookie
dd if=/dev/urandom bs=1 count=16 | ${pkgs.hexdump}/bin/hexdump -e '16/1 "%02x"' > "${cookieFile}"
fi
${cfg.package}/bin/pleroma_ctl migrate
'';

View file

@ -8,12 +8,7 @@ let
homeDir = "/var/lib/tox-node";
configFile = let
# fetchurl should be switched to getting this file from tox-node.src once
# the dpkg directory is in a release
src = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/tox-rs/tox-node/master/dpkg/config.yml";
sha256 = "1431wzpzm786mcvyzk1rp7ar418n45dr75hdggxvlm7pkpam31xa";
};
src = "${pkg.src}/dpkg/config.yml";
confJSON = pkgs.writeText "config.json" (
builtins.toJSON {
log-type = cfg.logType;

View file

@ -62,6 +62,7 @@ in {
};
stateDir = mkOption {
type = types.path;
default = "/var/lib/unbound";
description = "Directory holding all state for unbound to run.";
};

View file

@ -153,6 +153,7 @@ in
userlist = mkOption {
default = [];
type = types.listOf types.str;
description = "See <option>userlistFile</option>.";
};

View file

@ -102,17 +102,19 @@ in
# Taken from: https://github.com/oauth2-proxy/oauth2-proxy/blob/master/providers/providers.go
provider = mkOption {
type = types.enum [
"google"
"adfs"
"azure"
"bitbucket"
"digitalocean"
"facebook"
"github"
"keycloak"
"gitlab"
"google"
"keycloak"
"keycloak-oidc"
"linkedin"
"login.gov"
"bitbucket"
"nextcloud"
"digitalocean"
"oidc"
];
default = "google";

View file

@ -910,6 +910,11 @@ in
ORPort = mkForce [];
PublishServerDescriptor = mkForce false;
})
(mkIf (!cfg.client.enable) {
# Make sure application connections via SOCKS are disabled
# when services.tor.client.enable is false
SOCKSPort = mkForce [ 0 ];
})
(mkIf cfg.client.enable (
{ SOCKSPort = [ cfg.client.socksListenAddress ];
} // optionalAttrs cfg.client.transparentProxy.enable {
@ -962,7 +967,7 @@ in
'') onion.authorizedClients ++
optional (onion.secretKey != null) ''
install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path}
key="$(cut -f1 -d: ${escapeShellArg onion.secretKey})"
key="$(cut -f1 -d: ${escapeShellArg onion.secretKey} | head -1)"
case "$key" in
("== ed25519v"*"-secret")
install -o tor -g tor -m 0400 ${escapeShellArg onion.secretKey} ${escapeShellArg onion.path}/hs_ed25519_secret_key;;
@ -1008,7 +1013,11 @@ in
#InaccessiblePaths = [ "-+${runDir}/root" ];
UMask = "0066";
BindPaths = [ stateDir ];
BindReadOnlyPaths = [ storeDir "/etc" ];
BindReadOnlyPaths = [ storeDir "/etc" ] ++
optionals config.services.resolved.enable [
"/run/systemd/resolve/stub-resolv.conf"
"/run/systemd/resolve/resolv.conf"
];
AmbientCapabilities = [""] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE";
CapabilityBoundingSet = [""] ++ lib.optional bindsPrivilegedPort "CAP_NET_BIND_SERVICE";
# ProtectClock= adds DeviceAllow=char-rtc r

View file

@ -1,81 +1,73 @@
{ config, lib, pkgs, ... }:
with lib;
let
ecfg = config.services.earlyoom;
cfg = config.services.earlyoom;
inherit (lib)
mkDefault mkEnableOption mkIf mkOption types
mkRemovedOptionModule
concatStringsSep optional;
in
{
options = {
services.earlyoom = {
options.services.earlyoom = {
enable = mkEnableOption "Early out of memory killing";
enable = mkOption {
type = types.bool;
default = false;
description = ''
Enable early out of memory killing.
'';
};
freeMemThreshold = mkOption {
type = types.ints.between 1 100;
default = 10;
description = ''
Minimum of availabe memory (in percent).
If the free memory falls below this threshold and the analog is true for
<option>services.earlyoom.freeSwapThreshold</option>
the killing begins.
'';
};
freeMemThreshold = mkOption {
type = types.int;
default = 10;
description = ''
Minimum of availabe memory (in percent).
If the free memory falls below this threshold and the analog is true for
<option>services.earlyoom.freeSwapThreshold</option>
the killing begins.
'';
};
freeSwapThreshold = mkOption {
type = types.ints.between 1 100;
default = 10;
description = ''
Minimum of availabe swap space (in percent).
If the available swap space falls below this threshold and the analog
is true for <option>services.earlyoom.freeMemThreshold</option>
the killing begins.
'';
};
freeSwapThreshold = mkOption {
type = types.int;
default = 10;
description = ''
Minimum of availabe swap space (in percent).
If the available swap space falls below this threshold and the analog
is true for <option>services.earlyoom.freeMemThreshold</option>
the killing begins.
'';
};
# TODO: remove or warn after 1.7 (https://github.com/rfjakob/earlyoom/commit/7ebc4554)
ignoreOOMScoreAdjust = mkOption {
type = types.bool;
default = false;
description = ''
Ignore oom_score_adjust values of processes.
'';
};
# TODO: remove or warn after 1.7 (https://github.com/rfjakob/earlyoom/commit/7ebc4554)
ignoreOOMScoreAdjust = mkOption {
type = types.bool;
default = false;
description = ''
Ignore oom_score_adjust values of processes.
'';
};
enableDebugInfo = mkOption {
type = types.bool;
default = false;
description = ''
Enable debugging messages.
'';
};
enableDebugInfo = mkOption {
type = types.bool;
default = false;
description = ''
Enable debugging messages.
'';
};
enableNotifications = mkOption {
type = types.bool;
default = false;
description = ''
Send notifications about killed processes via the system d-bus.
notificationsCommand = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This option is deprecated and ignored by earlyoom since 1.6.
Use <option>services.earlyoom.enableNotifications</option> instead.
'';
};
WARNING: enabling this option (while convenient) should *not* be done on a
machine where you do not trust the other users as it allows any other
local user to DoS your session by spamming notifications.
enableNotifications = mkOption {
type = types.bool;
default = false;
description = ''
Send notifications about killed processes via the system d-bus.
To actually see the notifications in your GUI session, you need to have
<literal>systembus-notify</literal> running as your user.
To actually see the notifications in your GUI session, you need to have
<literal>systembus-notify</literal> running as your user which this
option handles.
See <link xlink:href="https://github.com/rfjakob/earlyoom#notifications">README</link> for details.
'';
};
See <link xlink:href="https://github.com/rfjakob/earlyoom#notifications">README</link> for details.
'';
};
};
@ -83,37 +75,30 @@ in
(mkRemovedOptionModule [ "services" "earlyoom" "useKernelOOMKiller" ] ''
This option is deprecated and ignored by earlyoom since 1.2.
'')
(mkRemovedOptionModule [ "services" "earlyoom" "notificationsCommand" ] ''
This option is deprecated and ignored by earlyoom since 1.6.
'')
];
config = mkIf ecfg.enable {
assertions = [
{ assertion = ecfg.freeMemThreshold > 0 && ecfg.freeMemThreshold <= 100;
message = "Needs to be a positive percentage"; }
{ assertion = ecfg.freeSwapThreshold > 0 && ecfg.freeSwapThreshold <= 100;
message = "Needs to be a positive percentage"; }
];
# TODO: reimplement this option as -N after 1.7 (https://github.com/rfjakob/earlyoom/commit/afe03606)
warnings = optional (ecfg.notificationsCommand != null)
"`services.earlyoom.notificationsCommand` is deprecated and ignored by earlyoom since 1.6.";
config = mkIf cfg.enable {
services.systembus-notify.enable = mkDefault cfg.enableNotifications;
systemd.services.earlyoom = {
description = "Early OOM Daemon for Linux";
wantedBy = [ "multi-user.target" ];
path = optional ecfg.enableNotifications pkgs.dbus;
path = optional cfg.enableNotifications pkgs.dbus;
serviceConfig = {
StandardOutput = "null";
StandardError = "journal";
ExecStart = concatStringsSep " " ([
"${pkgs.earlyoom}/bin/earlyoom"
"-m ${toString ecfg.freeMemThreshold}"
"-s ${toString ecfg.freeSwapThreshold}"
] ++ optional ecfg.ignoreOOMScoreAdjust "-i"
++ optional ecfg.enableDebugInfo "-d"
++ optional ecfg.enableNotifications "-n");
"-m ${toString cfg.freeMemThreshold}"
"-s ${toString cfg.freeSwapThreshold}"
]
++ optional cfg.ignoreOOMScoreAdjust "-i"
++ optional cfg.enableDebugInfo "-d"
++ optional cfg.enableNotifications "-n"
);
};
};
environment.systemPackages = optional ecfg.enableNotifications pkgs.systembus-notify;
};
}

View file

@ -0,0 +1,27 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.systembus-notify;
inherit (lib) mkEnableOption mkIf;
in
{
options.services.systembus-notify = {
enable = mkEnableOption ''
System bus notification support
WARNING: enabling this option (while convenient) should *not* be done on a
machine where you do not trust the other users as it allows any other
local user to DoS your session by spamming notifications.
'';
};
config = mkIf cfg.enable {
systemd = {
packages = with pkgs; [ systembus-notify ];
user.services.systembus-notify.wantedBy = [ "graphical-session.target" ];
};
};
}

View file

@ -1,30 +1,40 @@
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.epgstation;
opt = options.services.epgstation;
description = "EPGStation: DVR system for Mirakurun-managed TV tuners";
username = config.users.users.epgstation.name;
groupname = config.users.users.epgstation.group;
mirakurun = {
sock = config.services.mirakurun.unixSocket;
option = options.services.mirakurun.unixSocket;
};
settingsFmt = pkgs.formats.json {};
settingsTemplate = settingsFmt.generate "config.json" cfg.settings;
yaml = pkgs.formats.yaml { };
settingsTemplate = yaml.generate "config.yml" cfg.settings;
preStartScript = pkgs.writeScript "epgstation-prestart" ''
#!${pkgs.runtimeShell}
PASSWORD="$(head -n1 "${cfg.basicAuth.passwordFile}")"
DB_PASSWORD="$(head -n1 "${cfg.database.passwordFile}")"
DB_PASSWORD_FILE=${lib.escapeShellArg cfg.database.passwordFile}
if [[ ! -f "$DB_PASSWORD_FILE" ]]; then
printf "[FATAL] File containing the DB password was not found in '%s'. Double check the NixOS option '%s'." \
"$DB_PASSWORD_FILE" ${lib.escapeShellArg opt.database.passwordFile} >&2
exit 1
fi
DB_PASSWORD="$(head -n1 ${lib.escapeShellArg cfg.database.passwordFile})"
# setup configuration
touch /etc/epgstation/config.json
chmod 640 /etc/epgstation/config.json
touch /etc/epgstation/config.yml
chmod 640 /etc/epgstation/config.yml
sed \
-e "s,@password@,$PASSWORD,g" \
-e "s,@dbPassword@,$DB_PASSWORD,g" \
${settingsTemplate} > /etc/epgstation/config.json
chown "${username}:${groupname}" /etc/epgstation/config.json
${settingsTemplate} > /etc/epgstation/config.yml
chown "${username}:${groupname}" /etc/epgstation/config.yml
# NOTE: Use password authentication, since mysqljs does not yet support auth_socket
if [ ! -e /var/lib/epgstation/db-created ]; then
@ -35,7 +45,7 @@ let
'';
streamingConfig = lib.importJSON ./streaming.json;
logConfig = {
logConfig = yaml.generate "logConfig.yml" {
appenders.stdout.type = "stdout";
categories = {
default = { appenders = [ "stdout" ]; level = "info"; };
@ -45,53 +55,51 @@ let
};
};
defaultPassword = "INSECURE_GO_CHECK_CONFIGURATION_NIX\n";
# Deprecate top level options that are redundant.
deprecateTopLevelOption = config:
lib.mkRenamedOptionModule
([ "services" "epgstation" ] ++ config)
([ "services" "epgstation" "settings" ] ++ config);
removeOption = config: instruction:
lib.mkRemovedOptionModule
([ "services" "epgstation" ] ++ config)
instruction;
in
{
options.services.epgstation = {
enable = mkEnableOption "EPGStation: DTV Software in Japan";
meta.maintainers = with lib.maintainers; [ midchildan ];
usePreconfiguredStreaming = mkOption {
type = types.bool;
imports = [
(deprecateTopLevelOption [ "port" ])
(deprecateTopLevelOption [ "socketioPort" ])
(deprecateTopLevelOption [ "clientSocketioPort" ])
(removeOption [ "basicAuth" ]
"Use a TLS-terminated reverse proxy with authentication instead.")
];
options.services.epgstation = {
enable = lib.mkEnableOption description;
package = lib.mkOption {
default = pkgs.epgstation;
type = lib.types.package;
defaultText = lib.literalExpression "pkgs.epgstation";
description = "epgstation package to use";
};
usePreconfiguredStreaming = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Use preconfigured default streaming options.
Upstream defaults:
<link xlink:href="https://github.com/l3tnun/EPGStation/blob/master/config/config.sample.json"/>
<link xlink:href="https://github.com/l3tnun/EPGStation/blob/master/config/config.yml.template"/>
'';
};
port = mkOption {
type = types.port;
default = 20772;
description = ''
HTTP port for EPGStation to listen on.
'';
};
socketioPort = mkOption {
type = types.port;
default = cfg.port + 1;
defaultText = literalExpression "config.${opt.port} + 1";
description = ''
Socket.io port for EPGStation to listen on.
'';
};
clientSocketioPort = mkOption {
type = types.port;
default = cfg.socketioPort;
defaultText = literalExpression "config.${opt.socketioPort}";
description = ''
Socket.io port that the web client is going to connect to. This may be
different from <option>socketioPort</option> if EPGStation is hidden
behind a reverse proxy.
'';
};
openFirewall = mkOption {
type = types.bool;
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Open ports in the firewall for the EPGStation web interface.
@ -106,50 +114,17 @@ in
'';
};
basicAuth = {
user = mkOption {
type = with types; nullOr str;
default = null;
example = "epgstation";
description = ''
Basic auth username for EPGStation. If <literal>null</literal>, basic
auth will be disabled.
<warning>
<para>
Basic authentication has known weaknesses, the most critical being
that it sends passwords over the network in clear text. Use this
feature to control access to EPGStation within your family and
friends, but don't rely on it for security.
</para>
</warning>
'';
};
passwordFile = mkOption {
type = types.path;
default = pkgs.writeText "epgstation-password" defaultPassword;
defaultText = literalDocBook ''a file containing <literal>${defaultPassword}</literal>'';
example = "/run/keys/epgstation-password";
description = ''
A file containing the password for <option>basicAuth.user</option>.
'';
};
};
database = {
name = mkOption {
type = types.str;
database = {
name = lib.mkOption {
type = lib.types.str;
default = "epgstation";
description = ''
Name of the MySQL database that holds EPGStation's data.
'';
};
passwordFile = mkOption {
type = types.path;
default = pkgs.writeText "epgstation-db-password" defaultPassword;
defaultText = literalDocBook ''a file containing <literal>${defaultPassword}</literal>'';
passwordFile = lib.mkOption {
type = lib.types.path;
example = "/run/keys/epgstation-db-password";
description = ''
A file containing the password for the database named
@ -158,69 +133,106 @@ in
};
};
settings = mkOption {
# The defaults for some options come from the upstream template
# configuration, which is the one that users would get if they follow the
# upstream instructions. This is, in some cases, different from the
# application defaults. Some options like encodeProcessNum and
# concurrentEncodeNum doesn't have an optimal default value that works for
# all hardware setups and/or performance requirements. For those kind of
# options, the application default wouldn't always result in the expected
# out-of-the-box behavior because it's the responsibility of the user to
# configure them according to their needs. In these cases, the value in the
# upstream template configuration should serve as a "good enough" default.
settings = lib.mkOption {
description = ''
Options to add to config.json.
Options to add to config.yml.
Documentation:
<link xlink:href="https://github.com/l3tnun/EPGStation/blob/master/doc/conf-manual.md"/>
'';
default = {};
default = { };
example = {
recPriority = 20;
conflictPriority = 10;
};
type = types.submodule {
freeformType = settingsFmt.type;
type = lib.types.submodule {
freeformType = yaml.type;
options.readOnlyOnce = mkOption {
type = types.bool;
default = false;
description = "Don't reload configuration files at runtime.";
options.port = lib.mkOption {
type = lib.types.port;
default = 20772;
description = ''
HTTP port for EPGStation to listen on.
'';
};
options.mirakurunPath = mkOption (let
sockPath = config.services.mirakurun.unixSocket;
in {
type = types.str;
default = "http+unix://${replaceStrings ["/"] ["%2F"] sockPath}";
defaultText = literalExpression ''
"http+unix://''${replaceStrings ["/"] ["%2F"] config.${options.services.mirakurun.unixSocket}}"
options.socketioPort = lib.mkOption {
type = lib.types.port;
default = cfg.settings.port + 1;
defaultText = lib.literalExpression "config.${opt.settings}.port + 1";
description = ''
Socket.io port for EPGStation to listen on. It is valid to share
ports with <option>${opt.settings}.port</option>.
'';
};
options.clientSocketioPort = lib.mkOption {
type = lib.types.port;
default = cfg.settings.socketioPort;
defaultText = lib.literalExpression "config.${opt.settings}.socketioPort";
description = ''
Socket.io port that the web client is going to connect to. This may
be different from <option>${opt.settings}.socketioPort</option> if
EPGStation is hidden behind a reverse proxy.
'';
};
options.mirakurunPath = with mirakurun; lib.mkOption {
type = lib.types.str;
default = "http+unix://${lib.replaceStrings ["/"] ["%2F"] sock}";
defaultText = lib.literalExpression ''
"http+unix://''${lib.replaceStrings ["/"] ["%2F"] config.${option}}"
'';
example = "http://localhost:40772";
description = "URL to connect to Mirakurun.";
});
};
options.encode = mkOption {
type = with types; listOf attrs;
options.encodeProcessNum = lib.mkOption {
type = lib.types.ints.positive;
default = 4;
description = ''
The maximum number of processes that EPGStation would allow to run
at the same time for encoding or streaming videos.
'';
};
options.concurrentEncodeNum = lib.mkOption {
type = lib.types.ints.positive;
default = 1;
description = ''
The maximum number of encoding jobs that EPGStation would run at the
same time.
'';
};
options.encode = lib.mkOption {
type = with lib.types; listOf attrs;
description = "Encoding presets for recorded videos.";
default = [
{
name = "H264";
cmd = "${pkgs.epgstation}/libexec/enc.sh main";
name = "H.264";
cmd = "%NODE% ${cfg.package}/libexec/enc.js";
suffix = ".mp4";
default = true;
}
{
name = "H264-sub";
cmd = "${pkgs.epgstation}/libexec/enc.sh sub";
suffix = "-sub.mp4";
}
];
defaultText = literalExpression ''
defaultText = lib.literalExpression ''
[
{
name = "H264";
cmd = "''${pkgs.epgstation}/libexec/enc.sh main";
name = "H.264";
cmd = "%NODE% config.${opt.package}/libexec/enc.js";
suffix = ".mp4";
default = true;
}
{
name = "H264-sub";
cmd = "''${pkgs.epgstation}/libexec/enc.sh sub";
suffix = "-sub.mp4";
}
]
'';
@ -229,14 +241,25 @@ in
};
};
config = mkIf cfg.enable {
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = !(lib.hasAttr "readOnlyOnce" cfg.settings);
message = ''
The option config.${opt.settings}.readOnlyOnce can no longer be used
since it's been removed. No replacements are available.
'';
}
];
environment.etc = {
"epgstation/operatorLogConfig.json".text = builtins.toJSON logConfig;
"epgstation/serviceLogConfig.json".text = builtins.toJSON logConfig;
"epgstation/epgUpdaterLogConfig.yml".source = logConfig;
"epgstation/operatorLogConfig.yml".source = logConfig;
"epgstation/serviceLogConfig.yml".source = logConfig;
};
networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts = with cfg; [ port socketioPort ];
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = with cfg.settings; [ port socketioPort ];
};
users.users.epgstation = {
@ -245,13 +268,13 @@ in
isSystemUser = true;
};
users.groups.epgstation = {};
users.groups.epgstation = { };
services.mirakurun.enable = mkDefault true;
services.mirakurun.enable = lib.mkDefault true;
services.mysql = {
enable = mkDefault true;
package = mkDefault pkgs.mariadb;
enable = lib.mkDefault true;
package = lib.mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
# FIXME: enable once mysqljs supports auth_socket
# ensureUsers = [ {
@ -260,39 +283,28 @@ in
# } ];
};
services.epgstation.settings = let
defaultSettings = {
serverPort = cfg.port;
socketioPort = cfg.socketioPort;
clientSocketioPort = cfg.clientSocketioPort;
services.epgstation.settings =
let
defaultSettings = {
dbtype = lib.mkDefault "mysql";
mysql = {
socketPath = lib.mkDefault "/run/mysqld/mysqld.sock";
user = username;
password = lib.mkDefault "@dbPassword@";
database = cfg.database.name;
};
dbType = mkDefault "mysql";
mysql = {
user = username;
database = cfg.database.name;
socketPath = mkDefault "/run/mysqld/mysqld.sock";
password = mkDefault "@dbPassword@";
connectTimeout = mkDefault 1000;
connectionLimit = mkDefault 10;
ffmpeg = lib.mkDefault "${pkgs.ffmpeg-full}/bin/ffmpeg";
ffprobe = lib.mkDefault "${pkgs.ffmpeg-full}/bin/ffprobe";
# for disambiguation with TypeScript files
recordedFileExtension = lib.mkDefault ".m2ts";
};
basicAuth = mkIf (cfg.basicAuth.user != null) {
user = mkDefault cfg.basicAuth.user;
password = mkDefault "@password@";
};
ffmpeg = mkDefault "${pkgs.ffmpeg-full}/bin/ffmpeg";
ffprobe = mkDefault "${pkgs.ffmpeg-full}/bin/ffprobe";
fileExtension = mkDefault ".m2ts";
maxEncode = mkDefault 2;
maxStreaming = mkDefault 2;
};
in
mkMerge [
defaultSettings
(mkIf cfg.usePreconfiguredStreaming streamingConfig)
];
in
lib.mkMerge [
defaultSettings
(lib.mkIf cfg.usePreconfiguredStreaming streamingConfig)
];
systemd.tmpfiles.rules = [
"d '/var/lib/epgstation/streamfiles' - ${username} ${groupname} - -"
@ -301,15 +313,15 @@ in
];
systemd.services.epgstation = {
description = pkgs.epgstation.meta.description;
inherit description;
wantedBy = [ "multi-user.target" ];
after = [
"network.target"
] ++ optional config.services.mirakurun.enable "mirakurun.service"
++ optional config.services.mysql.enable "mysql.service";
after = [ "network.target" ]
++ lib.optional config.services.mirakurun.enable "mirakurun.service"
++ lib.optional config.services.mysql.enable "mysql.service";
serviceConfig = {
ExecStart = "${pkgs.epgstation}/bin/epgstation start";
ExecStart = "${cfg.package}/bin/epgstation start";
ExecStartPre = "+${preStartScript}";
User = username;
Group = groupname;

View file

@ -1,119 +1,140 @@
{
"liveHLS": [
{
"name": "720p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%"
"urlscheme": {
"m2ts": {
"ios": "vlc-x-callback://x-callback-url/stream?url=PROTOCOL://ADDRESS",
"android": "intent://ADDRESS#Intent;package=org.videolan.vlc;type=video;scheme=PROTOCOL;end"
},
{
"name": "480p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -flags +loop-global_header %OUTPUT%"
"video": {
"ios": "infuse://x-callback-url/play?url=PROTOCOL://ADDRESS",
"android": "intent://ADDRESS#Intent;package=com.mxtech.videoplayer.ad;type=video;scheme=PROTOCOL;end"
},
{
"name": "180p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -c:a aac -ar 48000 -b:a 48k -ac 2 -c:v libx264 -vf yadif,scale=-2:180 -b:v 100k -preset veryfast -maxrate 110k -bufsize 1000k -flags +loop-global_header %OUTPUT%"
"download": {
"ios": "vlc-x-callback://x-callback-url/download?url=PROTOCOL://ADDRESS&filename=FILENAME"
}
],
"liveMP4": [
{
"name": "720p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1"
}
],
"liveWebM": [
{
"name": "720p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 2 -c:a libvorbis -ar 48000 -b:a 128k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1"
}
],
"mpegTsStreaming": [
{
"name": "720p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -y -f mpegts pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -y -f mpegts pipe:1"
},
{
"name": "Original"
}
],
"mpegTsViewer": {
"ios": "vlc-x-callback://x-callback-url/stream?url=http://ADDRESS",
"android": "intent://ADDRESS#Intent;package=com.mxtech.videoplayer.ad;type=video;scheme=http;end"
},
"recordedDownloader": {
"ios": "vlc-x-callback://x-callback-url/download?url=http://ADDRESS&filename=FILENAME",
"android": "intent://ADDRESS#Intent;package=com.dv.adm;type=video;scheme=http;end"
},
"recordedStreaming": {
"webm": [
{
"name": "720p",
"cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:720 %VB% %VBUFFER% %AB% %ABUFFER% -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1",
"vb": "3000k",
"ab": "192k"
},
{
"name": "360p",
"cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 2 -c:a libvorbis -ar 48000 -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:360 %VB% %VBUFFER% %AB% %ABUFFER% -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1",
"vb": "1500k",
"ab": "128k"
"stream": {
"live": {
"ts": {
"m2ts": [
{
"name": "720p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -y -f mpegts pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -y -f mpegts pipe:1"
},
{
"name": "無変換"
}
],
"m2tsll": [
{
"name": "720p",
"cmd": "%FFMPEG% -dual_mono_mode main -f mpegts -analyzeduration 500000 -i pipe:0 -map 0 -c:s copy -c:d copy -ignore_unknown -fflags nobuffer -flags low_delay -max_delay 250000 -max_interleave_delta 1 -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -flags +cgop -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -y -f mpegts pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -dual_mono_mode main -f mpegts -analyzeduration 500000 -i pipe:0 -map 0 -c:s copy -c:d copy -ignore_unknown -fflags nobuffer -flags low_delay -max_delay 250000 -max_interleave_delta 1 -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -flags +cgop -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -y -f mpegts pipe:1"
}
],
"webm": [
{
"name": "720p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 2 -c:a libvorbis -ar 48000 -b:a 128k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1"
}
],
"mp4": [
{
"name": "720p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1"
}
],
"hls": [
{
"name": "720p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%"
},
{
"name": "480p",
"cmd": "%FFMPEG% -re -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 17 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -flags +loop-global_header %OUTPUT%"
}
]
}
],
"mp4": [
{
"name": "720p",
"cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -ac 2 -c:v libx264 -vf yadif,scale=-2:720 %VB% %VBUFFER% %AB% %ABUFFER% -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1",
"vb": "3000k",
"ab": "192k"
},
{
"name": "360p",
"cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -ac 2 -c:v libx264 -vf yadif,scale=-2:360 %VB% %VBUFFER% %AB% %ABUFFER% -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1",
"vb": "1500k",
"ab": "128k"
}
],
"mpegTs": [
{
"name": "720p (H.264)",
"cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -ac 2 -c:v libx264 -vf yadif,scale=-2:720 %VB% %VBUFFER% %AB% %ABUFFER% -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -y -f mpegts pipe:1",
"vb": "3000k",
"ab": "192k"
},
{
"name": "360p (H.264)",
"cmd": "%FFMPEG% -dual_mono_mode main %RE% -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -ac 2 -c:v libx264 -vf yadif,scale=-2:360 %VB% %VBUFFER% %AB% %ABUFFER% -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -y -f mpegts pipe:1",
"vb": "1500k",
"ab": "128k"
}
]
},
"recordedHLS": [
{
"name": "720p",
"cmd": "%FFMPEG% -dual_mono_mode main -i %INPUT% -sn -threads 0 -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%"
},
{
"name": "480p",
"cmd": "%FFMPEG% -dual_mono_mode main -i %INPUT% -sn -threads 0 -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -flags +loop-global_header %OUTPUT%"
},
{
"name": "480p(h265)",
"cmd": "%FFMPEG% -dual_mono_mode main -i %INPUT% -sn -map 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_type fmp4 -hls_fmp4_init_filename stream%streamNum%-init.mp4 -hls_segment_filename stream%streamNum%-%09d.m4s -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx265 -vf yadif,scale=-2:480 -b:v 350k -preset veryfast -tag:v hvc1 %OUTPUT%"
"recorded": {
"ts": {
"webm": [
{
"name": "720p",
"cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 128k -ac 2 -c:v libvpx-vp9 -vf yadif,scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1"
}
],
"mp4": [
{
"name": "720p",
"cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1"
}
],
"hls": [
{
"name": "720p",
"cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf yadif,scale=-2:720 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%"
},
{
"name": "480p",
"cmd": "%FFMPEG% -dual_mono_mode main -i pipe:0 -sn -map 0 -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf yadif,scale=-2:480 -b:v 1500k -preset veryfast -flags +loop-global_header %OUTPUT%"
}
]
},
"encoded": {
"webm": [
{
"name": "720p",
"cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 192k -ac 2 -c:v libvpx-vp9 -vf scale=-2:720 -b:v 3000k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 3 -c:a libvorbis -ar 48000 -b:a 128k -ac 2 -c:v libvpx-vp9 -vf scale=-2:480 -b:v 1500k -deadline realtime -speed 4 -cpu-used -8 -y -f webm pipe:1"
}
],
"mp4": [
{
"name": "720p",
"cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf scale=-2:720 -b:v 3000k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1"
},
{
"name": "480p",
"cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf scale=-2:480 -b:v 1500k -profile:v baseline -preset veryfast -tune fastdecode,zerolatency -movflags frag_keyframe+empty_moov+faststart+default_base_moof -y -f mp4 pipe:1"
}
],
"hls": [
{
"name": "720p",
"cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v libx264 -vf scale=-2:720 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%"
},
{
"name": "480p",
"cmd": "%FFMPEG% -dual_mono_mode main -ss %SS% -i %INPUT% -sn -threads 0 -ignore_unknown -max_muxing_queue_size 1024 -f hls -hls_time 3 -hls_list_size 0 -hls_allow_cache 1 -hls_segment_filename %streamFileDir%/stream%streamNum%-%09d.ts -hls_flags delete_segments -c:a aac -ar 48000 -b:a 128k -ac 2 -c:v libx264 -vf scale=-2:480 -b:v 3000k -preset veryfast -flags +loop-global_header %OUTPUT%"
}
]
}
}
],
"recordedViewer": {
"ios": "infuse://x-callback-url/play?url=http://ADDRESS",
"android": "intent://ADDRESS#Intent;package=com.mxtech.videoplayer.ad;type=video;scheme=http;end"
}
}

View file

@ -153,7 +153,7 @@ in {
package = mkOption {
type = types.package;
description = "Which package to use for the Nextcloud instance.";
relatedPackages = [ "nextcloud21" "nextcloud22" "nextcloud23" ];
relatedPackages = [ "nextcloud22" "nextcloud23" ];
};
phpPackage = mkOption {
type = types.package;
@ -571,15 +571,6 @@ in {
nextcloud defined in an overlay, please set `services.nextcloud.package` to
`pkgs.nextcloud`.
''
# 21.03 will not be an official release - it was instead 21.05.
# This versionOlder statement remains set to 21.03 for backwards compatibility.
# See https://github.com/NixOS/nixpkgs/pull/108899 and
# https://github.com/NixOS/rfcs/blob/master/rfcs/0080-nixos-release-schedule.md.
# FIXME(@Ma27) remove this else-if as soon as 21.05 is EOL! This is only here
# to ensure that users who are on Nextcloud 19 with a stateVersion <21.05 with
# no explicit services.nextcloud.package don't upgrade to v21 by accident (
# nextcloud20 throws an eval-error because it's dropped).
else if versionOlder stateVersion "21.03" then nextcloud20
else if versionOlder stateVersion "21.11" then nextcloud21
else if versionOlder stateVersion "22.05" then nextcloud22
else nextcloud23

View file

@ -20,6 +20,21 @@ in
description = "PlantUML server package to use";
};
packages = {
jdk = mkOption {
type = types.package;
default = pkgs.jdk;
defaultText = literalExpression "pkgs.jdk";
description = "JDK package to use for the server";
};
jetty = mkOption {
type = types.package;
default = pkgs.jetty;
defaultText = literalExpression "pkgs.jetty";
description = "Jetty package to use for the server";
};
};
user = mkOption {
type = types.str;
default = "plantuml";
@ -105,10 +120,10 @@ in
ALLOW_PLANTUML_INCLUDE = if cfg.allowPlantumlInclude then "true" else "false";
};
script = ''
${pkgs.jre}/bin/java \
-jar ${pkgs.jetty}/start.jar \
${cfg.packages.jdk}/bin/java \
-jar ${cfg.packages.jetty}/start.jar \
--module=deploy,http,jsp \
jetty.home=${pkgs.jetty} \
jetty.home=${cfg.packages.jetty} \
jetty.base=${cfg.package} \
jetty.http.host=${cfg.listenHost} \
jetty.http.port=${builtins.toString cfg.listenPort}

View file

@ -69,11 +69,16 @@ in
CERTIFICATE_KEY_FILE = "key.pem";
};
startLimitIntervalSec = 60;
script = ''
if [[ -v CREDENTIALS_DIRECTORY ]]; then
cd "$CREDENTIALS_DIRECTORY"
fi
exec "${pkgs.pomerium}/bin/pomerium" -config "${cfgFile}"
'';
serviceConfig = {
DynamicUser = true;
StateDirectory = [ "pomerium" ];
ExecStart = "${pkgs.pomerium}/bin/pomerium -config ${cfgFile}";
PrivateUsers = false; # breaks CAP_NET_BIND_SERVICE
MemoryDenyWriteExecute = false; # breaks LuaJIT
@ -99,7 +104,6 @@ in
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
WorkingDirectory = mkIf (cfg.useACMEHost != null) "$CREDENTIALS_DIRECTORY";
LoadCredential = optionals (cfg.useACMEHost != null) [
"fullchain.pem:/var/lib/acme/${cfg.useACMEHost}/fullchain.pem"
"key.pem:/var/lib/acme/${cfg.useACMEHost}/key.pem"
@ -124,7 +128,7 @@ in
Type = "oneshot";
TimeoutSec = 60;
ExecCondition = "/run/current-system/systemd/bin/systemctl -q is-active pomerium.service";
ExecStart = "/run/current-system/systemd/bin/systemctl restart pomerium.service";
ExecStart = "/run/current-system/systemd/bin/systemctl --no-block restart pomerium.service";
};
};
});

View file

@ -23,8 +23,8 @@ in
package = mkOption {
type = types.package;
default = pkgs.tomcat85;
defaultText = literalExpression "pkgs.tomcat85";
default = pkgs.tomcat9;
defaultText = literalExpression "pkgs.tomcat9";
example = lib.literalExpression "pkgs.tomcat9";
description = ''
Which tomcat package to use.
@ -127,7 +127,7 @@ in
webapps = mkOption {
type = types.listOf types.path;
default = [ tomcat.webapps ];
defaultText = literalExpression "[ pkgs.tomcat85.webapps ]";
defaultText = literalExpression "[ config.services.tomcat.package.webapps ]";
description = "List containing WAR files or directories with WAR files which are web applications to be deployed on Tomcat";
};
@ -201,6 +201,7 @@ in
{ uid = config.ids.uids.tomcat;
description = "Tomcat user";
home = "/homeless-shelter";
group = "tomcat";
extraGroups = cfg.extraGroups;
};

View file

@ -74,11 +74,9 @@ in
# Debugging
environment.sessionVariables.MATE_SESSION_DEBUG = mkIf cfg.debug "1";
environment.systemPackages =
pkgs.mate.basePackages ++
(pkgs.gnome.removePackagesByName
pkgs.mate.extraPackages
config.environment.mate.excludePackages) ++
environment.systemPackages = pkgs.gnome.removePackagesByName
(pkgs.mate.basePackages ++
pkgs.mate.extraPackages ++
[
pkgs.desktop-file-utils
pkgs.glib
@ -87,7 +85,8 @@ in
pkgs.xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/
pkgs.mate.mate-settings-daemon
pkgs.yelp # for 'Contents' in 'Help' menus
];
])
config.environment.mate.excludePackages;
programs.dconf.enable = true;
# Shell integration for VTE terminals

View file

@ -227,6 +227,7 @@ in
# Settings from elementary-default-settings
environment.etc."gtk-3.0/settings.ini".source = "${pkgs.pantheon.elementary-default-settings}/etc/gtk-3.0/settings.ini";
xdg.portal.enable = true;
xdg.portal.extraPortals = with pkgs.pantheon; [
elementary-files
elementary-settings-daemon

View file

@ -219,6 +219,7 @@ in
session = mkOption {
default = [];
type = types.listOf types.attrs;
example = literalExpression
''
[ { manage = "desktop";

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,7 @@ in
boot.kernelPackages = mkOption {
default = pkgs.linuxPackages;
type = types.unspecified // { merge = mergeEqualOption; };
type = types.raw;
apply = kernelPackages: kernelPackages.extend (self: super: {
kernel = super.kernel.override (originalArgs: {
inherit randstructSeed;

View file

@ -15,9 +15,12 @@ import re
import datetime
import glob
import os.path
from typing import Tuple, List, Optional
from typing import NamedTuple, List, Optional
SystemIdentifier = Tuple[Optional[str], int, Optional[str]]
class SystemIdentifier(NamedTuple):
profile: Optional[str]
generation: int
specialisation: Optional[str]
def copy_if_not_exists(source: str, dest: str) -> None:
@ -151,7 +154,14 @@ def get_generations(profile: Optional[str] = None) -> List[SystemIdentifier]:
gen_lines.pop()
configurationLimit = @configurationLimit@
configurations: List[SystemIdentifier] = [ (profile, int(line.split()[0]), None) for line in gen_lines ]
configurations = [
SystemIdentifier(
profile=profile,
generation=int(line.split()[0]),
specialisation=None
)
for line in gen_lines
]
return configurations[-configurationLimit:]
@ -160,7 +170,7 @@ def get_specialisations(profile: Optional[str], generation: int, _: Optional[str
system_dir(profile, generation, None), "specialisation")
if not os.path.exists(specialisations_dir):
return []
return [(profile, generation, spec) for spec in os.listdir(specialisations_dir)]
return [SystemIdentifier(profile, generation, spec) for spec in os.listdir(specialisations_dir)]
def remove_old_entries(gens: List[SystemIdentifier]) -> None:
@ -271,7 +281,8 @@ def main() -> None:
if os.readlink(system_dir(*gen)) == args.default_config:
write_loader_conf(*gen)
except OSError as e:
print("ignoring generation '{}' in the list of boot entries because of the following error:\n{}".format(*gen, e), file=sys.stderr)
profile = f"profile '{gen.profile}'" if gen.profile else "default profile"
print("ignoring {} in the list of boot entries because of the following error:\n{}".format(profile, e), file=sys.stderr)
for root, _, files in os.walk('@efiSysMountPoint@/efi/nixos/.extra-files', topdown=False):
relative_root = root.removeprefix("@efiSysMountPoint@/efi/nixos/.extra-files").removeprefix("/")

View file

@ -34,23 +34,6 @@ with lib;
type = types.lines;
};
boot.initrd.extraModprobeConfig = mkOption {
default = "";
example =
''
options zfs zfs_arc_max=1073741824
'';
description = ''
Does exactly the same thing as
<option>boot.extraModprobeConfig</option>, except
that the generated <filename>modprobe.conf</filename>
file is also included in the initrd.
This is useful for setting module options for kernel
modules that are loaded during early boot in the initrd.
'';
type = types.lines;
};
};
@ -67,9 +50,6 @@ with lib;
'')}
${config.boot.extraModprobeConfig}
'';
environment.etc."modprobe.d/nixos-initrd.conf".text = ''
${config.boot.initrd.extraModprobeConfig}
'';
environment.etc."modprobe.d/debian.conf".source = pkgs.kmod-debian-aliases;
environment.etc."modprobe.d/systemd.conf".source = "${pkgs.systemd}/lib/modprobe.d/systemd.conf";

View file

@ -338,9 +338,6 @@ let
{ object = pkgs.writeText "mdadm.conf" config.boot.initrd.mdadmConf;
symlink = "/etc/mdadm.conf";
}
{ object = config.environment.etc."modprobe.d/nixos-initrd.conf".source;
symlink = "/etc/modprobe.d/nixos-initrd.conf";
}
{ object = pkgs.runCommand "initrd-kmod-blacklist-ubuntu" {
src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
preferLocalBuild = true;
@ -581,7 +578,7 @@ in
else "gzip"
);
defaultText = literalDocBook "<literal>zstd</literal> if the kernel supports it (5.9+), <literal>gzip</literal> if not";
type = types.unspecified; # We don't have a function type...
type = types.either types.str (types.functionTo types.str);
description = ''
The compressor to use on the initrd image. May be any of:

View file

@ -2,7 +2,6 @@
with utils;
with systemdUtils.unitOptions;
with systemdUtils.lib;
with lib;
let
@ -11,6 +10,24 @@ let
systemd = cfg.package;
inherit (systemdUtils.lib)
makeUnit
generateUnits
makeJobScript
unitConfig
serviceConfig
mountConfig
automountConfig
commonUnitText
targetToUnit
serviceToUnit
socketToUnit
timerToUnit
pathToUnit
mountToUnit
automountToUnit
sliceToUnit;
upstreamSystemUnits =
[ # Targets.
"basic.target"
@ -63,32 +80,6 @@ let
"printer.target"
"smartcard.target"
# Login stuff.
"systemd-logind.service"
"autovt@.service"
"systemd-user-sessions.service"
"dbus-org.freedesktop.import1.service"
"dbus-org.freedesktop.machine1.service"
"dbus-org.freedesktop.login1.service"
"user@.service"
"user-runtime-dir@.service"
# Journal.
"systemd-journald.socket"
"systemd-journald@.socket"
"systemd-journald-varlink@.socket"
"systemd-journald.service"
"systemd-journald@.service"
"systemd-journal-flush.service"
"systemd-journal-catalog-update.service"
] ++ (optional (!config.boot.isContainer) "systemd-journald-audit.socket") ++ [
"systemd-journald-dev-log.socket"
"syslog.socket"
# Coredumps.
"systemd-coredump.socket"
"systemd-coredump@.service"
# Kernel module loading.
"systemd-modules-load.service"
"kmod-static-nodes.service"
@ -149,19 +140,12 @@ let
# Slices / containers.
"slices.target"
"user.slice"
"machine.slice"
"machines.target"
"systemd-importd.service"
"systemd-machined.service"
"systemd-nspawn@.service"
# Temporary file creation / cleanup.
"systemd-tmpfiles-clean.service"
"systemd-tmpfiles-clean.timer"
"systemd-tmpfiles-setup.service"
"systemd-tmpfiles-setup-dev.service"
# Misc.
"systemd-sysctl.service"
"dbus-org.freedesktop.timedate1.service"
@ -172,9 +156,6 @@ let
"systemd-hostnamed.service"
"systemd-exit.service"
"systemd-update-done.service"
] ++ optionals config.services.journald.enableHttpGateway [
"systemd-journal-gatewayd.socket"
"systemd-journal-gatewayd.service"
] ++ cfg.additionalUpstreamSystemUnits;
upstreamSystemWants =
@ -185,237 +166,6 @@ let
"timers.target.wants"
];
upstreamUserUnits = [
"app.slice"
"background.slice"
"basic.target"
"bluetooth.target"
"default.target"
"exit.target"
"graphical-session-pre.target"
"graphical-session.target"
"paths.target"
"printer.target"
"session.slice"
"shutdown.target"
"smartcard.target"
"sockets.target"
"sound.target"
"systemd-exit.service"
"systemd-tmpfiles-clean.service"
"systemd-tmpfiles-clean.timer"
"systemd-tmpfiles-setup.service"
"timers.target"
"xdg-desktop-autostart.target"
];
makeJobScript = name: text:
let
scriptName = replaceChars [ "\\" "@" ] [ "-" "_" ] (shellEscape name);
out = (pkgs.writeShellScriptBin scriptName ''
set -e
${text}
'').overrideAttrs (_: {
# The derivation name is different from the script file name
# to keep the script file name short to avoid cluttering logs.
name = "unit-script-${scriptName}";
});
in "${out}/bin/${scriptName}";
unitConfig = { config, options, ... }: {
config = {
unitConfig =
optionalAttrs (config.requires != [])
{ Requires = toString config.requires; }
// optionalAttrs (config.wants != [])
{ Wants = toString config.wants; }
// optionalAttrs (config.after != [])
{ After = toString config.after; }
// optionalAttrs (config.before != [])
{ Before = toString config.before; }
// optionalAttrs (config.bindsTo != [])
{ BindsTo = toString config.bindsTo; }
// optionalAttrs (config.partOf != [])
{ PartOf = toString config.partOf; }
// optionalAttrs (config.conflicts != [])
{ Conflicts = toString config.conflicts; }
// optionalAttrs (config.requisite != [])
{ Requisite = toString config.requisite; }
// optionalAttrs (config.restartTriggers != [])
{ X-Restart-Triggers = toString config.restartTriggers; }
// optionalAttrs (config.reloadTriggers != [])
{ X-Reload-Triggers = toString config.reloadTriggers; }
// optionalAttrs (config.description != "") {
Description = config.description; }
// optionalAttrs (config.documentation != []) {
Documentation = toString config.documentation; }
// optionalAttrs (config.onFailure != []) {
OnFailure = toString config.onFailure; }
// optionalAttrs (options.startLimitIntervalSec.isDefined) {
StartLimitIntervalSec = toString config.startLimitIntervalSec;
} // optionalAttrs (options.startLimitBurst.isDefined) {
StartLimitBurst = toString config.startLimitBurst;
};
};
};
serviceConfig = { name, config, ... }: {
config = mkMerge
[ { # Default path for systemd services. Should be quite minimal.
path = mkAfter
[ pkgs.coreutils
pkgs.findutils
pkgs.gnugrep
pkgs.gnused
systemd
];
environment.PATH = "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}";
}
(mkIf (config.preStart != "")
{ serviceConfig.ExecStartPre =
[ (makeJobScript "${name}-pre-start" config.preStart) ];
})
(mkIf (config.script != "")
{ serviceConfig.ExecStart =
makeJobScript "${name}-start" config.script + " " + config.scriptArgs;
})
(mkIf (config.postStart != "")
{ serviceConfig.ExecStartPost =
[ (makeJobScript "${name}-post-start" config.postStart) ];
})
(mkIf (config.reload != "")
{ serviceConfig.ExecReload =
makeJobScript "${name}-reload" config.reload;
})
(mkIf (config.preStop != "")
{ serviceConfig.ExecStop =
makeJobScript "${name}-pre-stop" config.preStop;
})
(mkIf (config.postStop != "")
{ serviceConfig.ExecStopPost =
makeJobScript "${name}-post-stop" config.postStop;
})
];
};
mountConfig = { config, ... }: {
config = {
mountConfig =
{ What = config.what;
Where = config.where;
} // optionalAttrs (config.type != "") {
Type = config.type;
} // optionalAttrs (config.options != "") {
Options = config.options;
};
};
};
automountConfig = { config, ... }: {
config = {
automountConfig =
{ Where = config.where;
};
};
};
commonUnitText = def: ''
[Unit]
${attrsToSection def.unitConfig}
'';
targetToUnit = name: def:
{ inherit (def) aliases wantedBy requiredBy enable;
text =
''
[Unit]
${attrsToSection def.unitConfig}
'';
};
serviceToUnit = name: def:
{ inherit (def) aliases wantedBy requiredBy enable;
text = commonUnitText def +
''
[Service]
${let env = cfg.globalEnvironment // def.environment;
in concatMapStrings (n:
let s = optionalString (env.${n} != null)
"Environment=${builtins.toJSON "${n}=${env.${n}}"}\n";
# systemd max line length is now 1MiB
# https://github.com/systemd/systemd/commit/e6dde451a51dc5aaa7f4d98d39b8fe735f73d2af
in if stringLength s >= 1048576 then throw "The value of the environment variable ${n} in systemd service ${name}.service is too long." else s) (attrNames env)}
${if def.reloadIfChanged then ''
X-ReloadIfChanged=true
'' else if !def.restartIfChanged then ''
X-RestartIfChanged=false
'' else ""}
${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"}
${attrsToSection def.serviceConfig}
'';
};
socketToUnit = name: def:
{ inherit (def) aliases wantedBy requiredBy enable;
text = commonUnitText def +
''
[Socket]
${attrsToSection def.socketConfig}
${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
${concatStringsSep "\n" (map (s: "ListenDatagram=${s}") def.listenDatagrams)}
'';
};
timerToUnit = name: def:
{ inherit (def) aliases wantedBy requiredBy enable;
text = commonUnitText def +
''
[Timer]
${attrsToSection def.timerConfig}
'';
};
pathToUnit = name: def:
{ inherit (def) aliases wantedBy requiredBy enable;
text = commonUnitText def +
''
[Path]
${attrsToSection def.pathConfig}
'';
};
mountToUnit = name: def:
{ inherit (def) aliases wantedBy requiredBy enable;
text = commonUnitText def +
''
[Mount]
${attrsToSection def.mountConfig}
'';
};
automountToUnit = name: def:
{ inherit (def) aliases wantedBy requiredBy enable;
text = commonUnitText def +
''
[Automount]
${attrsToSection def.automountConfig}
'';
};
sliceToUnit = name: def:
{ inherit (def) aliases wantedBy requiredBy enable;
text = commonUnitText def +
''
[Slice]
${attrsToSection def.sliceConfig}
'';
};
logindHandlerType = types.enum [
"ignore" "poweroff" "reboot" "halt" "kexec" "suspend"
"hibernate" "hybrid-sleep" "suspend-then-hibernate" "lock"
];
proxy_env = config.networking.proxy.envVars;
in
@ -568,26 +318,6 @@ in
'';
};
systemd.coredump.enable = mkOption {
default = true;
type = types.bool;
description = ''
Whether core dumps should be processed by
<command>systemd-coredump</command>. If disabled, core dumps
appear in the current directory of the crashing process.
'';
};
systemd.coredump.extraConfig = mkOption {
default = "";
type = types.lines;
example = "Storage=journal";
description = ''
Extra config options for systemd-coredump. See coredump.conf(5) man page
for available options.
'';
};
systemd.extraConfig = mkOption {
default = "";
type = types.lines;
@ -598,142 +328,6 @@ in
'';
};
services.journald.console = mkOption {
default = "";
type = types.str;
description = "If non-empty, write log messages to the specified TTY device.";
};
services.journald.rateLimitInterval = mkOption {
default = "30s";
type = types.str;
description = ''
Configures the rate limiting interval that is applied to all
messages generated on the system. This rate limiting is applied
per-service, so that two services which log do not interfere with
each other's limit. The value may be specified in the following
units: s, min, h, ms, us. To turn off any kind of rate limiting,
set either value to 0.
See <option>services.journald.rateLimitBurst</option> for important
considerations when setting this value.
'';
};
services.journald.rateLimitBurst = mkOption {
default = 10000;
type = types.int;
description = ''
Configures the rate limiting burst limit (number of messages per
interval) that is applied to all messages generated on the system.
This rate limiting is applied per-service, so that two services
which log do not interfere with each other's limit.
Note that the effective rate limit is multiplied by a factor derived
from the available free disk space for the journal as described on
<link xlink:href="https://www.freedesktop.org/software/systemd/man/journald.conf.html">
journald.conf(5)</link>.
Note that the total amount of logs stored is limited by journald settings
such as <literal>SystemMaxUse</literal>, which defaults to a 4 GB cap.
It is thus recommended to compute what period of time that you will be
able to store logs for when an application logs at full burst rate.
With default settings for log lines that are 100 Bytes long, this can
amount to just a few hours.
'';
};
services.journald.extraConfig = mkOption {
default = "";
type = types.lines;
example = "Storage=volatile";
description = ''
Extra config options for systemd-journald. See man journald.conf
for available options.
'';
};
services.journald.enableHttpGateway = mkOption {
default = false;
type = types.bool;
description = ''
Whether to enable the HTTP gateway to the journal.
'';
};
services.journald.forwardToSyslog = mkOption {
default = config.services.rsyslogd.enable || config.services.syslog-ng.enable;
defaultText = literalExpression "services.rsyslogd.enable || services.syslog-ng.enable";
type = types.bool;
description = ''
Whether to forward log messages to syslog.
'';
};
services.logind.extraConfig = mkOption {
default = "";
type = types.lines;
example = "IdleAction=lock";
description = ''
Extra config options for systemd-logind. See
<link xlink:href="https://www.freedesktop.org/software/systemd/man/logind.conf.html">
logind.conf(5)</link> for available options.
'';
};
services.logind.killUserProcesses = mkOption {
default = false;
type = types.bool;
description = ''
Specifies whether the processes of a user should be killed
when the user logs out. If true, the scope unit corresponding
to the session and all processes inside that scope will be
terminated. If false, the scope is "abandoned" (see
<link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.scope.html#">
systemd.scope(5)</link>), and processes are not killed.
</para>
<para>
See <link xlink:href="https://www.freedesktop.org/software/systemd/man/logind.conf.html#KillUserProcesses=">logind.conf(5)</link>
for more details.
'';
};
services.logind.lidSwitch = mkOption {
default = "suspend";
example = "ignore";
type = logindHandlerType;
description = ''
Specifies what to be done when the laptop lid is closed.
'';
};
services.logind.lidSwitchDocked = mkOption {
default = "ignore";
example = "suspend";
type = logindHandlerType;
description = ''
Specifies what to be done when the laptop lid is closed
and another screen is added.
'';
};
services.logind.lidSwitchExternalPower = mkOption {
default = config.services.logind.lidSwitch;
defaultText = literalExpression "services.logind.lidSwitch";
example = "ignore";
type = logindHandlerType;
description = ''
Specifies what to do when the laptop lid is closed and the system is
on external power. By default use the same action as specified in
services.logind.lidSwitch.
'';
};
systemd.sleep.extraConfig = mkOption {
default = "";
type = types.lines;
@ -744,95 +338,6 @@ in
'';
};
systemd.user.extraConfig = mkOption {
default = "";
type = types.lines;
example = "DefaultCPUAccounting=yes";
description = ''
Extra config options for systemd user instances. See man systemd-user.conf for
available options.
'';
};
systemd.tmpfiles.rules = mkOption {
type = types.listOf types.str;
default = [];
example = [ "d /tmp 1777 root root 10d" ];
description = ''
Rules for creation, deletion and cleaning of volatile and temporary files
automatically. See
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the exact format.
'';
};
systemd.tmpfiles.packages = mkOption {
type = types.listOf types.package;
default = [];
example = literalExpression "[ pkgs.lvm2 ]";
apply = map getLib;
description = ''
List of packages containing <command>systemd-tmpfiles</command> rules.
All files ending in .conf found in
<filename><replaceable>pkg</replaceable>/lib/tmpfiles.d</filename>
will be included.
If this folder does not exist or does not contain any files an error will be returned instead.
If a <filename>lib</filename> output is available, rules are searched there and only there.
If there is no <filename>lib</filename> output it will fall back to <filename>out</filename>
and if that does not exist either, the default output will be used.
'';
};
systemd.user.units = mkOption {
description = "Definition of systemd per-user units.";
default = {};
type = with types; attrsOf (submodule (
{ name, config, ... }:
{ options = concreteUnitOptions;
config = {
unit = mkDefault (makeUnit name config);
};
}));
};
systemd.user.paths = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
description = "Definition of systemd per-user path units.";
};
systemd.user.services = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
description = "Definition of systemd per-user service units.";
};
systemd.user.slices = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ] );
description = "Definition of systemd per-user slice units.";
};
systemd.user.sockets = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] );
description = "Definition of systemd per-user socket units.";
};
systemd.user.targets = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
description = "Definition of systemd per-user target units.";
};
systemd.user.timers = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
description = "Definition of systemd per-user timer units.";
};
systemd.additionalUpstreamSystemUnits = mkOption {
default = [ ];
type = types.listOf types.str;
@ -968,8 +473,6 @@ in
in ({
"systemd/system".source = generateUnits "system" enabledUnits enabledUpstreamSystemUnits upstreamSystemWants;
"systemd/user".source = generateUnits "user" cfg.user.units upstreamUserUnits [];
"systemd/system.conf".text = ''
[Manager]
${optionalString config.systemd.enableCgroupAccounting ''
@ -995,76 +498,17 @@ in
${config.systemd.extraConfig}
'';
"systemd/user.conf".text = ''
[Manager]
${config.systemd.user.extraConfig}
'';
"systemd/journald.conf".text = ''
[Journal]
Storage=persistent
RateLimitInterval=${config.services.journald.rateLimitInterval}
RateLimitBurst=${toString config.services.journald.rateLimitBurst}
${optionalString (config.services.journald.console != "") ''
ForwardToConsole=yes
TTYPath=${config.services.journald.console}
''}
${optionalString (config.services.journald.forwardToSyslog) ''
ForwardToSyslog=yes
''}
${config.services.journald.extraConfig}
'';
"systemd/coredump.conf".text =
''
[Coredump]
${config.systemd.coredump.extraConfig}
'';
"systemd/logind.conf".text = ''
[Login]
KillUserProcesses=${if config.services.logind.killUserProcesses then "yes" else "no"}
HandleLidSwitch=${config.services.logind.lidSwitch}
HandleLidSwitchDocked=${config.services.logind.lidSwitchDocked}
HandleLidSwitchExternalPower=${config.services.logind.lidSwitchExternalPower}
${config.services.logind.extraConfig}
'';
"systemd/sleep.conf".text = ''
[Sleep]
${config.systemd.sleep.extraConfig}
'';
# install provided sysctl snippets
"sysctl.d/50-coredump.conf".source = "${systemd}/example/sysctl.d/50-coredump.conf";
"sysctl.d/50-default.conf".source = "${systemd}/example/sysctl.d/50-default.conf";
"tmpfiles.d".source = (pkgs.symlinkJoin {
name = "tmpfiles.d";
paths = map (p: p + "/lib/tmpfiles.d") cfg.tmpfiles.packages;
postBuild = ''
for i in $(cat $pathsPath); do
(test -d "$i" && test $(ls "$i"/*.conf | wc -l) -ge 1) || (
echo "ERROR: The path '$i' from systemd.tmpfiles.packages contains no *.conf files."
exit 1
)
done
'' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) ''
rm -f $out/${removePrefix "tmpfiles.d/" name}
'') config.system.build.etc.passthru.targets;
}) + "/*";
"systemd/system-generators" = { source = hooks "generators" cfg.generators; };
"systemd/system-shutdown" = { source = hooks "shutdown" cfg.shutdown; };
});
services.dbus.enable = true;
users.users.systemd-coredump = {
uid = config.ids.uids.systemd-coredump;
group = "systemd-coredump";
};
users.groups.systemd-coredump = {};
users.users.systemd-network = {
uid = config.ids.uids.systemd-network;
group = "systemd-network";
@ -1084,36 +528,6 @@ in
unitConfig.X-StopOnReconfiguration = true;
};
systemd.tmpfiles.packages = [
# Default tmpfiles rules provided by systemd
(pkgs.runCommand "systemd-default-tmpfiles" {} ''
mkdir -p $out/lib/tmpfiles.d
cd $out/lib/tmpfiles.d
ln -s "${systemd}/example/tmpfiles.d/home.conf"
ln -s "${systemd}/example/tmpfiles.d/journal-nocow.conf"
ln -s "${systemd}/example/tmpfiles.d/static-nodes-permissions.conf"
ln -s "${systemd}/example/tmpfiles.d/systemd.conf"
ln -s "${systemd}/example/tmpfiles.d/systemd-nologin.conf"
ln -s "${systemd}/example/tmpfiles.d/systemd-nspawn.conf"
ln -s "${systemd}/example/tmpfiles.d/systemd-tmp.conf"
ln -s "${systemd}/example/tmpfiles.d/tmp.conf"
ln -s "${systemd}/example/tmpfiles.d/var.conf"
ln -s "${systemd}/example/tmpfiles.d/x11.conf"
'')
# User-specified tmpfiles rules
(pkgs.writeTextFile {
name = "nixos-tmpfiles.d";
destination = "/lib/tmpfiles.d/00-nixos.conf";
text = ''
# This file is created automatically and should not be modified.
# Please change the option systemd.tmpfiles.rules instead.
${concatStringsSep "\n" cfg.tmpfiles.rules}
'';
})
];
systemd.units =
mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
// mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
@ -1128,14 +542,6 @@ in
(v: let n = escapeSystemdPath v.where;
in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
systemd.user.units =
mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.user.paths
// mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services
// mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.user.slices
// mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.user.sockets
// mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.user.targets
// mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.user.timers;
system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled
[ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET"
"SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC"
@ -1143,11 +549,6 @@ in
"TMPFS_XATTR" "SECCOMP"
];
users.groups.systemd-journal.gid = config.ids.gids.systemd-journal;
users.users.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
users.users.systemd-journal-gateway.group = "systemd-journal-gateway";
users.groups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
# Generate timer units for all services that have a startAt value.
systemd.timers =
mapAttrs (name: service:
@ -1164,42 +565,14 @@ in
})
(filterAttrs (name: service: service.startAt != []) cfg.user.services);
systemd.sockets.systemd-journal-gatewayd.wantedBy =
optional config.services.journald.enableHttpGateway "sockets.target";
# Provide the systemd-user PAM service, required to run systemd
# user instances.
security.pam.services.systemd-user =
{ # Ensure that pam_systemd gets included. This is special-cased
# in systemd to provide XDG_RUNTIME_DIR.
startSession = true;
};
# Some overrides to upstream units.
systemd.services."systemd-backlight@".restartIfChanged = false;
systemd.services."systemd-fsck@".restartIfChanged = false;
systemd.services."systemd-fsck@".path = [ config.system.path ];
systemd.services."user@".restartIfChanged = false;
systemd.services.systemd-journal-flush.restartIfChanged = false;
systemd.services.systemd-random-seed.restartIfChanged = false;
systemd.services.systemd-remount-fs.restartIfChanged = false;
systemd.services.systemd-update-utmp.restartIfChanged = false;
systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
systemd.services.systemd-udev-settle.restartIfChanged = false; # Causes long delays in nixos-rebuild
# Restarting systemd-logind breaks X11
# - upstream commit: https://cgit.freedesktop.org/xorg/xserver/commit/?id=dc48bd653c7e101
# - systemd announcement: https://github.com/systemd/systemd/blob/22043e4317ecd2bc7834b48a6d364de76bb26d91/NEWS#L103-L112
# - this might be addressed in the future by xorg
#systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ];
systemd.services.systemd-logind.restartIfChanged = false;
systemd.services.systemd-logind.stopIfChanged = false;
# The user-runtime-dir@ service is managed by systemd-logind we should not touch it or else we break the users' sessions.
systemd.services."user-runtime-dir@".stopIfChanged = false;
systemd.services."user-runtime-dir@".restartIfChanged = false;
systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
systemd.services.systemd-journald.stopIfChanged = false;
systemd.services."systemd-journald@".restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
systemd.services."systemd-journald@".stopIfChanged = false;
systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
systemd.targets.network-online.wantedBy = [ "multi-user.target" ];
@ -1210,8 +583,6 @@ in
systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container";
systemd.services.systemd-random-seed.unitConfig.ConditionVirtualization = "!container";
boot.kernel.sysctl."kernel.core_pattern" = mkIf (!cfg.coredump.enable) "core";
# Increase numeric PID range (set directly instead of copying a one-line file from systemd)
# https://github.com/systemd/systemd/pull/12226
boot.kernel.sysctl."kernel.pid_max" = mkIf pkgs.stdenv.is64bit (lib.mkDefault 4194304);

View file

@ -0,0 +1,57 @@
{ config, lib, pkgs, utils, ... }:
with lib;
let
cfg = config.systemd.coredump;
systemd = config.systemd.package;
in {
options = {
systemd.coredump.enable = mkOption {
default = true;
type = types.bool;
description = ''
Whether core dumps should be processed by
<command>systemd-coredump</command>. If disabled, core dumps
appear in the current directory of the crashing process.
'';
};
systemd.coredump.extraConfig = mkOption {
default = "";
type = types.lines;
example = "Storage=journal";
description = ''
Extra config options for systemd-coredump. See coredump.conf(5) man page
for available options.
'';
};
};
config = {
systemd.additionalUpstreamSystemUnits = [
"systemd-coredump.socket"
"systemd-coredump@.service"
];
environment.etc = {
"systemd/coredump.conf".text =
''
[Coredump]
${cfg.extraConfig}
'';
# install provided sysctl snippets
"sysctl.d/50-coredump.conf".source = "${systemd}/example/sysctl.d/50-coredump.conf";
"sysctl.d/50-default.conf".source = "${systemd}/example/sysctl.d/50-default.conf";
};
users.users.systemd-coredump = {
uid = config.ids.uids.systemd-coredump;
group = "systemd-coredump";
};
users.groups.systemd-coredump = {};
boot.kernel.sysctl."kernel.core_pattern" = mkIf (!cfg.enable) "core";
};
}

View file

@ -0,0 +1,131 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.journald;
in {
options = {
services.journald.console = mkOption {
default = "";
type = types.str;
description = "If non-empty, write log messages to the specified TTY device.";
};
services.journald.rateLimitInterval = mkOption {
default = "30s";
type = types.str;
description = ''
Configures the rate limiting interval that is applied to all
messages generated on the system. This rate limiting is applied
per-service, so that two services which log do not interfere with
each other's limit. The value may be specified in the following
units: s, min, h, ms, us. To turn off any kind of rate limiting,
set either value to 0.
See <option>services.journald.rateLimitBurst</option> for important
considerations when setting this value.
'';
};
services.journald.rateLimitBurst = mkOption {
default = 10000;
type = types.int;
description = ''
Configures the rate limiting burst limit (number of messages per
interval) that is applied to all messages generated on the system.
This rate limiting is applied per-service, so that two services
which log do not interfere with each other's limit.
Note that the effective rate limit is multiplied by a factor derived
from the available free disk space for the journal as described on
<link xlink:href="https://www.freedesktop.org/software/systemd/man/journald.conf.html">
journald.conf(5)</link>.
Note that the total amount of logs stored is limited by journald settings
such as <literal>SystemMaxUse</literal>, which defaults to a 4 GB cap.
It is thus recommended to compute what period of time that you will be
able to store logs for when an application logs at full burst rate.
With default settings for log lines that are 100 Bytes long, this can
amount to just a few hours.
'';
};
services.journald.extraConfig = mkOption {
default = "";
type = types.lines;
example = "Storage=volatile";
description = ''
Extra config options for systemd-journald. See man journald.conf
for available options.
'';
};
services.journald.enableHttpGateway = mkOption {
default = false;
type = types.bool;
description = ''
Whether to enable the HTTP gateway to the journal.
'';
};
services.journald.forwardToSyslog = mkOption {
default = config.services.rsyslogd.enable || config.services.syslog-ng.enable;
defaultText = literalExpression "services.rsyslogd.enable || services.syslog-ng.enable";
type = types.bool;
description = ''
Whether to forward log messages to syslog.
'';
};
};
config = {
systemd.additionalUpstreamSystemUnits = [
"systemd-journald.socket"
"systemd-journald@.socket"
"systemd-journald-varlink@.socket"
"systemd-journald.service"
"systemd-journald@.service"
"systemd-journal-flush.service"
"systemd-journal-catalog-update.service"
] ++ (optional (!config.boot.isContainer) "systemd-journald-audit.socket") ++ [
"systemd-journald-dev-log.socket"
"syslog.socket"
] ++ optionals cfg.enableHttpGateway [
"systemd-journal-gatewayd.socket"
"systemd-journal-gatewayd.service"
];
environment.etc = {
"systemd/journald.conf".text = ''
[Journal]
Storage=persistent
RateLimitInterval=${cfg.rateLimitInterval}
RateLimitBurst=${toString cfg.rateLimitBurst}
${optionalString (cfg.console != "") ''
ForwardToConsole=yes
TTYPath=${cfg.console}
''}
${optionalString (cfg.forwardToSyslog) ''
ForwardToSyslog=yes
''}
${cfg.extraConfig}
'';
};
users.groups.systemd-journal.gid = config.ids.gids.systemd-journal;
users.users.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
users.users.systemd-journal-gateway.group = "systemd-journal-gateway";
users.groups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
systemd.sockets.systemd-journal-gatewayd.wantedBy =
optional cfg.enableHttpGateway "sockets.target";
systemd.services.systemd-journal-flush.restartIfChanged = false;
systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
systemd.services.systemd-journald.stopIfChanged = false;
systemd.services."systemd-journald@".restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
systemd.services."systemd-journald@".stopIfChanged = false;
};
}

View file

@ -0,0 +1,114 @@
{ config, lib, pkgs, utils, ... }:
with lib;
let
cfg = config.services.logind;
logindHandlerType = types.enum [
"ignore" "poweroff" "reboot" "halt" "kexec" "suspend"
"hibernate" "hybrid-sleep" "suspend-then-hibernate" "lock"
];
in
{
options = {
services.logind.extraConfig = mkOption {
default = "";
type = types.lines;
example = "IdleAction=lock";
description = ''
Extra config options for systemd-logind. See
<link xlink:href="https://www.freedesktop.org/software/systemd/man/logind.conf.html">
logind.conf(5)</link> for available options.
'';
};
services.logind.killUserProcesses = mkOption {
default = false;
type = types.bool;
description = ''
Specifies whether the processes of a user should be killed
when the user logs out. If true, the scope unit corresponding
to the session and all processes inside that scope will be
terminated. If false, the scope is "abandoned" (see
<link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.scope.html#">
systemd.scope(5)</link>), and processes are not killed.
</para>
<para>
See <link xlink:href="https://www.freedesktop.org/software/systemd/man/logind.conf.html#KillUserProcesses=">logind.conf(5)</link>
for more details.
'';
};
services.logind.lidSwitch = mkOption {
default = "suspend";
example = "ignore";
type = logindHandlerType;
description = ''
Specifies what to be done when the laptop lid is closed.
'';
};
services.logind.lidSwitchDocked = mkOption {
default = "ignore";
example = "suspend";
type = logindHandlerType;
description = ''
Specifies what to be done when the laptop lid is closed
and another screen is added.
'';
};
services.logind.lidSwitchExternalPower = mkOption {
default = cfg.lidSwitch;
defaultText = literalExpression "services.logind.lidSwitch";
example = "ignore";
type = logindHandlerType;
description = ''
Specifies what to do when the laptop lid is closed and the system is
on external power. By default use the same action as specified in
services.logind.lidSwitch.
'';
};
};
config = {
systemd.additionalUpstreamSystemUnits = [
"systemd-logind.service"
"autovt@.service"
"systemd-user-sessions.service"
"dbus-org.freedesktop.import1.service"
"dbus-org.freedesktop.machine1.service"
"dbus-org.freedesktop.login1.service"
"user@.service"
"user-runtime-dir@.service"
];
environment.etc = {
"systemd/logind.conf".text = ''
[Login]
KillUserProcesses=${if cfg.killUserProcesses then "yes" else "no"}
HandleLidSwitch=${cfg.lidSwitch}
HandleLidSwitchDocked=${cfg.lidSwitchDocked}
HandleLidSwitchExternalPower=${cfg.lidSwitchExternalPower}
${cfg.extraConfig}
'';
};
# Restarting systemd-logind breaks X11
# - upstream commit: https://cgit.freedesktop.org/xorg/xserver/commit/?id=dc48bd653c7e101
# - systemd announcement: https://github.com/systemd/systemd/blob/22043e4317ecd2bc7834b48a6d364de76bb26d91/NEWS#L103-L112
# - this might be addressed in the future by xorg
#systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ];
systemd.services.systemd-logind.restartIfChanged = false;
systemd.services.systemd-logind.stopIfChanged = false;
# The user-runtime-dir@ service is managed by systemd-logind we should not touch it or else we break the users' sessions.
systemd.services."user-runtime-dir@".stopIfChanged = false;
systemd.services."user-runtime-dir@".restartIfChanged = false;
};
}

View file

@ -0,0 +1,104 @@
{ config, lib, pkgs, utils, ... }:
with lib;
let
cfg = config.systemd.tmpfiles;
systemd = config.systemd.package;
in
{
options = {
systemd.tmpfiles.rules = mkOption {
type = types.listOf types.str;
default = [];
example = [ "d /tmp 1777 root root 10d" ];
description = ''
Rules for creation, deletion and cleaning of volatile and temporary files
automatically. See
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the exact format.
'';
};
systemd.tmpfiles.packages = mkOption {
type = types.listOf types.package;
default = [];
example = literalExpression "[ pkgs.lvm2 ]";
apply = map getLib;
description = ''
List of packages containing <command>systemd-tmpfiles</command> rules.
All files ending in .conf found in
<filename><replaceable>pkg</replaceable>/lib/tmpfiles.d</filename>
will be included.
If this folder does not exist or does not contain any files an error will be returned instead.
If a <filename>lib</filename> output is available, rules are searched there and only there.
If there is no <filename>lib</filename> output it will fall back to <filename>out</filename>
and if that does not exist either, the default output will be used.
'';
};
};
config = {
systemd.additionalUpstreamSystemUnits = [
"systemd-tmpfiles-clean.service"
"systemd-tmpfiles-clean.timer"
"systemd-tmpfiles-setup.service"
"systemd-tmpfiles-setup-dev.service"
];
systemd.additionalUpstreamUserUnits = [
"systemd-tmpfiles-clean.service"
"systemd-tmpfiles-clean.timer"
"systemd-tmpfiles-setup.service"
];
environment.etc = {
"tmpfiles.d".source = (pkgs.symlinkJoin {
name = "tmpfiles.d";
paths = map (p: p + "/lib/tmpfiles.d") cfg.packages;
postBuild = ''
for i in $(cat $pathsPath); do
(test -d "$i" && test $(ls "$i"/*.conf | wc -l) -ge 1) || (
echo "ERROR: The path '$i' from systemd.tmpfiles.packages contains no *.conf files."
exit 1
)
done
'' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) ''
rm -f $out/${removePrefix "tmpfiles.d/" name}
'') config.system.build.etc.passthru.targets;
}) + "/*";
};
systemd.tmpfiles.packages = [
# Default tmpfiles rules provided by systemd
(pkgs.runCommand "systemd-default-tmpfiles" {} ''
mkdir -p $out/lib/tmpfiles.d
cd $out/lib/tmpfiles.d
ln -s "${systemd}/example/tmpfiles.d/home.conf"
ln -s "${systemd}/example/tmpfiles.d/journal-nocow.conf"
ln -s "${systemd}/example/tmpfiles.d/static-nodes-permissions.conf"
ln -s "${systemd}/example/tmpfiles.d/systemd.conf"
ln -s "${systemd}/example/tmpfiles.d/systemd-nologin.conf"
ln -s "${systemd}/example/tmpfiles.d/systemd-nspawn.conf"
ln -s "${systemd}/example/tmpfiles.d/systemd-tmp.conf"
ln -s "${systemd}/example/tmpfiles.d/tmp.conf"
ln -s "${systemd}/example/tmpfiles.d/var.conf"
ln -s "${systemd}/example/tmpfiles.d/x11.conf"
'')
# User-specified tmpfiles rules
(pkgs.writeTextFile {
name = "nixos-tmpfiles.d";
destination = "/lib/tmpfiles.d/00-nixos.conf";
text = ''
# This file is created automatically and should not be modified.
# Please change the option systemd.tmpfiles.rules instead.
${concatStringsSep "\n" cfg.rules}
'';
})
];
};
}

View file

@ -0,0 +1,158 @@
{ config, lib, pkgs, utils, ... }:
with utils;
with systemdUtils.unitOptions;
with lib;
let
cfg = config.systemd.user;
systemd = config.systemd.package;
inherit
(systemdUtils.lib)
makeUnit
generateUnits
makeJobScript
unitConfig
serviceConfig
commonUnitText
targetToUnit
serviceToUnit
socketToUnit
timerToUnit
pathToUnit;
upstreamUserUnits = [
"app.slice"
"background.slice"
"basic.target"
"bluetooth.target"
"default.target"
"exit.target"
"graphical-session-pre.target"
"graphical-session.target"
"paths.target"
"printer.target"
"session.slice"
"shutdown.target"
"smartcard.target"
"sockets.target"
"sound.target"
"systemd-exit.service"
"timers.target"
"xdg-desktop-autostart.target"
] ++ config.systemd.additionalUpstreamUserUnits;
in {
options = {
systemd.user.extraConfig = mkOption {
default = "";
type = types.lines;
example = "DefaultCPUAccounting=yes";
description = ''
Extra config options for systemd user instances. See man systemd-user.conf for
available options.
'';
};
systemd.user.units = mkOption {
description = "Definition of systemd per-user units.";
default = {};
type = with types; attrsOf (submodule (
{ name, config, ... }:
{ options = concreteUnitOptions;
config = {
unit = mkDefault (makeUnit name config);
};
}));
};
systemd.user.paths = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
description = "Definition of systemd per-user path units.";
};
systemd.user.services = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
description = "Definition of systemd per-user service units.";
};
systemd.user.slices = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ] );
description = "Definition of systemd per-user slice units.";
};
systemd.user.sockets = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] );
description = "Definition of systemd per-user socket units.";
};
systemd.user.targets = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
description = "Definition of systemd per-user target units.";
};
systemd.user.timers = mkOption {
default = {};
type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
description = "Definition of systemd per-user timer units.";
};
systemd.additionalUpstreamUserUnits = mkOption {
default = [];
type = types.listOf types.str;
example = [];
description = ''
Additional units shipped with systemd that should be enabled for per-user systemd instances.
'';
internal = true;
};
};
config = {
systemd.additionalUpstreamSystemUnits = [
"user.slice"
];
environment.etc = {
"systemd/user".source = generateUnits "user" cfg.units upstreamUserUnits [];
"systemd/user.conf".text = ''
[Manager]
${cfg.extraConfig}
'';
};
systemd.user.units =
mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
// mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
// mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices
// mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
// mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets
// mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers;
# Generate timer units for all services that have a startAt value.
systemd.user.timers =
mapAttrs (name: service: {
wantedBy = ["timers.target"];
timerConfig.OnCalendar = service.startAt;
})
(filterAttrs (name: service: service.startAt != []) cfg.services);
# Provide the systemd-user PAM service, required to run systemd
# user instances.
security.pam.services.systemd-user =
{ # Ensure that pam_systemd gets included. This is special-cased
# in systemd to provide XDG_RUNTIME_DIR.
startSession = true;
};
# Some overrides to upstream units.
systemd.services."user@".restartIfChanged = false;
systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
};
}

View file

@ -80,6 +80,7 @@ in {
Reboot the system into the new generation instead of a switch
if the new generation uses a different kernel, kernel modules
or initrd than the booted system.
See <option>rebootWindow</option> for configuring the times at which a reboot is allowed.
'';
};
@ -96,6 +97,32 @@ in {
'';
};
rebootWindow = mkOption {
description = ''
Define a lower and upper time value (in HH:MM format) which
constitute a time window during which reboots are allowed after an upgrade.
This option only has an effect when <option>allowReboot</option> is enabled.
The default value of <literal>null</literal> means that reboots are allowed at any time.
'';
default = null;
example = { lower = "01:00"; upper = "05:00"; };
type = with types; nullOr (submodule {
options = {
lower = mkOption {
description = "Lower limit of the reboot window";
type = types.strMatching "[[:digit:]]{2}:[[:digit:]]{2}";
example = "01:00";
};
upper = mkOption {
description = "Upper limit of the reboot window";
type = types.strMatching "[[:digit:]]{2}:[[:digit:]]{2}";
example = "05:00";
};
};
});
};
};
};
@ -110,12 +137,10 @@ in {
}];
system.autoUpgrade.flags = (if cfg.flake == null then
[ "--no-build-output" ] ++ (if cfg.channel == null then
[ "--upgrade" ]
else [
[ "--no-build-output" ] ++ optionals (cfg.channel != null) [
"-I"
"nixpkgs=${cfg.channel}/nixexprs.tar.xz"
])
]
else
[ "--flake ${cfg.flake}" ]);
@ -143,19 +168,52 @@ in {
];
script = let
nixos-rebuild =
"${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
date = "${pkgs.coreutils}/bin/date";
readlink = "${pkgs.coreutils}/bin/readlink";
shutdown = "${pkgs.systemd}/bin/shutdown";
upgradeFlag = optional (cfg.channel == null) "--upgrade";
in if cfg.allowReboot then ''
${nixos-rebuild} boot ${toString cfg.flags}
booted="$(readlink /run/booted-system/{initrd,kernel,kernel-modules})"
built="$(readlink /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})"
if [ "$booted" = "$built" ]; then
${nixos-rebuild} boot ${toString (cfg.flags ++ upgradeFlag)}
booted="$(${readlink} /run/booted-system/{initrd,kernel,kernel-modules})"
built="$(${readlink} /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})"
${optionalString (cfg.rebootWindow != null) ''
current_time="$(${date} +%H:%M)"
lower="${cfg.rebootWindow.lower}"
upper="${cfg.rebootWindow.upper}"
if [[ "''${lower}" < "''${upper}" ]]; then
if [[ "''${current_time}" > "''${lower}" ]] && \
[[ "''${current_time}" < "''${upper}" ]]; then
do_reboot="true"
else
do_reboot="false"
fi
else
# lower > upper, so we are crossing midnight (e.g. lower=23h, upper=6h)
# we want to reboot if cur > 23h or cur < 6h
if [[ "''${current_time}" < "''${upper}" ]] || \
[[ "''${current_time}" > "''${lower}" ]]; then
do_reboot="true"
else
do_reboot="false"
fi
fi
''}
if [ "''${booted}" = "''${built}" ]; then
${nixos-rebuild} switch ${toString cfg.flags}
${optionalString (cfg.rebootWindow != null) ''
elif [ "''${do_reboot}" != true ]; then
echo "Outside of configured reboot window, skipping."
''}
else
/run/current-system/sw/bin/shutdown -r +1
${shutdown} -r +1
fi
'' else ''
${nixos-rebuild} switch ${toString cfg.flags}
${nixos-rebuild} switch ${toString (cfg.flags ++ upgradeFlag)}
'';
startAt = cfg.dates;
@ -167,3 +225,4 @@ in {
};
}

View file

@ -1021,6 +1021,12 @@ in
dev = "enp4s0f0";
type = "tap";
};
gre6Tunnel = {
remote = "fd7a:5634::1";
local = "fd7a:5634::2";
dev = "enp4s0f0";
type = "tun6";
};
}
'';
description = ''
@ -1058,10 +1064,15 @@ in
};
type = mkOption {
type = with types; enum [ "tun" "tap" ];
type = with types; enum [ "tun" "tap" "tun6" "tap6" ];
default = "tap";
example = "tap";
apply = v: if v == "tun" then "gre" else "gretap";
apply = v: {
tun = "gre";
tap = "gretap";
tun6 = "ip6gre";
tap6 = "ip6gretap";
}.${v};
description = ''
Whether the tunnel routes layer 2 (tap) or layer 3 (tun) traffic.
'';

View file

@ -22,11 +22,13 @@ let
type = with types; nullOr package;
default = null;
description = ''
Path to an image file to load instead of pulling from a registry.
If defined, do not pull from registry.
Path to an image file to load before running the image. This can
be used to bypass pulling the image from the registry.
You still need to set the <literal>image</literal> attribute, as it
will be used as the image name for docker to start a container.
The <literal>image</literal> attribute must match the name and
tag of the image contained in this file, as they will be used to
run the container with that image. If they do not match, the
image will be pulled from the registry as usual.
'';
example = literalExpression "pkgs.dockerTools.buildImage {...};";
};