From c93eae7878325927cd11489de40be35c907c84ca Mon Sep 17 00:00:00 2001 From: Malte Voos Date: Fri, 13 May 2022 13:21:18 +0200 Subject: nixos module for non-nixos systems (s6-rc config only) --- module.nix | 308 +++++++++++++++++++++++-------------------------------------- 1 file changed, 115 insertions(+), 193 deletions(-) (limited to '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 "-log" and adds the necessary "producer-for" - # and "consumer-for" files. - logger = mkNullableOption { + # named "-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; }; } -- cgit 1.4.1