summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMalte Voos <malte@malvo.org>2022-05-13 13:21:18 +0200
committerMalte Voos <malte@malvo.org>2022-05-13 13:21:18 +0200
commitc93eae7878325927cd11489de40be35c907c84ca (patch)
treedb9cd2aafa76c219b827283178e3211f7b54c319
parent785f9e04b123b8fd3b5a6fd0901f4ac5177fd752 (diff)
downloads6-rc.nix-c93eae7878325927cd11489de40be35c907c84ca.tar.gz
s6-rc.nix-c93eae7878325927cd11489de40be35c907c84ca.zip
nixos module for non-nixos systems (s6-rc config only)
-rw-r--r--flake.nix2
-rw-r--r--module.nix308
2 files changed, 116 insertions, 194 deletions
diff --git a/flake.nix b/flake.nix
index 8c4dc5c..0a1e899 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,5 +1,5 @@
{
- outputs = { ... }: {
+ outputs = { self }: {
nixosModule = import ./module.nix;
};
}
diff --git a/module.nix b/module.nix
index bd607c8..928f9d7 100644
--- a/module.nix
+++ b/module.nix
@@ -1,33 +1,31 @@
-{ config, lib, pkgs, ... }:
-with lib;
+{ config, pkgs, lib, ... }:
+with pkgs.lib;
let
writeExeclineScript = name: text: pkgs.writeScript name ''
#!${pkgs.execline}/bin/execlineb -P
${text}
'';
- cfg = config.s6;
+ cfg = config;
- # -------- configuration for s6-rc -------- #
+ logNames = flatten (mapAttrsToList (_: sv: optional (sv.log != null) sv.log) cfg.longruns);
- loggerNames = flatten (mapAttrsToList (_: srv: optional (srv.logger != null) srv.logger) cfg.longruns);
-
- loggingRelations = genAttrs loggerNames
- (loggerName: filter
- (serviceName: cfg.longruns.${serviceName}.logger == loggerName)
+ loggingRelations = genAttrs logNames
+ (logName: filter
+ (serviceName: cfg.longruns.${serviceName}.log == logName)
(attrNames cfg.longruns));
- loggerLongruns = mapAttrs'
- (loggerName: producers:
- nameValuePair "${loggerName}-log" {
+ logLongruns = mapAttrs'
+ (logName: producers:
+ nameValuePair "${logName}-log" {
run = ''
- s6-setuidgid ${cfg.logUser}
- exec -c
- s6-log -d 3 -- ${cfg.loggingScript loggerName}
+ ${pkgs.s6}/bin/s6-setuidgid "${cfg.logUser}"
+ ${pkgs.execline}/bin/exec -c
+ ${pkgs.s6}/bin/s6-log -d 3 -- ${cfg.loggingScript logName}
'';
notificationFd = 3;
consumerFor = producers;
- pipelineName = "${loggerName}-pipeline";
+ pipelineName = "${logName}-pipeline";
flagEssential = false;
timeoutUp = null;
timeoutDown = null;
@@ -38,92 +36,92 @@ let
maxDeathTally = null;
downSignal = null;
producerFor = null;
- logger = null;
+ log = null;
})
loggingRelations;
- allLongruns = cfg.longruns // loggerLongruns;
+ allLongruns = cfg.longruns // logLongruns;
- makeCommonConfig = srv: ''
+ makeCommonConfig = sv: ''
mkdir -p $out
- '' + optionalString srv.flagEssential ''
+ '' + optionalString sv.flagEssential ''
touch $out/flagEssential
'';
- makeBundleConfig = srv: makeCommonConfig srv + ''
+ makeBundleConfig = sv: makeCommonConfig sv + ''
echo bundle > $out/type
mkdir $out/contents.d
'' + concatMapStringsSep "\n"
- (srvName: ''touch "$out/contents.d/${srvName}"'')
- srv.contents + "\n";
-
- makeAtomicConfig = srv: makeCommonConfig srv
- + optionalString (srv.timeoutUp != null) ''
- echo "${toString srv.timeoutUp}" > $out/timeout-up
- '' + optionalString (srv.timeoutDown != null) ''
- echo "${toString srv.timeoutDown}" > $out/timeout-down
- '' + optionalString (srv.dependencies != null) (
+ (svName: ''touch "$out/contents.d/${svName}"'')
+ sv.contents + "\n";
+
+ makeAtomicConfig = sv: makeCommonConfig sv
+ + optionalString (sv.timeoutUp != null) ''
+ echo "${toString sv.timeoutUp}" > $out/timeout-up
+ '' + optionalString (sv.timeoutDown != null) ''
+ echo "${toString sv.timeoutDown}" > $out/timeout-down
+ '' + optionalString (sv.dependencies != null) (
''
mkdir $out/dependencies.d
'' + concatMapStringsSep "\n"
- (srvName: ''touch "$out/dependencies.d/${srvName}"'')
- srv.dependencies + "\n"
+ (svName: ''touch "$out/dependencies.d/${svName}"'')
+ sv.dependencies + "\n"
);
- makeOneshotConfig = srv:
+ makeOneshotConfig = sv:
let
- upScript = pkgs.writeText "up-script" srv.up;
- downScript = mapNullable (pkgs.writeText "down-script") srv.down;
+ upScript = pkgs.writeText "up-script" sv.up;
+ downScript = mapNullable (pkgs.writeText "down-script") sv.down;
in
- makeAtomicConfig srv + ''
+ makeAtomicConfig sv + ''
echo oneshot > $out/type
cp ${upScript} $out/up
'' + optionalString (downScript != null) ''
cp ${downScript} $out/down
'';
- makeLongrunConfig = srv:
+ makeLongrunConfig = sv:
let
- runScript = writeExeclineScript "run-script" srv.run;
- finishScript = mapNullable (writeExeclineScript "finish-script") srv.finish;
+ runScript = writeExeclineScript "run-script" sv.run;
+ finishScript = mapNullable (writeExeclineScript "finish-script") sv.finish;
in
- makeAtomicConfig srv + ''
+ makeAtomicConfig sv + ''
echo longrun > $out/type
cp ${runScript} $out/run
'' + optionalString (finishScript != null) ''
cp ${finishScript} $out/finish
- '' + optionalString (srv.notificationFd != null) ''
- echo "${toString srv.notificationFd}" > $out/notification-fd
- '' + optionalString (srv.timeoutKill != null) ''
- echo "${toString srv.timeoutKill}" > $out/timeout-kill
- '' + optionalString (srv.timeoutFinish != null) ''
- echo "${toString srv.timeoutFinish}" > $out/timeout-finish
- '' + optionalString (srv.maxDeathTally != null) ''
- echo "${toString srv.maxDeathTally}" > $out/max-death-tally
- '' + optionalString (srv.downSignal != null) ''
- echo "${srv.downSignal}" > $out/down-signal
- '' + optionalString (srv.producerFor != null) ''
- echo "${srv.producerFor}" > $out/producer-for
- '' + optionalString (srv.consumerFor != null) ''
- echo "${concatStringsSep "\n" srv.consumerFor}" > $out/consumer-for
- '' + optionalString (srv.pipelineName != null) ''
- echo "${srv.pipelineName}" > $out/pipeline-name
- '' + optionalString (srv.logger != null) ''
- echo "${srv.logger}-log" > $out/producer-for
+ '' + optionalString (sv.notificationFd != null) ''
+ echo "${toString sv.notificationFd}" > $out/notification-fd
+ '' + optionalString (sv.timeoutKill != null) ''
+ echo "${toString sv.timeoutKill}" > $out/timeout-kill
+ '' + optionalString (sv.timeoutFinish != null) ''
+ echo "${toString sv.timeoutFinish}" > $out/timeout-finish
+ '' + optionalString (sv.maxDeathTally != null) ''
+ echo "${toString sv.maxDeathTally}" > $out/max-death-tally
+ '' + optionalString (sv.downSignal != null) ''
+ echo "${sv.downSignal}" > $out/down-signal
+ '' + optionalString (sv.producerFor != null) ''
+ echo "${sv.producerFor}" > $out/producer-for
+ '' + optionalString (sv.consumerFor != null) ''
+ echo "${concatStringsSep "\n" sv.consumerFor}" > $out/consumer-for
+ '' + optionalString (sv.pipelineName != null) ''
+ echo "${sv.pipelineName}" > $out/pipeline-name
+ '' + optionalString (sv.log != null) ''
+ echo "${sv.log}-log" > $out/producer-for
'';
- makeBundleDefinitionDir = name: srv: pkgs.runCommand
+ makeBundleDefinitionDir = name: sv: pkgs.runCommand
"s6-rc-bundle-definition-${name}"
{ }
- (makeBundleConfig srv);
- makeOneshotDefinitionDir = name: srv: pkgs.runCommand
+ (makeBundleConfig sv);
+ makeOneshotDefinitionDir = name: sv: pkgs.runCommand
"s6-rc-oneshot-definition-${name}"
{ }
- (makeOneshotConfig srv);
- makeLongrunDefinitionDir = name: srv: pkgs.runCommand
+ (makeOneshotConfig sv);
+ makeLongrunDefinitionDir = name: sv: pkgs.runCommand
"s6-rc-longrun-definition-${name}"
{ }
- (makeLongrunConfig srv);
+ (makeLongrunConfig sv);
bundleDefinitionDirs = mapAttrs makeBundleDefinitionDir cfg.bundles;
oneshotDefinitionDirs = mapAttrs makeOneshotDefinitionDir cfg.oneshots;
@@ -133,69 +131,12 @@ let
serviceSourceDir = pkgs.linkFarm "s6-rc-service-source"
(mapAttrsToList
- (srvName: definitionDir: { name = srvName; path = definitionDir; })
+ (svName: definitionDir: { name = svName; path = definitionDir; })
allDefinitionDirs);
compiledDatabase = pkgs.runCommand "s6-rc-compiled-database" { } ''
${pkgs.s6-rc}/bin/s6-rc-compile $out ${serviceSourceDir}
'';
-
- # -------- init script -------- #
-
- initialPath = lib.makeBinPath (with pkgs; [
- coreutils
- execline
- s6
- s6-rc
- ]);
-
- catchAllLoggerServiceDir =
- let
- runScript = writeExeclineScript "catch-all-logger-run-script" ''
- s6-setuidgid "${cfg.logUser}"
- redirfd -w 1 /dev/null
- redirfd -rnb 0 "${cfg.logFifo}"
- s6-log -bpd3 -- ${cfg.loggingScript "uncaught"}
- '';
- in
- pkgs.runCommand "catch-all-logger-srvdir" { } ''
- mkdir -p $out
- cp ${runScript} $out/run
- echo 3 > $out/notification-fd
- '';
-
- # this runs after NixOS' stage-2-init.sh
- initScript = writeExeclineScript "init-script" ''
- ${pkgs.execline}/bin/export PATH "${initialPath}"
- ${pkgs.execline}/bin/exec
-
- # stage-2-init.sh prints the line "starting systemd..."
- foreground { echo "...NOT! starting s6-svscan on ${cfg.scanDir}..." }
-
- if { mkdir -p "${cfg.scanDir}" }
- if { cp -r "${catchAllLoggerServiceDir}" "${cfg.scanDir}/catch-all-log" }
-
- if { mkdir -p "${cfg.logDir}" }
- if { chown "${cfg.logUser}:${cfg.logGroup}" "${cfg.logDir}" }
- if { chmod 700 "${cfg.logDir}" }
-
- if { mkfifo "${cfg.logFifo}" }
- if { chown "${cfg.logUser}:${cfg.logGroup}" "${cfg.logFifo}" }
- if { chmod 600 "${cfg.logFifo}" }
-
- redirfd -r 0 /dev/null # don't use /dev/console as stdin
- redirfd -wnb 1 "${cfg.logFifo}" # redirect stdout to fifo
- fdmove -c 2 1 # redirect stderr to fifo
-
- background {
- redirfd -w 3 "${cfg.logFifo}" # this blocks until the catch-all logger is running
- if { s6-rc-init -c "${compiledDatabase}" "${cfg.scanDir}" }
- s6-rc -v2 change "${cfg.allBundle}"
- }
-
- exec -a s6-svscan # name it 's6-svscan' instead of /nix/store/...
- s6-svscan "${cfg.scanDir}"
- '';
in
{
options = with types;
@@ -271,101 +212,82 @@ in
type = str;
};
# this is not an actual option in s6-rc; it creates a logger service
- # with the name "<logger>-log" and adds the necessary "producer-for"
- # and "consumer-for" files.
- logger = mkNullableOption {
+ # named "<log>-log" and adds the necessary "producer-for" and
+ # "consumer-for" files.
+ log = mkNullableOption {
type = str;
};
} // atomicOptions;
in
{
- s6 = {
- enable = mkEnableOption "s6 init system";
- allBundle = mkOption {
- type = str;
- };
- logUser = mkOption {
- type = str;
- default = "log";
- };
- logGroup = mkOption {
- type = str;
- default = "log";
- };
- logDir = mkOption {
- type = path;
- default = "/log";
- };
- logFifo = mkOption {
- type = path;
- default = "/run/log-fifo";
- };
- loggingScript = mkOption {
- type = anything;
- default = name: "T s1000000 n20 ${cfg.logDir}/${name}";
- };
- scanDir = mkOption {
- type = path;
- default = "/run/service";
- };
+ logUser = mkOption {
+ type = str;
+ default = "log";
+ };
- bundles = mkOption {
- type = with types; attrsOf (submodule {
- options = bundleOptions;
- });
- };
+ logGroup = mkOption {
+ type = str;
+ default = "log";
+ };
- oneshots = mkOption {
- type = with types; attrsOf (submodule {
- options = oneshotOptions;
- });
- default = { };
- };
+ logDir = mkOption {
+ type = path;
+ default = "/log";
+ };
- longruns = mkOption {
- type = with types; attrsOf (submodule {
- options = longrunOptions;
- });
- default = { };
- };
+ loggingScript = mkOption {
+ type = anything;
+ default = name: "T s1000000 n20 ${cfg.logDir}/${name}";
+ };
+
+ bundles = mkOption {
+ type = with types; attrsOf (submodule {
+ options = bundleOptions;
+ });
+ };
+
+ oneshots = mkOption {
+ type = with types; attrsOf (submodule {
+ options = oneshotOptions;
+ });
+ default = { };
+ };
+
+ longruns = mkOption {
+ type = with types; attrsOf (submodule {
+ options = longrunOptions;
+ });
+ default = { };
+ };
+
+ # ---- output ---- #
+
+ serviceSourceDir = mkOption {
+ type = types.raw;
+ };
+
+ compiledDatabase = mkOption {
+ type = types.raw;
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
- assertion = hasAttr cfg.allBundle cfg.bundles;
- message = "the bundle '${cfg.allBundle}' does not exist!";
- }
- {
- assertion = !(elem "uncaught" loggerNames);
- message = "the logger name 'uncaught' is reserved";
- }
- {
assertion = length (attrNames allDefinitionDirs)
== length (attrNames cfg.bundles)
+ length (attrNames cfg.oneshots)
+ length (attrNames cfg.longruns)
- + length (attrNames loggerLongruns);
+ + length (attrNames logLongruns);
message = "no two services can have the same name, even if they are of different types";
}
] ++ mapAttrsToList
(name: def: {
- assertion = def.producerFor == null || def.logger == null;
- message = "in s6.longruns.${name}: 'producerFor' and 'logger' are mutually exclusive";
+ assertion = def.producerFor == null || def.log == null;
+ message = "in `longruns.${name}`: `producerFor` and `log` are mutually exclusive";
})
cfg.longruns;
- users = {
- users.${cfg.logUser} = {
- isSystemUser = true;
- group = cfg.logGroup;
- home = cfg.logDir;
- };
- groups.${cfg.logGroup} = { };
- };
-
- # checkmate!
- boot.systemdExecutable = "${initScript}";
+ inherit serviceSourceDir compiledDatabase;
};
}