├── nixproc ├── backends │ ├── s6-rc │ │ ├── image-steps │ │ │ ├── dynamic-steps.nix │ │ │ ├── static-steps.nix │ │ │ ├── s6-rc-dynamic.nix │ │ │ ├── s6-rc-static.nix │ │ │ └── s6-rc.nix │ │ ├── default.nix │ │ ├── test-module │ │ │ ├── processes-s6-svscan.nix │ │ │ └── default.nix │ │ ├── create-service-bundle.nix │ │ ├── util.nix │ │ ├── create-log-service-for-longrun-service.nix │ │ ├── build-s6-rc-env.nix │ │ └── create-oneshot-service.nix │ ├── disnix │ │ ├── image-steps │ │ │ ├── dynamic-steps.nix │ │ │ ├── static-steps.nix │ │ │ ├── disnix.nix │ │ │ ├── disnix-static.nix │ │ │ └── disnix-dynamic.nix │ │ ├── create-process-script.nix │ │ ├── test-module │ │ │ └── default.nix │ │ ├── services-adapter.nix │ │ ├── build-disnix-env.nix │ │ └── generate-process-script.nix │ ├── sysvinit │ │ ├── image-steps │ │ │ ├── dynamic-steps.nix │ │ │ ├── static-steps.nix │ │ │ ├── sysvinit.nix │ │ │ ├── sysvinit-dynamic.nix │ │ │ └── sysvinit-static.nix │ │ ├── test-module │ │ │ └── default.nix │ │ ├── init-functions.nix │ │ ├── generate-sysvinit-script.nix │ │ └── build-sysvinit-env.nix │ ├── supervisord │ │ ├── image-steps │ │ │ ├── dynamic-steps.nix │ │ │ ├── static-steps.nix │ │ │ ├── supervisord.nix │ │ │ ├── supervisord-dynamic.nix │ │ │ └── supervisord-static.nix │ │ ├── supervisord.conf │ │ ├── test-module │ │ │ ├── processes-supervisord.nix │ │ │ └── default.nix │ │ ├── build-supervisord-env.nix │ │ ├── generate-supervisord-program.nix │ │ └── create-supervisord-program.nix │ ├── systemd │ │ ├── test-module │ │ │ ├── systemd-path.nix │ │ │ ├── xserver-autologin-module.nix │ │ │ └── default.nix │ │ ├── build-systemd-env.nix │ │ └── generate-systemd-service.nix │ ├── util │ │ ├── generate-prestart-script.nix │ │ ├── generate-compound-proxy.nix │ │ ├── generate-foreground-proxy.nix │ │ └── default.nix │ ├── bsdrc │ │ ├── rcsubr.nix │ │ ├── build-bsdrc-env.nix │ │ └── generate-bsdrc-script.nix │ ├── docker │ │ ├── test-module │ │ │ ├── processes-docker.nix │ │ │ └── default.nix │ │ ├── build-docker-env.nix │ │ └── create-docker-container.nix │ ├── launchd │ │ ├── build-launchd-env.nix │ │ ├── generate-launchd-daemon.nix │ │ └── create-launchd-daemon.nix │ └── cygrunsrv │ │ ├── build-cygrunsrv-env.nix │ │ ├── generate-cygrunsrv-params.nix │ │ └── create-cygrunsrv-params.nix ├── create-image-from-steps │ ├── generate-config-from-steps.nix │ ├── steps │ │ ├── init.nix │ │ ├── man.nix │ │ ├── nix-processmgmt-static.nix │ │ ├── basic.nix │ │ ├── su-pam.nix │ │ ├── bootstrap-init.nix │ │ ├── interactive.nix │ │ ├── nix-support.nix │ │ └── nix-processmgmt-dynamic.nix │ ├── create-image-from-steps.nix │ ├── create-nix-image.nix │ ├── create-multi-process-image-universal.nix │ ├── create-mutable-multi-process-image-universal.nix │ ├── create-multi-process-image.nix │ └── create-mutable-multi-process-image.nix ├── test-driver │ ├── util │ │ ├── execute-command.nix │ │ └── execute-deploy.nix │ ├── profiles │ │ ├── privileged.nix │ │ ├── unprivileged-user-module.nix │ │ └── unprivileged.nix │ ├── universal.nix │ └── agnostic.nix ├── create-managed-process │ └── agnostic │ │ ├── create-agnostic-config.nix │ │ ├── create-managed-process-from-config.nix │ │ └── create-managed-process.nix ├── derive-dysnomia-process-type.nix └── create-credentials │ └── default.nix ├── examples ├── webapps-agnostic │ ├── infra-bootstrap.nix │ ├── distribution.nix │ ├── idresources.nix │ ├── network-physical.nix │ ├── ids.nix │ ├── constructors │ │ ├── constructors.nix │ │ └── webapp │ │ │ ├── webapp-fg.nix │ │ │ ├── default.nix │ │ │ └── webapp-daemon.nix │ ├── ids-advanced.nix │ ├── network-logical.nix │ ├── processes.nix │ ├── services.nix │ └── processes-advanced.nix ├── webapps-sysvinit │ ├── distribution.nix │ ├── constructors │ │ ├── nginx │ │ │ ├── errorpage │ │ │ │ └── index.html │ │ │ ├── default.nix │ │ │ └── nginx-reverse-proxy.nix │ │ ├── webapp │ │ │ └── default.nix │ │ └── constructors.nix │ ├── idresources.nix │ ├── network-physical.nix │ ├── network-logical.nix │ ├── ids.nix │ ├── ids-advanced.nix │ ├── processes.nix │ ├── services.nix │ └── processes-advanced.nix ├── services-agnostic │ ├── idresources.nix │ ├── ids.nix │ ├── constructors │ │ ├── nginx │ │ │ ├── errorpage │ │ │ │ └── index.html │ │ │ ├── default.nix │ │ │ └── nginx-reverse-proxy-hostbased.nix │ │ ├── supervisord │ │ │ ├── default.nix │ │ │ └── extendable.nix │ │ ├── s6-svscan │ │ │ └── default.nix │ │ ├── docker │ │ │ └── default.nix │ │ └── constructors.nix │ └── processes.nix ├── multi-process-image │ └── default.nix └── service-containers-agnostic │ ├── constructors.nix │ └── extendable.nix ├── tools ├── chainload-user │ ├── default.nix │ ├── Makefile │ └── main.c ├── supervisord │ ├── supervisordchecks │ └── default.nix ├── generate-config │ └── default.nix ├── sysvinit │ ├── sysvinitchecks │ ├── default.nix │ └── nixproc-sysvinit-runactivity.in ├── s6-rc │ ├── s6-rc-checks │ ├── default.nix │ └── nixproc-s6-svscan.in ├── idassign │ └── default.nix ├── disnix │ └── default.nix ├── systemd │ └── default.nix ├── cygrunsrv │ └── default.nix ├── launchd │ └── default.nix ├── docker │ └── default.nix ├── common │ └── default.nix ├── bsdrc │ ├── default.nix │ └── nixproc-bsdrc-runactivity.in └── default.nix ├── webapp ├── default.nix ├── Makefile ├── README.md ├── service.h ├── daemonize.h └── main.c ├── tests ├── builds.nix ├── services │ ├── nginx-reverse-proxy-hostbased │ │ ├── default.nix │ │ └── processes.nix │ ├── default.nix │ ├── docker │ │ ├── processes.nix │ │ └── default.nix │ ├── s6-svscan │ │ ├── processes.nix │ │ └── default.nix │ └── supervisord │ │ ├── processes.nix │ │ └── default.nix ├── webapps-agnostic-supervisord-stateless.nix ├── webapps-agnostic-config.nix └── multi-process-images.nix ├── LICENSE └── release.nix /nixproc/backends/s6-rc/image-steps/dynamic-steps.nix: -------------------------------------------------------------------------------- 1 | [ 2 | ./s6-rc.nix 3 | ./s6-rc-dynamic.nix 4 | ] 5 | -------------------------------------------------------------------------------- /nixproc/backends/s6-rc/image-steps/static-steps.nix: -------------------------------------------------------------------------------- 1 | [ 2 | ./s6-rc.nix 3 | ./s6-rc-static.nix 4 | ] 5 | -------------------------------------------------------------------------------- /nixproc/backends/disnix/image-steps/dynamic-steps.nix: -------------------------------------------------------------------------------- 1 | [ 2 | ./disnix.nix 3 | ./disnix-dynamic.nix 4 | ] 5 | -------------------------------------------------------------------------------- /nixproc/backends/disnix/image-steps/static-steps.nix: -------------------------------------------------------------------------------- 1 | [ 2 | ./disnix.nix 3 | ./disnix-static.nix 4 | ] 5 | -------------------------------------------------------------------------------- /nixproc/backends/sysvinit/image-steps/dynamic-steps.nix: -------------------------------------------------------------------------------- 1 | [ 2 | ./sysvinit.nix 3 | ./sysvinit-dynamic.nix 4 | ] 5 | -------------------------------------------------------------------------------- /nixproc/backends/sysvinit/image-steps/static-steps.nix: -------------------------------------------------------------------------------- 1 | [ 2 | ./sysvinit.nix 3 | ./sysvinit-static.nix 4 | ] 5 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/image-steps/dynamic-steps.nix: -------------------------------------------------------------------------------- 1 | [ 2 | ./supervisord.nix 3 | ./supervisord-dynamic.nix 4 | ] 5 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/image-steps/static-steps.nix: -------------------------------------------------------------------------------- 1 | [ 2 | ./supervisord.nix 3 | ./supervisord-static.nix 4 | ] 5 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/infra-bootstrap.nix: -------------------------------------------------------------------------------- 1 | { 2 | test1.properties.hostname = "test1"; 3 | test2.properties.hostname = "test2"; 4 | } 5 | -------------------------------------------------------------------------------- /nixproc/backends/systemd/test-module/systemd-path.nix: -------------------------------------------------------------------------------- 1 | {...}: 2 | 3 | { 4 | boot.extraSystemdUnitPaths = [ "/etc/systemd-mutable/system" ]; 5 | } 6 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/distribution.nix: -------------------------------------------------------------------------------- 1 | {infrastructure}: 2 | 3 | { 4 | webapp = [ infrastructure.test1 ]; 5 | nginxReverseProxy = [ infrastructure.test2 ]; 6 | } 7 | -------------------------------------------------------------------------------- /nixproc/backends/disnix/image-steps/disnix.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | result // { 4 | contents = result.contents or [] ++ [ pkgs.disnix pkgs.dysnomia ]; 5 | } 6 | -------------------------------------------------------------------------------- /tools/chainload-user/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-chainload-user"; 5 | src = ./.; 6 | makeFlags = "PREFIX=$(out)"; 7 | dontStrip = true; 8 | } 9 | -------------------------------------------------------------------------------- /nixproc/backends/sysvinit/image-steps/sysvinit.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | result // { 4 | contents = result.contents or [] ++ [ pkgs.sysvinit pkgs.gnugrep pkgs.coreutils ]; 5 | } 6 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/distribution.nix: -------------------------------------------------------------------------------- 1 | {infrastructure}: 2 | 3 | { 4 | webapp = [ infrastructure.test1 ]; 5 | webapp2 = [ infrastructure.test2 ]; 6 | nginx = [ infrastructure.test2 ]; 7 | } 8 | -------------------------------------------------------------------------------- /examples/services-agnostic/idresources.nix: -------------------------------------------------------------------------------- 1 | rec { 2 | uids = { 3 | min = 2000; 4 | max = 3000; 5 | }; 6 | 7 | gids = uids; 8 | 9 | inetHTTPPorts = { 10 | min = 9001; 11 | max = 9091; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/generate-config-from-steps.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common ? {}, input, steps}: 2 | 3 | pkgs.lib.foldl (result: moduleFile: 4 | import moduleFile { 5 | inherit pkgs common input result; 6 | } 7 | ) {} steps 8 | -------------------------------------------------------------------------------- /nixproc/test-driver/util/execute-command.nix: -------------------------------------------------------------------------------- 1 | {lib}: 2 | {forceDisableUserChange, command}: 3 | 4 | lib.optionalString forceDisableUserChange "su - unprivileged -c '" 5 | + command 6 | + lib.optionalString forceDisableUserChange "'" 7 | -------------------------------------------------------------------------------- /nixproc/test-driver/profiles/privileged.nix: -------------------------------------------------------------------------------- 1 | { 2 | params = rec { 3 | stateDir = "/var"; 4 | runtimeDir = "${stateDir}/run"; 5 | forceDisableUserChange = false; 6 | }; 7 | 8 | deployArgs = ""; 9 | 10 | nixosModules = []; 11 | } 12 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/steps/init.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | buildImageFormalArgs = builtins.functionArgs pkgs.dockerTools.buildImage; 5 | buildImageArgs = builtins.intersectAttrs buildImageFormalArgs input; 6 | in 7 | buildImageArgs 8 | -------------------------------------------------------------------------------- /examples/services-agnostic/ids.nix: -------------------------------------------------------------------------------- 1 | { 2 | "ids" = { 3 | "gids" = { 4 | }; 5 | "inetHTTPPorts" = { 6 | "supervisord" = 9001; 7 | }; 8 | "uids" = { 9 | }; 10 | }; 11 | "lastAssignments" = { 12 | "inetHTTPPorts" = 9001; 13 | }; 14 | } -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/create-image-from-steps.nix: -------------------------------------------------------------------------------- 1 | {pkgs, steps, common ? {}, input}: 2 | 3 | let 4 | generatedConfig = import ./generate-config-from-steps.nix { 5 | inherit pkgs common input steps; 6 | }; 7 | in 8 | pkgs.dockerTools.buildImage generatedConfig 9 | -------------------------------------------------------------------------------- /examples/services-agnostic/constructors/nginx/errorpage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Error page 5 | 6 | 7 | 8 | This web application is unavailable at the moment! 9 | 10 | 11 | -------------------------------------------------------------------------------- /nixproc/backends/util/generate-prestart-script.nix: -------------------------------------------------------------------------------- 1 | { stdenv, writeTextFile }: 2 | { name, initialize }: 3 | 4 | writeTextFile { 5 | name = "${name}-prestart"; 6 | executable = true; 7 | text = '' 8 | #! ${stdenv.shell} -e 9 | ${initialize} 10 | ''; 11 | } 12 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/constructors/nginx/errorpage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Disnix VirtualHosts example 5 | 6 | 7 | 8 | This web application is unavailable at the moment! 9 | 10 | 11 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | 3 | [include] 4 | files=conf.d/* 5 | 6 | [inet_http_server] 7 | port = 127.0.0.1:9001 8 | 9 | [rpcinterface:supervisor] 10 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 11 | 12 | [supervisorctl] 13 | -------------------------------------------------------------------------------- /tools/chainload-user/Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | 3 | all: 4 | $(CC) $(CFLAGS) $(LDFLAGS) main.c -o nixproc-chainload-user 5 | 6 | install: all 7 | install -d -m755 $(PREFIX)/bin 8 | install -m755 nixproc-chainload-user $(PREFIX)/bin 9 | 10 | clean: 11 | rm -f *.o 12 | rm -f nixproc-chainload-user 13 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/idresources.nix: -------------------------------------------------------------------------------- 1 | rec { 2 | webappPorts = { 3 | min = 5000; 4 | max = 6000; 5 | }; 6 | 7 | nginxPorts = { 8 | min = 8080; 9 | max = 9000; 10 | }; 11 | 12 | uids = { 13 | min = 2000; 14 | max = 3000; 15 | }; 16 | 17 | gids = uids; 18 | } 19 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/idresources.nix: -------------------------------------------------------------------------------- 1 | rec { 2 | webappPorts = { 3 | min = 5000; 4 | max = 6000; 5 | }; 6 | 7 | nginxPorts = { 8 | min = 8080; 9 | max = 9000; 10 | }; 11 | 12 | uids = { 13 | min = 2000; 14 | max = 3000; 15 | }; 16 | 17 | gids = uids; 18 | } 19 | -------------------------------------------------------------------------------- /tools/supervisord/supervisordchecks: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | checkSupervisordConfDir() 4 | { 5 | if [ "$SUPERVISORD_CONF_DIR" = "" ] 6 | then 7 | echo "Please set SUPERVISORD_CONF_DIR to the directory where the supervisord.conf configuration resides!" >&2 8 | exit 1 9 | fi 10 | } 11 | -------------------------------------------------------------------------------- /webapp/default.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | 3 | stdenv.mkDerivation { 4 | name = "webapp"; 5 | src = ./.; 6 | buildInputs = [ libmicrohttpd ]; 7 | CFLAGS = "-I${libmicrohttpd.dev}/include"; 8 | LDFLAGS = "-L${libmicrohttpd}/lib"; 9 | makeFlags = "PREFIX=$(out)"; 10 | dontStrip = true; 11 | } 12 | -------------------------------------------------------------------------------- /webapp/Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | 3 | all: 4 | $(CC) $(CFLAGS) -c daemonize.c 5 | $(CC) $(CFLAGS) -c service.c 6 | $(CC) $(CFLAGS) $(LDFLAGS) daemonize.o service.o main.c -lmicrohttpd -o webapp 7 | 8 | install: all 9 | install -d -m755 $(PREFIX)/bin 10 | install -m755 webapp $(PREFIX)/bin 11 | 12 | clean: 13 | rm -f *.o 14 | rm -f webapp 15 | -------------------------------------------------------------------------------- /nixproc/test-driver/profiles/unprivileged-user-module.nix: -------------------------------------------------------------------------------- 1 | { 2 | users.extraUsers = { 3 | unprivileged = { 4 | uid = 1000; 5 | group = "users"; 6 | shell = "/bin/sh"; 7 | description = "Unprivileged user"; 8 | home = "/home/unprivileged"; 9 | createHome = true; 10 | isNormalUser = true; 11 | }; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/network-physical.nix: -------------------------------------------------------------------------------- 1 | { 2 | test1 = {pkgs, ...}: 3 | { 4 | deployment.targetEnv = "virtualbox"; 5 | deployment.virtualbox.memorySize = 4096; # megabytes 6 | }; 7 | 8 | test2 = {pkgs, ...}: 9 | { 10 | deployment.targetEnv = "virtualbox"; 11 | deployment.virtualbox.memorySize = 4096; # megabytes 12 | }; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/network-physical.nix: -------------------------------------------------------------------------------- 1 | { 2 | test1 = {pkgs, ...}: 3 | { 4 | deployment.targetEnv = "virtualbox"; 5 | deployment.virtualbox.memorySize = 4096; # megabytes 6 | }; 7 | 8 | test2 = {pkgs, ...}: 9 | { 10 | deployment.targetEnv = "virtualbox"; 11 | deployment.virtualbox.memorySize = 4096; # megabytes 12 | }; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/create-nix-image.nix: -------------------------------------------------------------------------------- 1 | {pkgs}: 2 | input: 3 | 4 | import ./create-image-from-steps.nix { 5 | inherit pkgs input; 6 | 7 | common = { 8 | system = builtins.currentSystem; 9 | }; 10 | 11 | steps = [ 12 | ./steps/init.nix 13 | ./steps/basic.nix 14 | ./steps/interactive.nix 15 | ./steps/man.nix 16 | ./steps/nix-support.nix 17 | ]; 18 | } 19 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/steps/man.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | result // pkgs.lib.optionalAttrs (input ? manpages && input.manpages) { 4 | contents = result.contents ++ (with pkgs; [ 5 | man groff gzip 6 | ]); 7 | 8 | config = result.config or {} // { 9 | Env = result.config.Env or [] ++ [ "MANPATH=/share/man:/nix/var/nix/profiles/default/share/man" ]; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/network-logical.nix: -------------------------------------------------------------------------------- 1 | { 2 | test1 = {pkgs, ...}: 3 | 4 | { 5 | services.disnix.enable = true; 6 | services.openssh.enable = true; 7 | networking.firewall.enable = false; 8 | }; 9 | 10 | test2 = {pkgs, ...}: 11 | 12 | { 13 | services.disnix.enable = true; 14 | services.openssh.enable = true; 15 | networking.firewall.enable = false; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /nixproc/test-driver/profiles/unprivileged.nix: -------------------------------------------------------------------------------- 1 | { 2 | params = rec { 3 | stateDir = "/home/unprivileged/var"; 4 | runtimeDir = "${stateDir}/run"; 5 | forceDisableUserChange = true; 6 | callingUser = "unprivileged"; 7 | callingGroup = "users"; 8 | }; 9 | 10 | deployArgs = [ "--state-dir" "/home/unprivileged/var" "--force-disable-user-change" ]; 11 | 12 | nixosModules = [ ./unprivileged-user-module.nix ]; 13 | } 14 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/image-steps/supervisord.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | result // { 4 | contents = result.contents or [] ++ [ pkgs.python3Packages.supervisor ]; 5 | config = result.config or {} // { 6 | Cmd = [ "${pkgs.python3Packages.supervisor}/bin/supervisord" "--nodaemon" "--configuration" "/etc/supervisor/supervisord.conf" "--logfile" "/var/log/supervisord.log" "--pidfile" "/var/run/supervisord.pid" ]; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /nixproc/create-managed-process/agnostic/create-agnostic-config.nix: -------------------------------------------------------------------------------- 1 | {stdenv}: 2 | properties: 3 | 4 | let 5 | configJSON = builtins.toJSON properties; 6 | in 7 | stdenv.mkDerivation rec { 8 | name = if properties ? name then properties.name else properties.instanceName; 9 | inherit configJSON; 10 | passAsFile = [ "configJSON" ]; 11 | buildCommand = '' 12 | mkdir -p $out 13 | cp $configJSONPath $out/${name}.json 14 | ''; 15 | } 16 | -------------------------------------------------------------------------------- /tools/generate-config/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-generate-config"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | sed -e "s|/bin/bash|$SHELL|" \ 9 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 10 | -e "s|@NIXPROC@|${../../nixproc}|" \ 11 | ${./nixproc-generate-config.in} > $out/bin/nixproc-generate-config 12 | chmod +x $out/bin/nixproc-generate-config 13 | ''; 14 | } 15 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/steps/nix-processmgmt-static.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | commonTools = (import ../../../tools { 5 | inherit pkgs; 6 | inherit (common) system; 7 | }).common; 8 | in 9 | result // { 10 | runAsRoot = result.runAsRoot or "" + '' 11 | # Initialize common state directories 12 | ${commonTools}/bin/nixproc-init-state --state-dir ${input.stateDir} --runtime-dir ${input.runtimeDir} 13 | ''; 14 | } 15 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/create-multi-process-image-universal.nix: -------------------------------------------------------------------------------- 1 | {pkgs}: 2 | input: 3 | 4 | import ./create-multi-process-image.nix { 5 | inherit pkgs; 6 | } (input // { 7 | steps = { 8 | disnix = ../backends/disnix/image-steps/static-steps.nix; 9 | s6-rc = ../backends/s6-rc/image-steps/static-steps.nix; 10 | supervisord = ../backends/supervisord/image-steps/static-steps.nix; 11 | sysvinit = ../backends/sysvinit/image-steps/static-steps.nix; 12 | }; 13 | }) 14 | -------------------------------------------------------------------------------- /nixproc/test-driver/util/execute-deploy.nix: -------------------------------------------------------------------------------- 1 | {lib}: 2 | {processManager, profileSettings, envVars ? [], extraDeployArgs ? [], processesEnv}: 3 | 4 | let 5 | executeCommand = import ./execute-command.nix { 6 | inherit lib; 7 | }; 8 | in 9 | executeCommand { 10 | inherit (profileSettings.params) forceDisableUserChange; 11 | command = "${toString envVars} nixproc-${processManager}-deploy ${toString profileSettings.deployArgs} ${toString extraDeployArgs} ${processesEnv}"; 12 | } 13 | -------------------------------------------------------------------------------- /nixproc/backends/bsdrc/rcsubr.nix: -------------------------------------------------------------------------------- 1 | {stdenv, forceDisableUserChange}: 2 | 3 | if forceDisableUserChange then 4 | # Disable the limits command when we want to deploy processes as an unprivileged user 5 | stdenv.mkDerivation { 6 | name = "rc.subr"; 7 | src = /etc/rc.subr; 8 | 9 | buildCommand = '' 10 | sed -e 's|limits -C $_login_class $_limits||' $src > $out 11 | ''; 12 | } 13 | else 14 | # Otherwise, simply return the path to the rc subroutines 15 | "/etc/rc.subr" 16 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/ids.nix: -------------------------------------------------------------------------------- 1 | { 2 | "ids" = { 3 | "gids" = { 4 | "nginx" = 2000; 5 | "webapp" = 2001; 6 | }; 7 | "nginxPorts" = { 8 | "nginx" = 8080; 9 | }; 10 | "uids" = { 11 | "nginx" = 2000; 12 | "webapp" = 2001; 13 | }; 14 | "webappPorts" = { 15 | "webapp" = 5000; 16 | }; 17 | }; 18 | "lastAssignments" = { 19 | "gids" = 2001; 20 | "nginxPorts" = 8080; 21 | "uids" = 2001; 22 | "webappPorts" = 5000; 23 | }; 24 | } -------------------------------------------------------------------------------- /examples/webapps-sysvinit/ids.nix: -------------------------------------------------------------------------------- 1 | { 2 | "ids" = { 3 | "gids" = { 4 | "nginx" = 2000; 5 | "webapp" = 2001; 6 | }; 7 | "nginxPorts" = { 8 | "nginx" = 8080; 9 | }; 10 | "uids" = { 11 | "nginx" = 2000; 12 | "webapp" = 2001; 13 | }; 14 | "webappPorts" = { 15 | "webapp" = 5000; 16 | }; 17 | }; 18 | "lastAssignments" = { 19 | "gids" = 2001; 20 | "nginxPorts" = 8080; 21 | "uids" = 2001; 22 | "webappPorts" = 5000; 23 | }; 24 | } -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/create-mutable-multi-process-image-universal.nix: -------------------------------------------------------------------------------- 1 | {pkgs}: 2 | input: 3 | 4 | import ./create-mutable-multi-process-image.nix { 5 | inherit pkgs; 6 | } (input // { 7 | steps = { 8 | disnix = ../backends/disnix/image-steps/dynamic-steps.nix; 9 | s6-rc = ../backends/s6-rc/image-steps/dynamic-steps.nix; 10 | supervisord = ../backends/supervisord/image-steps/dynamic-steps.nix; 11 | sysvinit = ../backends/sysvinit/image-steps/dynamic-steps.nix; 12 | }; 13 | }) 14 | -------------------------------------------------------------------------------- /tests/builds.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} 2 | , nix-processmgmt ? ./.. 3 | }: 4 | 5 | let 6 | backends = [ "bsdrc" "cygrunsrv" "disnix" "docker" "launchd" "s6-rc" "supervisord" "systemd" "sysvinit" ]; 7 | in 8 | pkgs.lib.genAttrs backends (backend: import "${nix-processmgmt}/nixproc/backends/${backend}/build-${backend}-env.nix" ({ 9 | exprFile = ../examples/webapps-agnostic/processes.nix; 10 | } // pkgs.lib.optionalAttrs (backend == "disnix") { 11 | disnixDataDir = "${pkgs.disnix}/share/disnix"; 12 | })) 13 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/steps/basic.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | result // { 4 | runAsRoot = result.runAsRoot or "" + '' 5 | ${pkgs.dockerTools.shadowSetup} 6 | 7 | # Always create these global state directories, because they are needed quite often 8 | mkdir -p /run /tmp 9 | chmod 1777 /tmp 10 | 11 | mkdir -p /var/empty 12 | ln -s ../run /var/run 13 | 14 | # Always create nobody/nogroup 15 | groupadd -g 999 -r nogroup 16 | useradd -u 999 -r nobody -g nogroup -d /dev/null 17 | ''; 18 | } 19 | -------------------------------------------------------------------------------- /examples/multi-process-image/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , processManager ? "supervisord" 4 | , forceDisableUserChange ? false 5 | }: 6 | 7 | let 8 | createMultiProcessImage = import ../../nixproc/create-image-from-steps/create-multi-process-image-universal.nix { 9 | inherit pkgs; 10 | }; 11 | in 12 | createMultiProcessImage { 13 | name = "multiprocess"; 14 | tag = "test"; 15 | exprFile = ../webapps-agnostic/processes.nix; 16 | inherit processManager forceDisableUserChange; 17 | } 18 | -------------------------------------------------------------------------------- /nixproc/backends/s6-rc/image-steps/s6-rc-dynamic.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | s6-rcTools = (import ../../../../tools { 5 | inherit pkgs; 6 | inherit (common) system; 7 | }).s6-rc; 8 | in 9 | result // { 10 | runAsRoot = result.runAsRoot or "" + '' 11 | # Create empty service directory 12 | mkdir -p /etc/s6/sv 13 | 14 | # Initialize s6-rc with a compiled database 15 | mkdir -p /etc/s6/rc 16 | s6-rc-compile /etc/s6/rc/compiled /etc/s6/sv 17 | ''; 18 | contents = result.contents or [] ++ [ s6-rcTools ]; 19 | } 20 | -------------------------------------------------------------------------------- /tools/sysvinit/sysvinitchecks: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | checkRunlevel() 4 | { 5 | if [ "$runlevel" = "" ] 6 | then 7 | if command -v runlevel > /dev/null 8 | then 9 | runlevel=$(runlevel | cut -d ' ' -f2) 10 | 11 | # If the runlevel is unknown, fall back to 3 12 | if [ "$runlevel" = "unknown" ] 13 | then 14 | runlevel=3 15 | fi 16 | else 17 | # If no runlevel command exists, then default to 3 18 | runlevel=3 19 | fi 20 | fi 21 | } 22 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/image-steps/supervisord-dynamic.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | supervisordTools = (import ../../../../tools { 5 | inherit pkgs; 6 | inherit (common) system; 7 | }).supervisord; 8 | in 9 | result // { 10 | contents = result.contents or [] ++ [ supervisordTools ]; 11 | 12 | runAsRoot = result.runAsRoot or "" + '' 13 | mkdir -p /etc/supervisor/conf.d 14 | cp ${../supervisord.conf} /etc/supervisor/supervisord.conf 15 | ''; 16 | 17 | config = result.config or {} // { 18 | Env = result.config.Env or [] 19 | ++ [ "SUPERVISORD_CONF_DIR=/etc/supervisor" ]; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /nixproc/test-driver/universal.nix: -------------------------------------------------------------------------------- 1 | { nixpkgs ? 2 | , system ? builtins.currentSystem 3 | }: 4 | 5 | import ./agnostic.nix { 6 | inherit nixpkgs system; 7 | 8 | processManagerModules = { 9 | disnix = ../backends/disnix/test-module; 10 | docker = ../backends/docker/test-module; 11 | s6-rc = ../backends/s6-rc/test-module; 12 | supervisord = ../backends/supervisord/test-module; 13 | systemd = ../backends/systemd/test-module; 14 | sysvinit = ../backends/sysvinit/test-module; 15 | }; 16 | 17 | profileSettingModules = { 18 | privileged = ./profiles/privileged.nix; 19 | unprivileged = ./profiles/unprivileged.nix; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /tools/s6-rc/s6-rc-checks: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | checkStateDir() 4 | { 5 | if [ "$stateDir" = "" ] 6 | then 7 | if [ "$NIXPROC_STATE_DIR" = "" ] 8 | then 9 | stateDir="/var" 10 | else 11 | stateDir="$NIXPROC_STATE_DIR" 12 | fi 13 | fi 14 | } 15 | 16 | checkRuntimeDir() 17 | { 18 | if [ "$runtimeDir" = "" ] 19 | then 20 | if [ "$NIXPROC_RUNTIME_DIR" = "" ] 21 | then 22 | runtimeDir="$stateDir/run" 23 | else 24 | runtimeDir="$NIXPROC_RUNTIME_DIR" 25 | fi 26 | fi 27 | } 28 | 29 | checkScanDir() 30 | { 31 | scanDir="$runtimeDir/service" 32 | } 33 | -------------------------------------------------------------------------------- /tools/idassign/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-id-assign"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | if [ "$(type -p greadlink)" = "" ] 9 | then 10 | readlink="$(type -p readlink)" 11 | else 12 | readlink="$(type -p greadlink)" 13 | fi 14 | 15 | sed -e "s|/bin/bash|$SHELL|" \ 16 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 17 | -e "s|@readlink@|$readlink|" \ 18 | -e "s|@NIXPROC@|${../../nixproc}|" \ 19 | -e "s|@commonchecks@|${../commonchecks}|" \ 20 | ${./nixproc-id-assign.in} > $out/bin/nixproc-id-assign 21 | chmod +x $out/bin/nixproc-id-assign 22 | ''; 23 | } 24 | -------------------------------------------------------------------------------- /tools/disnix/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-disnix-tools"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | sed -e "s|/bin/bash|$SHELL|" \ 9 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 10 | -e "s|@commonchecks@|${../commonchecks}|" \ 11 | ${./nixproc-disnix-deploy.in} > $out/bin/nixproc-disnix-deploy 12 | chmod +x $out/bin/nixproc-disnix-deploy 13 | 14 | sed -e "s|/bin/bash|$SHELL|" \ 15 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 16 | -e "s|@commonchecks@|${../commonchecks}|" \ 17 | ${./nixproc-disnix-switch.in} > $out/bin/nixproc-disnix-switch 18 | chmod +x $out/bin/nixproc-disnix-switch 19 | ''; 20 | } 21 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/image-steps/supervisord-static.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | profile = import ../build-supervisord-env.nix { 5 | inherit pkgs; 6 | inherit (input) exprFile extraParams stateDir runtimeDir forceDisableUserChange; 7 | inherit (common) system; 8 | }; 9 | in 10 | result // { 11 | runAsRoot = result.runAsRoot or "" + '' 12 | ln -s ${profile} /etc/supervisor 13 | 14 | ${pkgs.lib.optionalString (!input.forceDisableUserChange) '' 15 | export PATH=$PATH:${pkgs.findutils}/bin:${pkgs.glibc.bin}/bin 16 | ${pkgs.dysnomia}/bin/dysnomia-addgroups ${profile} 17 | ${pkgs.dysnomia}/bin/dysnomia-addusers ${profile} 18 | ''} 19 | ''; 20 | } 21 | -------------------------------------------------------------------------------- /tools/systemd/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-systemd-tools"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | sed -e "s|/bin/bash|$SHELL|" \ 9 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 10 | -e "s|@commonchecks@|${../commonchecks}|" \ 11 | ${./nixproc-systemd-switch.in} > $out/bin/nixproc-systemd-switch 12 | chmod +x $out/bin/nixproc-systemd-switch 13 | 14 | sed -e "s|/bin/bash|$SHELL|" \ 15 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 16 | -e "s|@commonchecks@|${../commonchecks}|" \ 17 | ${./nixproc-systemd-deploy.in} > $out/bin/nixproc-systemd-deploy 18 | chmod +x $out/bin/nixproc-systemd-deploy 19 | ''; 20 | } 21 | -------------------------------------------------------------------------------- /nixproc/derive-dysnomia-process-type.nix: -------------------------------------------------------------------------------- 1 | {processManager}: 2 | 3 | if processManager == null then "managed-process" 4 | else if processManager == "sysvinit" then "sysvinit-script" 5 | else if processManager == "systemd" then "systemd-unit" 6 | else if processManager == "supervisord" then "supervisord-program" 7 | else if processManager == "bsdrc" then "bsdrc-script" 8 | else if processManager == "cygrunsrv" then "cygrunsrv-service" 9 | else if processManager == "launchd" then "launchd-daemon" 10 | else if processManager == "disnix" then "process" 11 | else if processManager == "docker" then "docker-container" 12 | else if processManager == "s6-rc" then "s6-rc-service" 13 | else throw "Unknown process manager: ${processManager}" 14 | -------------------------------------------------------------------------------- /examples/service-containers-agnostic/constructors.nix: -------------------------------------------------------------------------------- 1 | { pkgs 2 | , stateDir 3 | , logDir 4 | , runtimeDir 5 | , cacheDir 6 | , tmpDir 7 | , forceDisableUserChange 8 | , processManager 9 | , ids ? {} 10 | }: 11 | 12 | let 13 | constructors = import ../services-agnostic/constructors.nix { 14 | inherit pkgs stateDir logDir runtimeDir cacheDir tmpDir forceDisableUserChange processManager ids; 15 | }; 16 | in 17 | { 18 | extendableSupervisord = import ./extendable.nix { 19 | inherit stateDir; 20 | inherit (pkgs) stdenv; 21 | supervisordConstructorFun = constructors.extendableSupervisord; 22 | dysnomia = pkgs.dysnomia.override (origArgs: { 23 | enableSupervisordProgram = true; 24 | }); 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /tools/cygrunsrv/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-cygrunsrv-tools"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | sed -e "s|/bin/bash|$SHELL|" \ 9 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 10 | -e "s|@commonchecks@|${../commonchecks}|" \ 11 | ${./nixproc-cygrunsrv-switch.in} > $out/bin/nixproc-cygrunsrv-switch 12 | chmod +x $out/bin/nixproc-cygrunsrv-switch 13 | 14 | sed -e "s|/bin/bash|$SHELL|" \ 15 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 16 | -e "s|@commonchecks@|${../commonchecks}|" \ 17 | ${./nixproc-cygrunsrv-deploy.in} > $out/bin/nixproc-cygrunsrv-deploy 18 | chmod +x $out/bin/nixproc-cygrunsrv-deploy 19 | ''; 20 | } 21 | -------------------------------------------------------------------------------- /nixproc/backends/s6-rc/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, lib, execline, createCredentials, logDir, logDirUser ? "s6-log", logDirGroup ? "s6-log", forceDisableUserChange}: 2 | 3 | rec { 4 | createLogServiceForLongRunService = import ./create-log-service-for-longrun-service.nix { 5 | inherit stdenv lib execline logDir logDirUser logDirGroup forceDisableUserChange; 6 | }; 7 | createLongRunService = import ./create-longrun-service.nix { 8 | inherit stdenv lib createCredentials createLogServiceForLongRunService; 9 | }; 10 | createOneShotService = import ./create-oneshot-service.nix { 11 | inherit stdenv lib createCredentials; 12 | }; 13 | createServiceBundle = import ./create-service-bundle.nix { 14 | inherit stdenv lib; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /nixproc/backends/disnix/create-process-script.nix: -------------------------------------------------------------------------------- 1 | {stdenv, lib, createCredentials, forceDisableUserChange}: 2 | 3 | { name 4 | , process 5 | , pidFile ? null 6 | , dependencies ? [] 7 | , postInstall ? "" 8 | , credentials ? {} 9 | }: 10 | 11 | let 12 | credentialsSpec = createCredentials credentials; 13 | in 14 | stdenv.mkDerivation { 15 | inherit name; 16 | buildCommand = '' 17 | mkdir -p $out/etc/dysnomia/process 18 | cat > $out/etc/dysnomia/process/${name} < { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , libDir ? "${stateDir}/lib" 8 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 9 | , forceDisableUserChange ? false 10 | , processManager ? "sysvinit" 11 | }: 12 | 13 | let 14 | constructors = import ../../../../examples/services-agnostic/constructors/constructors.nix { 15 | inherit pkgs stateDir runtimeDir logDir tmpDir cacheDir libDir forceDisableUserChange processManager; 16 | }; 17 | in 18 | rec { 19 | docker = { 20 | pkg = constructors.docker {}; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /tools/launchd/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-launchd-tools"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | sed -e "s|/bin/bash|$SHELL|" \ 9 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 10 | -e "s|@commonchecks@|${../commonchecks}|" \ 11 | ${./nixproc-launchd-switch.in} > $out/bin/nixproc-launchd-switch 12 | chmod +x $out/bin/nixproc-launchd-switch 13 | 14 | sed -e "s|/bin/bash|$SHELL|" \ 15 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 16 | -e "s|@commonchecks@|${../commonchecks}|" \ 17 | -e "s|@readlink@|$(type -p readlink)|" \ 18 | ${./nixproc-launchd-deploy.in} > $out/bin/nixproc-launchd-deploy 19 | chmod +x $out/bin/nixproc-launchd-deploy 20 | ''; 21 | } 22 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/constructors/webapp/default.nix: -------------------------------------------------------------------------------- 1 | {createSystemVInitScript, tmpDir}: 2 | {port, instanceSuffix ? "", instanceName ? "webapp${instanceSuffix}"}: 3 | 4 | let 5 | webapp = import ../../../../webapp; 6 | in 7 | createSystemVInitScript { 8 | inherit instanceName; 9 | 10 | process = "${webapp}/bin/webapp"; 11 | args = [ "-D" ]; 12 | environment = { 13 | PORT = port; 14 | PID_FILE = "${tmpDir}/${instanceName}.pid"; 15 | }; 16 | 17 | runlevels = [ 3 4 5 ]; 18 | 19 | user = instanceName; 20 | 21 | credentials = { 22 | groups = { 23 | "${instanceName}" = {}; 24 | }; 25 | users = { 26 | "${instanceName}" = { 27 | group = instanceName; 28 | description = "Webapp"; 29 | }; 30 | }; 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /nixproc/backends/s6-rc/test-module/processes-s6-svscan.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , libDir ? "${stateDir}/lib" 8 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 9 | , forceDisableUserChange ? false 10 | , processManager ? "sysvinit" 11 | }: 12 | 13 | let 14 | constructors = import ../../../../examples/services-agnostic/constructors/constructors.nix { 15 | inherit pkgs stateDir runtimeDir logDir tmpDir cacheDir libDir forceDisableUserChange processManager; 16 | }; 17 | in 18 | rec { 19 | s6-svscan = { 20 | pkg = constructors.s6-svscan {}; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /tests/services/nginx-reverse-proxy-hostbased/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, testService, processManagers, profiles }: 2 | 3 | testService { 4 | name = "nginx-reverse-proxy-hostbased"; 5 | exprFile = ./processes.nix; 6 | 7 | readiness = {instanceName, instance, ...}: 8 | '' 9 | machine.wait_for_open_port(${toString instance.port}) 10 | ''; 11 | 12 | tests = {instanceName, instance, ...}: 13 | pkgs.lib.optionalString (instanceName == "nginx" || instanceName == "nginx2") 14 | (pkgs.lib.concatMapStrings (webapp: '' 15 | machine.succeed( 16 | "curl --fail -H 'Host: ${webapp.dnsName}' http://localhost:${toString instance.port} | grep ': ${toString webapp.port}'" 17 | ) 18 | '') instance.webapps); 19 | 20 | inherit processManagers profiles; 21 | } 22 | -------------------------------------------------------------------------------- /examples/services-agnostic/constructors/supervisord/default.nix: -------------------------------------------------------------------------------- 1 | {createManagedProcess, supervisor, runtimeDir, logDir}: 2 | {instanceSuffix ? "", instanceName ? "supervisord${instanceSuffix}", initialize ? "", configFile, postInstall ? ""}: 3 | 4 | let 5 | pidFile = "${runtimeDir}/${instanceName}.pid"; 6 | logFile = "${logDir}/${instanceName}.log"; 7 | in 8 | createManagedProcess { 9 | inherit instanceName postInstall; 10 | initialize = '' 11 | mkdir -p ${logDir} 12 | ${initialize} 13 | ''; 14 | process = "${supervisor}/bin/supervisord"; 15 | args = [ "--configuration" configFile "--logfile" logFile "--pidfile" pidFile ]; 16 | foregroundProcessExtraArgs = [ "--nodaemon" ]; 17 | 18 | overrides = { 19 | sysvinit = { 20 | runlevels = [ 3 4 5 ]; 21 | }; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /nixproc/backends/s6-rc/image-steps/s6-rc-static.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | profile = import ../build-s6-rc-env.nix { 5 | inherit pkgs; 6 | inherit (common) system; 7 | inherit (input) exprFile extraParams stateDir runtimeDir forceDisableUserChange; 8 | }; 9 | in 10 | result // { 11 | runAsRoot = result.runAsRoot or "" + '' 12 | # Initialize s6-rc with a compiled database 13 | mkdir -p /etc/s6/rc 14 | s6-rc-compile /etc/s6/rc/compiled ${profile}/etc/s6/sv 15 | 16 | ${pkgs.lib.optionalString (!input.forceDisableUserChange) '' 17 | export PATH=$PATH:${pkgs.findutils}/bin:${pkgs.glibc.bin}/bin 18 | ${pkgs.dysnomia}/bin/dysnomia-addgroups ${profile} 19 | ${pkgs.dysnomia}/bin/dysnomia-addusers ${profile} 20 | ''} 21 | ''; 22 | } 23 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/test-module/processes-supervisord.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , libDir ? "${stateDir}/lib" 8 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 9 | , forceDisableUserChange ? false 10 | , processManager ? "sysvinit" 11 | }: 12 | 13 | let 14 | constructors = import ../../../../examples/services-agnostic/constructors/constructors.nix { 15 | inherit pkgs stateDir runtimeDir logDir tmpDir cacheDir libDir forceDisableUserChange processManager; 16 | }; 17 | in 18 | rec { 19 | extendableSupervisord = { 20 | pkg = constructors.extendableSupervisord {}; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/create-multi-process-image.nix: -------------------------------------------------------------------------------- 1 | {pkgs}: 2 | {processManager, steps, ...}@input: 3 | 4 | let 5 | _input = rec { 6 | stateDir = "/var"; 7 | runtimeDir = "${stateDir}/run"; 8 | forceDisableUserChange = false; 9 | extraParams = {}; 10 | } // input; 11 | 12 | processManagerSpecificStepsFile = builtins.getAttr processManager steps; 13 | in 14 | import ./create-image-from-steps.nix { 15 | inherit pkgs; 16 | input = _input; 17 | 18 | common = { 19 | system = builtins.currentSystem; 20 | }; 21 | 22 | steps = [ 23 | ./steps/init.nix 24 | ./steps/basic.nix 25 | ./steps/interactive.nix 26 | ./steps/man.nix 27 | ./steps/nix-processmgmt-static.nix 28 | ./steps/su-pam.nix 29 | ] 30 | ++ import processManagerSpecificStepsFile; 31 | } 32 | -------------------------------------------------------------------------------- /tools/docker/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-docker-tools"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | sed -e "s|/bin/bash|$SHELL|" \ 9 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 10 | -e "s|@commonchecks@|${../commonchecks}|" \ 11 | ${./nixproc-docker-switch.in} > $out/bin/nixproc-docker-switch 12 | chmod +x $out/bin/nixproc-docker-switch 13 | 14 | sed -e "s|/bin/bash|$SHELL|" \ 15 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 16 | -e "s|@readlink@|$(type -p readlink)|" \ 17 | -e "s|@xargs@|$(type -p xargs)|" \ 18 | -e "s|@commonchecks@|${../commonchecks}|" \ 19 | ${./nixproc-docker-deploy.in} > $out/bin/nixproc-docker-deploy 20 | chmod +x $out/bin/nixproc-docker-deploy 21 | ''; 22 | } 23 | -------------------------------------------------------------------------------- /nixproc/backends/util/generate-compound-proxy.nix: -------------------------------------------------------------------------------- 1 | {stdenv, lib, writeTextFile}: 2 | {startCommand, stopCommand, path ? []}: 3 | 4 | writeTextFile { 5 | name = "compound-proxy-script"; 6 | text = '' 7 | #! ${stdenv.shell} -e 8 | 9 | _term() 10 | { 11 | echo "${stopCommand}" 12 | ${stopCommand} 13 | kill "$pid" 14 | exit 0 15 | } 16 | 17 | export PATH=${lib.escapeShellArg (builtins.concatStringsSep ":" (map (pathComponent: "${pathComponent}/bin") path))}:$PATH 18 | 19 | ${startCommand} 20 | 21 | # Keep process running, but allow it to respond to the TERM and INT signals so that all scripts are stopped properly 22 | 23 | trap _term TERM 24 | trap _term INT 25 | 26 | tail -f /dev/null & pid=$! 27 | wait "$pid" 28 | ''; 29 | executable = true; 30 | } 31 | -------------------------------------------------------------------------------- /nixproc/backends/sysvinit/test-module/default.nix: -------------------------------------------------------------------------------- 1 | {profileSettings, exprFile, extraParams, tools, pkgs, system}: 2 | 3 | let 4 | executeDeploy = import ../../../test-driver/util/execute-deploy.nix { 5 | inherit (pkgs) lib; 6 | }; 7 | 8 | processesEnvSystem = import ../build-sysvinit-env.nix ({ 9 | inherit pkgs system exprFile extraParams; 10 | } // profileSettings.params); 11 | in 12 | { 13 | inherit (profileSettings) params; 14 | 15 | nixosModules = []; 16 | 17 | systemPackages = [ 18 | tools.sysvinit 19 | ]; 20 | 21 | additionalPaths = [ processesEnvSystem ]; 22 | 23 | deployProcessManager = ""; 24 | 25 | deploySystem = '' 26 | machine.succeed( 27 | "${executeDeploy { inherit profileSettings; processManager = "sysvinit"; processesEnv = processesEnvSystem; }}" 28 | ) 29 | ''; 30 | } 31 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/create-mutable-multi-process-image.nix: -------------------------------------------------------------------------------- 1 | {pkgs}: 2 | {processManager, steps, ...}@input: 3 | 4 | let 5 | _input = rec { 6 | stateDir = "/var"; 7 | runtimeDir = "${stateDir}/run"; 8 | } // input; 9 | 10 | processManagerSpecificStepsFile = builtins.getAttr processManager steps; 11 | in 12 | import ./create-image-from-steps.nix { 13 | inherit pkgs; 14 | common = { 15 | system = builtins.currentSystem; 16 | }; 17 | input = _input; 18 | 19 | steps = [ 20 | ./steps/init.nix 21 | ./steps/basic.nix 22 | ./steps/interactive.nix 23 | ./steps/man.nix 24 | ./steps/nix-processmgmt-dynamic.nix 25 | ./steps/su-pam.nix 26 | ] 27 | ++ import processManagerSpecificStepsFile 28 | ++ [ 29 | ./steps/bootstrap-init.nix 30 | ./steps/nix-support.nix 31 | ]; 32 | } 33 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/constructors/constructors.nix: -------------------------------------------------------------------------------- 1 | { pkgs 2 | , stateDir 3 | , logDir 4 | , runtimeDir 5 | , tmpDir 6 | , forceDisableUserChange 7 | , processManager 8 | , webappMode # set to 'foreground' to make them all foreground process, 'daemon' to make them all daemons. null is to pick best option for the selected processManager 9 | , ids ? {} 10 | }: 11 | 12 | let 13 | createManagedProcess = import ../../../nixproc/create-managed-process/universal/create-managed-process-universal.nix { 14 | inherit pkgs runtimeDir stateDir logDir tmpDir forceDisableUserChange processManager ids; 15 | }; 16 | 17 | webappExpr = if webappMode == "foreground" then ./webapp/webapp-fg.nix 18 | else if webappMode == "daemon" then ./webapp/webapp-daemon.nix 19 | else ./webapp; 20 | in 21 | { 22 | webapp = import webappExpr { 23 | inherit createManagedProcess tmpDir; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/constructors/webapp/webapp-fg.nix: -------------------------------------------------------------------------------- 1 | {createManagedProcess, tmpDir}: 2 | {port, instanceSuffix ? "", instanceName ? "webapp${instanceSuffix}"}: 3 | 4 | let 5 | webapp = import ../../../../webapp; 6 | in 7 | createManagedProcess { 8 | description = "Simple web application"; 9 | inherit instanceName; 10 | 11 | # This expression only specifies how to run the webapp in foreground mode 12 | foregroundProcess = "${webapp}/bin/webapp"; 13 | 14 | environment = { 15 | PORT = port; 16 | }; 17 | user = instanceName; 18 | credentials = { 19 | groups = { 20 | "${instanceName}" = {}; 21 | }; 22 | users = { 23 | "${instanceName}" = { 24 | group = instanceName; 25 | description = "Webapp"; 26 | }; 27 | }; 28 | }; 29 | 30 | overrides = { 31 | sysvinit = { 32 | runlevels = [ 3 4 5 ]; 33 | }; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /nixproc/backends/disnix/test-module/default.nix: -------------------------------------------------------------------------------- 1 | {profileSettings, exprFile, extraParams, tools, pkgs, system}: 2 | 3 | let 4 | executeDeploy = import ../../../test-driver/util/execute-deploy.nix { 5 | inherit (pkgs) lib; 6 | }; 7 | 8 | processesEnvSystem = import ../build-disnix-env.nix ({ 9 | inherit pkgs system exprFile extraParams; 10 | disnixDataDir = "${pkgs.disnix}/share/disnix"; 11 | } // profileSettings.params); 12 | in 13 | { 14 | inherit (profileSettings) params; 15 | 16 | nixosModules = []; 17 | 18 | systemPackages = [ 19 | tools.disnix 20 | pkgs.disnix 21 | ]; 22 | 23 | additionalPaths = [ processesEnvSystem ]; 24 | 25 | deployProcessManager = ""; 26 | 27 | deploySystem = '' 28 | machine.succeed( 29 | "${executeDeploy { inherit profileSettings; processManager = "disnix"; processesEnv = processesEnvSystem; }}" 30 | ) 31 | ''; 32 | } 33 | -------------------------------------------------------------------------------- /tests/services/default.nix: -------------------------------------------------------------------------------- 1 | { nixpkgs ? 2 | , system ? builtins.currentSystem 3 | , processManagers ? [ "supervisord" "sysvinit" "systemd" "disnix" "s6-rc" ] 4 | , profiles ? [ "privileged" "unprivileged" ] 5 | }: 6 | 7 | let 8 | pkgs = import nixpkgs { inherit system; }; 9 | 10 | testService = import ../../nixproc/test-driver/universal.nix { 11 | inherit nixpkgs system; 12 | }; 13 | in 14 | { 15 | docker = import ./docker { 16 | inherit pkgs processManagers profiles testService; 17 | }; 18 | 19 | nginx-reverse-proxy-hostbased = import ./nginx-reverse-proxy-hostbased { 20 | inherit pkgs processManagers profiles testService; 21 | }; 22 | 23 | s6-svscan = import ./s6-svscan { 24 | inherit pkgs processManagers profiles testService; 25 | }; 26 | 27 | supervisord = import ./supervisord { 28 | inherit pkgs processManagers profiles testService; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /examples/services-agnostic/constructors/s6-svscan/default.nix: -------------------------------------------------------------------------------- 1 | {createManagedProcess, s6, execline, runtimeDir}: 2 | 3 | { instanceSuffix ? "" 4 | , instanceName ? "s6-svscan${instanceSuffix}" 5 | , scanDir ? "${runtimeDir}/service${instanceSuffix}" 6 | , logUser ? "s6-log${instanceSuffix}" 7 | , logGroup ? "s6-log${instanceSuffix}" 8 | }: 9 | 10 | createManagedProcess { 11 | inherit instanceName; 12 | 13 | path = [ s6 execline ]; 14 | foregroundProcess = "${s6}/bin/s6-svscan"; 15 | args = [ scanDir ]; 16 | initialize = '' 17 | mkdir -p ${scanDir} 18 | ''; 19 | 20 | credentials = { 21 | groups = { 22 | "${logGroup}" = {}; 23 | }; 24 | users = { 25 | "${logUser}" = { 26 | group = logGroup; 27 | description = "s6-log user"; 28 | }; 29 | }; 30 | }; 31 | 32 | overrides = { 33 | sysvinit = { 34 | runlevels = [ 3 4 5 ]; 35 | }; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /tools/common/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-common-tools"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | if [ "$(type -p greadlink)" = "" ] 9 | then 10 | readlink="$(type -p readlink)" 11 | else 12 | readlink="$(type -p greadlink)" 13 | fi 14 | 15 | sed -e "s|/bin/bash|$SHELL|" \ 16 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 17 | -e "s|@readlink@|$readlink|" \ 18 | -e "s|@NIXPROC@|${../../nixproc}|" \ 19 | ${./nixproc-build.in} > $out/bin/nixproc-build 20 | chmod +x $out/bin/nixproc-build 21 | 22 | sed -e "s|/bin/bash|$SHELL|" \ 23 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 24 | -e 's|@DEFAULT_RUNTIME_DIR@|${if stdenv.isLinux || stdenv.isCygwin then "/run" else "/var/run"}|' \ 25 | ${./nixproc-init-state.in} > $out/bin/nixproc-init-state 26 | chmod +x $out/bin/nixproc-init-state 27 | ''; 28 | } 29 | -------------------------------------------------------------------------------- /nixproc/backends/sysvinit/init-functions.nix: -------------------------------------------------------------------------------- 1 | {stdenv, fetchurl, basePackages, runtimeDir}: 2 | 3 | let 4 | basePath = builtins.concatStringsSep ":" (map (package: "${package}/bin") basePackages) + ":\\$PATH"; 5 | 6 | src = fetchurl { 7 | url = http://www.linuxfromscratch.org/lfs/downloads/9.0/lfs-bootscripts-20190524.tar.xz; 8 | sha256 = "0975wmghhh7j5qify0m170ba2d7vl0km7sw05kclnmwpgivimb38"; 9 | }; 10 | in 11 | stdenv.mkDerivation { 12 | name = "init-functions"; 13 | 14 | buildCommand = '' 15 | tar xfv ${src} lfs-bootscripts-20190524/lfs/lib/services/init-functions 16 | 17 | sed \ 18 | -e "s|/bin:/usr/bin:/sbin:/usr/sbin|${basePath}|" \ 19 | -e "s|/bin/sh|${stdenv.shell}|" \ 20 | -e "s|/bin/echo|echo|" \ 21 | -e "s|/bin/head|head|" \ 22 | -e "s|/var/run|${runtimeDir}|" \ 23 | -e "s|/run/bootlog|${runtimeDir}/bootlog|" \ 24 | lfs-bootscripts-20190524/lfs/lib/services/init-functions > $out 25 | ''; 26 | } 27 | -------------------------------------------------------------------------------- /webapp/README.md: -------------------------------------------------------------------------------- 1 | Test web application 2 | ==================== 3 | This is a very simple test web application that can run in foreground mode and 4 | daemon mode. Its only purpose is to return a very simple static HTML page. 5 | 6 | The most interesting part of this example is probably the daemonize 7 | infrastructure (`daemonize.h`, `daemonize.c`) -- I have been trying to closely 8 | follow systemd's recommendations for implementing traditional SysV daemons 9 | (more info:`man 7 daemon`) sticking myself to POSIX standards as much as 10 | possible. 11 | 12 | To keep the code as clear as possible, I have encapsulated each recommended 13 | step into a function abstraction, and every failure yields a distinct error 14 | code so that we can easily trace the origins of the error. 15 | 16 | The daemonize infrastructure is very generic -- you only need to provide a 17 | pointer to a function that initializes the daemon's state and a pointer to 18 | a function that runs the main loop. 19 | -------------------------------------------------------------------------------- /tests/services/docker/processes.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , libDir ? "${stateDir}/lib" 8 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 9 | , forceDisableUserChange ? false 10 | , processManager 11 | }: 12 | 13 | let 14 | constructors = import ../../../examples/services-agnostic/constructors/constructors.nix { 15 | inherit pkgs stateDir runtimeDir logDir tmpDir cacheDir libDir forceDisableUserChange processManager; 16 | }; 17 | in 18 | rec { 19 | docker = { 20 | pkg = constructors.docker {}; 21 | }; 22 | 23 | docker-secondary = rec { 24 | pkg = constructors.docker { 25 | instanceSuffix = "-secondary"; 26 | extraArgs = [ "--iptables=false" ]; # Avoids conflicting NAT settings with the primary instances 27 | }; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /nixproc/backends/sysvinit/image-steps/sysvinit-dynamic.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | sysvinitTools = (import ../../../../tools { 5 | inherit pkgs; 6 | inherit (common) system; 7 | }).sysvinit; 8 | 9 | generateCompoundProxy = import ../../util/generate-compound-proxy.nix { 10 | inherit (pkgs) stdenv lib writeTextFile; 11 | }; 12 | 13 | runlevel = "3"; 14 | 15 | script = generateCompoundProxy { 16 | startCommand = "${sysvinitTools}/bin/nixproc-sysvinit-runactivity --runlevel ${runlevel} start /"; 17 | stopCommand = "${sysvinitTools}/bin/nixproc-sysvinit-runactivity --runlevel ${runlevel} -r stop /"; 18 | }; 19 | in 20 | result // { 21 | runAsRoot = result.runAsRoot or "" + '' 22 | # Make symlink to processes profile 23 | ln -s /nix/var/nix/profiles/processes/etc/rc.d /etc/rc.d 24 | ''; 25 | config = result.config or {} // { 26 | Cmd = [ script ]; 27 | }; 28 | contents = result.contents or [] ++ [ sysvinitTools ]; 29 | } 30 | -------------------------------------------------------------------------------- /examples/services-agnostic/constructors/supervisord/extendable.nix: -------------------------------------------------------------------------------- 1 | {createManagedProcess, writeTextFile, supervisor, runtimeDir, logDir, libDir}: 2 | 3 | { instanceSuffix ? "" 4 | , instanceName ? "supervisord${instanceSuffix}" 5 | , inetHTTPServerPort ? 9001 6 | , postInstall ? "" 7 | }: 8 | 9 | let 10 | includeDir = "${libDir}/${instanceName}/conf.d"; 11 | in 12 | import ./default.nix { 13 | inherit createManagedProcess supervisor logDir runtimeDir; 14 | } { 15 | inherit instanceName postInstall; 16 | 17 | initialize = '' 18 | mkdir -p ${includeDir} 19 | ''; 20 | configFile = writeTextFile { 21 | name = "supervisord.conf"; 22 | text = '' 23 | [supervisord] 24 | 25 | [include] 26 | files=${includeDir}/* 27 | 28 | [inet_http_server] 29 | port = 127.0.0.1:${toString inetHTTPServerPort} 30 | 31 | [rpcinterface:supervisor] 32 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 33 | ''; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /nixproc/backends/systemd/test-module/xserver-autologin-module.nix: -------------------------------------------------------------------------------- 1 | {lib, ...}: 2 | 3 | { 4 | # Xserver + PAM only needed for unprivileged systemd deployments 5 | services.xserver = { 6 | enable = true; 7 | 8 | displayManager.autoLogin = { 9 | enable = true; 10 | user = "unprivileged"; 11 | }; 12 | 13 | # Use IceWM as the window manager. 14 | # Don't use a desktop manager. 15 | displayManager.defaultSession = lib.mkDefault "none+icewm"; 16 | windowManager.icewm.enable = true; 17 | }; 18 | 19 | # lightdm by default doesn't allow auto login for root, which is 20 | # required by some nixos tests. Override it here. 21 | security.pam.services.lightdm-autologin.text = lib.mkForce '' 22 | auth requisite pam_nologin.so 23 | auth required pam_succeed_if.so quiet 24 | auth required pam_permit.so 25 | 26 | account include lightdm 27 | 28 | password include lightdm 29 | 30 | session include lightdm 31 | ''; 32 | } 33 | -------------------------------------------------------------------------------- /tests/services/s6-svscan/processes.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , libDir ? "${stateDir}/lib" 8 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 9 | , forceDisableUserChange ? false 10 | , processManager 11 | }: 12 | 13 | let 14 | constructors = import ../../../examples/services-agnostic/constructors/constructors.nix { 15 | inherit pkgs stateDir runtimeDir logDir tmpDir cacheDir libDir forceDisableUserChange processManager; 16 | }; 17 | in 18 | rec { 19 | s6-svscan-primary = rec { 20 | instanceSuffix = "-primary"; 21 | pkg = constructors.s6-svscan { 22 | inherit instanceSuffix; 23 | }; 24 | }; 25 | 26 | s6-svscan-secondary = rec { 27 | instanceSuffix = "-secondary"; 28 | pkg = constructors.s6-svscan { 29 | inherit instanceSuffix; 30 | }; 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/constructors/webapp/default.nix: -------------------------------------------------------------------------------- 1 | {createManagedProcess, tmpDir}: 2 | {port, instanceSuffix ? "", instanceName ? "webapp${instanceSuffix}"}: 3 | 4 | let 5 | webapp = import ../../../../webapp; 6 | in 7 | createManagedProcess { 8 | description = "Simple web application"; 9 | inherit instanceName; 10 | 11 | # This expression can both run in foreground or daemon mode. 12 | # The process manager can pick which mode it prefers. 13 | process = "${webapp}/bin/webapp"; 14 | daemonArgs = [ "-D" ]; 15 | 16 | environment = { 17 | PORT = port; 18 | PID_FILE = "${tmpDir}/${instanceName}.pid"; 19 | }; 20 | user = instanceName; 21 | credentials = { 22 | groups = { 23 | "${instanceName}" = {}; 24 | }; 25 | users = { 26 | "${instanceName}" = { 27 | group = instanceName; 28 | description = "Webapp"; 29 | }; 30 | }; 31 | }; 32 | 33 | overrides = { 34 | sysvinit = { 35 | runlevels = [ 3 4 5 ]; 36 | }; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/steps/su-pam.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | result // { 4 | runAsRoot = result.runAsRoot or "" + '' 5 | mkdir -p /etc/pam.d 6 | cat > /etc/pam.d/su < /etc/nsswitch.conf < $out/bin/nixproc-bsdrc-switch 12 | chmod +x $out/bin/nixproc-bsdrc-switch 13 | 14 | sed -e "s|/bin/bash|$SHELL|" \ 15 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 16 | -e "s|@sed@|$(type -p sed)|" \ 17 | -e "s|@commonchecks@|${../commonchecks}|" \ 18 | ${./nixproc-bsdrc-deploy.in} > $out/bin/nixproc-bsdrc-deploy 19 | chmod +x $out/bin/nixproc-bsdrc-deploy 20 | 21 | sed -e "s|/bin/bash|$SHELL|" \ 22 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 23 | -e "s|@commonchecks@|${../commonchecks}|" \ 24 | ${./nixproc-bsdrc-runactivity.in} > $out/bin/nixproc-bsdrc-runactivity 25 | chmod +x $out/bin/nixproc-bsdrc-runactivity 26 | ''; 27 | } 28 | -------------------------------------------------------------------------------- /nixproc/backends/s6-rc/create-service-bundle.nix: -------------------------------------------------------------------------------- 1 | {stdenv, lib}: 2 | 3 | { name 4 | # When a service is flagged as essential it will not stop with the command: s6-rc -d change foo, but only: s6-rc -D change foo 5 | , flagEssential ? false 6 | # List of s6-rc services that are in the bundle 7 | , contents ? [] 8 | # Arbitrary commands executed after generating the configuration files 9 | , postInstall ? "" 10 | }: 11 | 12 | let 13 | util = import ./util.nix { 14 | inherit lib; 15 | }; 16 | in 17 | stdenv.mkDerivation { 18 | inherit name; 19 | buildCommand = '' 20 | mkdir -p $out/etc/s6/sv/${name} 21 | cd $out/etc/s6/sv/${name} 22 | '' 23 | + util.generateStringProperty { value = "bundle"; filename = "type"; } 24 | + util.generateBooleanProperty { value = flagEssential; filename = "flag-essential"; } 25 | + (if contents == [] then util.generateBooleanProperty { value = true; filename = "contents"; } 26 | else util.generateServiceNameList { services = contents; filename = "contents"; } 27 | ) 28 | + postInstall; 29 | } 30 | -------------------------------------------------------------------------------- /examples/services-agnostic/processes.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , libDir ? "${stateDir}/lib" 8 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 9 | , forceDisableUserChange ? false 10 | , processManager 11 | }: 12 | 13 | let 14 | ids = if builtins.pathExists ./ids.nix then (import ./ids.nix).ids else {}; 15 | 16 | constructors = import ./constructors/constructors.nix { 17 | inherit pkgs stateDir runtimeDir logDir tmpDir cacheDir libDir forceDisableUserChange processManager ids; 18 | }; 19 | in 20 | rec { 21 | supervisord = rec { 22 | inetHTTPServerPort = ids.inetHTTPPorts.supervisord or 0; 23 | 24 | pkg = constructors.extendableSupervisord { 25 | inherit inetHTTPServerPort; 26 | }; 27 | 28 | requiresUniqueIdsFor = [ "inetHTTPPorts" ]; 29 | }; 30 | 31 | docker = { 32 | pkg = constructors.docker {}; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /nixproc/backends/disnix/image-steps/disnix-static.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | generateCompoundProxy = import ../../util/generate-compound-proxy.nix { 5 | inherit (pkgs) stdenv lib writeTextFile; 6 | }; 7 | 8 | disnixDataDir = "${pkgs.disnix}/share/disnix"; 9 | 10 | profile = import ../build-disnix-env.nix { 11 | inherit pkgs disnixDataDir; 12 | inherit (common) system; 13 | inherit (input) exprFile stateDir runtimeDir forceDisableUserChange extraParams; 14 | }; 15 | 16 | emptyProfile = import ../build-disnix-env.nix { 17 | inherit pkgs disnixDataDir; 18 | inherit (common) system; 19 | inherit (input) stateDir runtimeDir forceDisableUserChange extraParams; 20 | exprFile = null; 21 | }; 22 | 23 | script = generateCompoundProxy { 24 | path = [ pkgs.dysnomia pkgs.disnix ]; 25 | startCommand = "disnix-activate ${profile}"; 26 | stopCommand = "disnix-activate -o ${profile} ${emptyProfile}"; 27 | }; 28 | in 29 | result // { 30 | config = result.config or {} // { 31 | Cmd = [ script ]; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/constructors/nginx/default.nix: -------------------------------------------------------------------------------- 1 | {createSystemVInitScript, lib, nginx, stateDir, runtimeDir, cacheDir, forceDisableUserChange}: 2 | {configFile, dependencies ? [], instanceSuffix ? "", instanceName ? "nginx${instanceSuffix}"}: 3 | 4 | let 5 | user = instanceName; 6 | group = instanceName; 7 | nginxLogDir = "${stateDir}/logs"; 8 | nginxCacheDir = "${cacheDir}/${instanceName}"; 9 | in 10 | createSystemVInitScript { 11 | description = "Nginx"; 12 | 13 | initialize = '' 14 | mkdir -p ${nginxLogDir} 15 | mkdir -p ${nginxCacheDir} 16 | 17 | ${lib.optionalString (!forceDisableUserChange) '' 18 | chown ${user}:${group} ${nginxLogDir} 19 | ''} 20 | ''; 21 | process = "${nginx}/bin/nginx"; 22 | args = [ "-c" configFile "-p" stateDir ]; 23 | runlevels = [ 3 4 5 ]; 24 | 25 | inherit dependencies instanceName; 26 | 27 | credentials = { 28 | groups = { 29 | "${group}" = {}; 30 | }; 31 | users = { 32 | "${user}" = { 33 | inherit group; 34 | description = "Nginx user"; 35 | }; 36 | }; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /tools/s6-rc/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-s6-rc-tools"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | sed -e "s|/bin/bash|$SHELL|" \ 9 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 10 | -e "s|@commonchecks@|${../commonchecks}|" \ 11 | ${./nixproc-s6-rc-switch.in} > $out/bin/nixproc-s6-rc-switch 12 | chmod +x $out/bin/nixproc-s6-rc-switch 13 | 14 | sed -e "s|/bin/bash|$SHELL|" \ 15 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 16 | -e "s|@readlink@|$(type -p readlink)|g" \ 17 | -e "s|@commonchecks@|${../commonchecks}|" \ 18 | -e "s|@s6rcchecks@|${./s6-rc-checks}|" \ 19 | ${./nixproc-s6-rc-deploy.in} > $out/bin/nixproc-s6-rc-deploy 20 | chmod +x $out/bin/nixproc-s6-rc-deploy 21 | 22 | sed -e "s|/bin/bash|$SHELL|" \ 23 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 24 | -e "s|@commonchecks@|${../commonchecks}|" \ 25 | -e "s|@s6rcchecks@|${./s6-rc-checks}|" \ 26 | ${./nixproc-s6-svscan.in} > $out/bin/nixproc-s6-svscan 27 | chmod +x $out/bin/nixproc-s6-svscan 28 | ''; 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2021 Sander van der Burg 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/steps/bootstrap-init.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | cmd = pkgs.lib.escapeShellArgs result.config.Cmd; 5 | 6 | channelURL = input.channelURL or "https://nixos.org/channels/nixpkgs-unstable"; 7 | in 8 | result // pkgs.lib.optionalAttrs (!(input ? bootstrap) || input.bootstrap) { 9 | runAsRoot = result.runAsRoot or "" + '' 10 | cat > /bin/bootstrap < /bin/bootstrap < ${filename} 12 | ''; 13 | 14 | generateIntProperty = {value, filename}: 15 | lib.optionalString (value != null) '' 16 | echo "${toString value}" > ${filename} 17 | ''; 18 | 19 | copyFile = {path, filename}: 20 | lib.optionalString (path != null) '' 21 | cp ${path} ${filename} 22 | ''; 23 | 24 | copyDir = {path, filename}: 25 | lib.optionalString (path != null) '' 26 | cp -rLv ${path} ${filename} 27 | ''; 28 | 29 | generateServiceName = {service, filename}: 30 | lib.optionalString (service != null) '' 31 | echo "${service.name}" > ${filename} 32 | ''; 33 | 34 | generateServiceNameList = {services, filename}: 35 | lib.optionalString (services != []) 36 | (lib.concatMapStrings (service: '' 37 | echo "${service.name}" >> ${filename} 38 | '') services); 39 | } 40 | -------------------------------------------------------------------------------- /nixproc/backends/bsdrc/build-bsdrc-env.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${cacheDir}/cache" 7 | , spoolDir ? "${stateDir}/spool" 8 | , lockDir ? "${stateDir}/lock" 9 | , libDir ? "${stateDir}/lib" 10 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 11 | , forceDisableUserChange ? false 12 | , callingUser ? null 13 | , callingGroup ? null 14 | , exprFile ? null 15 | , extraParams ? {} 16 | }@args: 17 | 18 | let 19 | processesFun = import exprFile; 20 | 21 | processesFormalArgs = builtins.functionArgs processesFun; 22 | 23 | processesArgs = builtins.intersectAttrs processesFormalArgs (args // { 24 | processManager = "bsdrc"; 25 | } // extraParams); 26 | 27 | processes = if exprFile == null then {} else processesFun processesArgs; 28 | in 29 | pkgs.buildEnv { 30 | name = "rc.d"; 31 | paths = map (processName: 32 | let 33 | process = builtins.getAttr processName processes; 34 | in 35 | process.pkg 36 | ) (builtins.attrNames processes); 37 | } 38 | -------------------------------------------------------------------------------- /nixproc/backends/docker/build-docker-env.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , spoolDir ? "${stateDir}/spool" 8 | , lockDir ? "${stateDir}/lock" 9 | , libDir ? "${stateDir}/lib" 10 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 11 | , forceDisableUserChange ? false 12 | , callingUser ? null 13 | , callingGroup ? null 14 | , extraParams ? {} 15 | , exprFile ? null 16 | }@args: 17 | 18 | let 19 | processesFun = import exprFile; 20 | 21 | processesFormalArgs = builtins.functionArgs processesFun; 22 | 23 | processesArgs = builtins.intersectAttrs processesFormalArgs (args // { 24 | processManager = "docker"; 25 | } // extraParams); 26 | 27 | processes = if exprFile == null then {} else processesFun processesArgs; 28 | in 29 | pkgs.buildEnv { 30 | name = "docker"; 31 | paths = map (processName: 32 | let 33 | process = builtins.getAttr processName processes; 34 | in 35 | process.pkg 36 | ) (builtins.attrNames processes); 37 | } 38 | -------------------------------------------------------------------------------- /examples/service-containers-agnostic/extendable.nix: -------------------------------------------------------------------------------- 1 | {supervisordConstructorFun, stdenv, dysnomia, libDir}: 2 | 3 | { instanceSuffix ? "", instanceName ? "supervisord${instanceSuffix}" 4 | , containerName ? "supervisord-program${instanceSuffix}" 5 | , inetHTTPServerPort ? 9001 6 | , postInstall ? "" 7 | , type 8 | , properties ? {} 9 | }: 10 | 11 | let 12 | supervisordTargetDir = "${libDir}/${instanceName}/conf.d"; 13 | 14 | pkg = supervisordConstructorFun { 15 | inherit instanceName inetHTTPServerPort; 16 | postInstall = '' 17 | # Add Dysnomia container configuration file for Supervisord 18 | mkdir -p $out/etc/dysnomia/containers 19 | cat > $out/etc/dysnomia/containers/${containerName} < { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${cacheDir}/cache" 7 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 8 | , spoolDir ? "${stateDir}/spool" 9 | , lockDir ? "${stateDir}/lock" 10 | , libDir ? "${stateDir}/lib" 11 | , forceDisableUserChange ? false 12 | , callingUser ? null 13 | , callingGroup ? null 14 | , extraParams ? {} 15 | , exprFile ? null 16 | }@args: 17 | 18 | let 19 | processesFun = import exprFile; 20 | 21 | processesFormalArgs = builtins.functionArgs processesFun; 22 | 23 | processesArgs = builtins.intersectAttrs processesFormalArgs (args // { 24 | processManager = "launchd"; 25 | } // extraParams); 26 | 27 | processes = if exprFile == null then {} else processesFun processesArgs; 28 | in 29 | pkgs.buildEnv { 30 | name = "launchd"; 31 | paths = map (processName: 32 | let 33 | process = builtins.getAttr processName processes; 34 | in 35 | process.pkg 36 | ) (builtins.attrNames processes); 37 | } 38 | -------------------------------------------------------------------------------- /nixproc/backends/systemd/build-systemd-env.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , spoolDir ? "${stateDir}/spool" 8 | , lockDir ? "${stateDir}/lock" 9 | , libDir ? "${stateDir}/lib" 10 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 11 | , forceDisableUserChange ? false 12 | , callingUser ? null 13 | , callingGroup ? null 14 | , extraParams ? {} 15 | , exprFile ? null 16 | }@args: 17 | 18 | let 19 | processesFun = import exprFile; 20 | 21 | processesFormalArgs = builtins.functionArgs processesFun; 22 | 23 | processesArgs = builtins.intersectAttrs processesFormalArgs (args // { 24 | processManager = "systemd"; 25 | } // extraParams); 26 | 27 | processes = if exprFile == null then {} else processesFun processesArgs; 28 | in 29 | pkgs.buildEnv { 30 | name = "systemd"; 31 | paths = map (processName: 32 | let 33 | process = builtins.getAttr processName processes; 34 | in 35 | process.pkg 36 | ) (builtins.attrNames processes); 37 | } 38 | -------------------------------------------------------------------------------- /nixproc/backends/sysvinit/build-sysvinit-env.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , spoolDir ? "${stateDir}/spool" 8 | , lockDir ? "${stateDir}/lock" 9 | , libDir ? "${stateDir}/lib" 10 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 11 | , forceDisableUserChange ? false 12 | , callingUser ? null 13 | , callingGroup ? null 14 | , extraParams ? {} 15 | , exprFile ? null 16 | }@args: 17 | 18 | let 19 | processesFun = import exprFile; 20 | 21 | processesFormalArgs = builtins.functionArgs processesFun; 22 | 23 | processesArgs = builtins.intersectAttrs processesFormalArgs (args // { 24 | processManager = "sysvinit"; 25 | } // extraParams); 26 | 27 | processes = if exprFile == null then {} else processesFun processesArgs; 28 | in 29 | pkgs.buildEnv { 30 | name = "rc.d"; 31 | paths = map (processName: 32 | let 33 | process = builtins.getAttr processName processes; 34 | in 35 | process.pkg 36 | ) (builtins.attrNames processes); 37 | } 38 | -------------------------------------------------------------------------------- /nixproc/backends/cygrunsrv/build-cygrunsrv-env.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${cacheDir}/cache" 7 | , spoolDir ? "${stateDir}/spool" 8 | , lockDir ? "${stateDir}/lock" 9 | , libDir ? "${stateDir}/lib" 10 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 11 | , forceDisableUserChange ? false 12 | , callingUser ? null 13 | , callingGroup ? null 14 | , extraParams ? {} 15 | , exprFile ? null 16 | }@args: 17 | 18 | let 19 | processesFun = import exprFile; 20 | 21 | processesFormalArgs = builtins.functionArgs processesFun; 22 | 23 | processesArgs = builtins.intersectAttrs processesFormalArgs (args // { 24 | processManager = "cygrunsrv"; 25 | } // extraParams); 26 | 27 | processes = if exprFile == null then {} else processesFun processesArgs; 28 | in 29 | pkgs.buildEnv { 30 | name = "cygrunsrv-env"; 31 | paths = map (processName: 32 | let 33 | process = builtins.getAttr processName processes; 34 | in 35 | process.pkg 36 | ) (builtins.attrNames processes); 37 | } 38 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/processes.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 8 | , forceDisableUserChange ? false 9 | }: 10 | 11 | let 12 | ids = if builtins.pathExists ./ids.nix then (import ./ids.nix).ids else {}; 13 | 14 | constructors = import ./constructors/constructors.nix { 15 | inherit pkgs stateDir runtimeDir logDir cacheDir tmpDir forceDisableUserChange ids; 16 | }; 17 | in 18 | rec { 19 | webapp = rec { 20 | port = ids.webappPorts.webapp or 0; 21 | dnsName = "webapp.local"; 22 | 23 | pkg = constructors.webapp { 24 | inherit port; 25 | }; 26 | 27 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 28 | }; 29 | 30 | nginx = rec { 31 | port = ids.nginxPorts.nginx or 0; 32 | 33 | pkg = constructors.nginxReverseProxy { 34 | webapps = [ webapp ]; 35 | inherit port; 36 | } {}; 37 | 38 | requiresUniqueIdsFor = [ "nginxPorts" "uids" "gids" ]; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /tools/sysvinit/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-sysvinit-tools"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | sed -e "s|/bin/bash|$SHELL|" \ 9 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 10 | -e "s|@commonchecks@|${../commonchecks}|" \ 11 | -e "s|@sysvinitchecks@|${./sysvinitchecks}|" \ 12 | ${./nixproc-sysvinit-deploy.in} > $out/bin/nixproc-sysvinit-deploy 13 | chmod +x $out/bin/nixproc-sysvinit-deploy 14 | 15 | sed -e "s|/bin/bash|$SHELL|" \ 16 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 17 | -e "s|@commonchecks@|${../commonchecks}|" \ 18 | -e "s|@sysvinitchecks@|${./sysvinitchecks}|" \ 19 | ${./nixproc-sysvinit-switch.in} > $out/bin/nixproc-sysvinit-switch 20 | chmod +x $out/bin/nixproc-sysvinit-switch 21 | 22 | sed -e "s|/bin/bash|$SHELL|" \ 23 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 24 | -e "s|@commonchecks@|${../commonchecks}|" \ 25 | -e "s|@sysvinitchecks@|${./sysvinitchecks}|" \ 26 | ${./nixproc-sysvinit-runactivity.in} > $out/bin/nixproc-sysvinit-runactivity 27 | chmod +x $out/bin/nixproc-sysvinit-runactivity 28 | ''; 29 | } 30 | -------------------------------------------------------------------------------- /nixproc/backends/bsdrc/generate-bsdrc-script.nix: -------------------------------------------------------------------------------- 1 | { createBSDRCScript, lib }: 2 | 3 | { name 4 | , description 5 | , initialize 6 | , daemon 7 | , daemonArgs 8 | , instanceName 9 | , pidFile 10 | , foregroundProcess 11 | , foregroundProcessArgs 12 | , path 13 | , environment 14 | , directory 15 | , umask 16 | , nice 17 | , user 18 | , dependencies 19 | , credentials 20 | , overrides 21 | , postInstall 22 | }: 23 | 24 | let 25 | generatedTargetSpecificArgs = { 26 | inherit name environment path directory nice umask dependencies; 27 | inherit user instanceName credentials postInstall; 28 | 29 | command = if daemon != null then daemon else foregroundProcess; 30 | commandIsDaemon = daemon != null; 31 | commandArgs = if daemon != null then daemonArgs else foregroundProcessArgs; 32 | } // lib.optionalAttrs (pidFile != null) { 33 | inherit pidFile; 34 | } // lib.optionalAttrs (initialize != "") { 35 | commands.start.pre = initialize; 36 | }; 37 | 38 | targetSpecificArgs = 39 | if builtins.isFunction overrides then overrides generatedTargetSpecificArgs 40 | else lib.recursiveUpdate generatedTargetSpecificArgs overrides; 41 | in 42 | createBSDRCScript targetSpecificArgs 43 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/constructors/constructors.nix: -------------------------------------------------------------------------------- 1 | { pkgs 2 | , stateDir 3 | , cacheDir 4 | , logDir 5 | , runtimeDir 6 | , tmpDir 7 | , forceDisableUserChange 8 | , ids ? {} 9 | }: 10 | 11 | let 12 | createSystemVInitScript = import ../../../nixproc/backends/sysvinit/create-sysvinit-script.nix { 13 | inherit (pkgs) stdenv writeTextFile lib daemon; 14 | inherit runtimeDir logDir tmpDir forceDisableUserChange; 15 | 16 | createCredentials = import ../../../nixproc/create-credentials { 17 | inherit (pkgs) stdenv lib; 18 | inherit ids forceDisableUserChange; 19 | }; 20 | 21 | initFunctions = import ../../../nixproc/backends/sysvinit/init-functions.nix { 22 | basePackages = [ pkgs.coreutils pkgs.gnused pkgs.inetutils pkgs.gnugrep pkgs.sysvinit ]; 23 | inherit (pkgs) stdenv fetchurl; 24 | inherit runtimeDir; 25 | }; 26 | }; 27 | in 28 | { 29 | webapp = import ./webapp { 30 | inherit createSystemVInitScript tmpDir; 31 | }; 32 | 33 | nginxReverseProxy = import ./nginx/nginx-reverse-proxy.nix { 34 | inherit createSystemVInitScript stateDir logDir cacheDir runtimeDir forceDisableUserChange; 35 | inherit (pkgs) stdenv lib writeTextFile nginx; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/services.nix: -------------------------------------------------------------------------------- 1 | { pkgs, distribution, invDistribution, system 2 | , stateDir ? "/var" 3 | , runtimeDir ? "${stateDir}/run" 4 | , logDir ? "${stateDir}/log" 5 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 6 | , forceDisableUserChange ? true 7 | }: 8 | 9 | let 10 | ids = if builtins.pathExists ./ids.nix then (import ./ids.nix).ids else {}; 11 | 12 | constructors = import ./constructors.nix { 13 | inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange; 14 | }; 15 | in 16 | rec { 17 | webapp = rec { 18 | name = "webapp"; 19 | port = ids.webappPorts.webapp or 0; 20 | dnsName = "webapp.local"; 21 | pkg = constructors.webapp { 22 | inherit port; 23 | }; 24 | type = "sysvinit-script"; 25 | 26 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 27 | }; 28 | 29 | nginxReverseProxy = rec { 30 | name = "nginxReverseProxy"; 31 | port = ids.nginxPorts.nginx or 0; 32 | pkg = constructors.nginxReverseProxy { 33 | inherit port; 34 | }; 35 | dependsOn = { 36 | inherit webapp; 37 | }; 38 | type = "sysvinit-script"; 39 | 40 | requiresUniqueIdsFor = [ "nginxPorts" "uids" "gids" ]; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/build-supervisord-env.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , spoolDir ? "${stateDir}/spool" 8 | , lockDir ? "${stateDir}/lock" 9 | , libDir ? "${stateDir}/lib" 10 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 11 | , forceDisableUserChange ? false 12 | , callingUser ? null 13 | , callingGroup ? null 14 | , extraParams ? {} 15 | , exprFile ? null 16 | }@args: 17 | 18 | let 19 | processesFun = import exprFile; 20 | 21 | processesFormalArgs = builtins.functionArgs processesFun; 22 | 23 | processesArgs = builtins.intersectAttrs processesFormalArgs (args // { 24 | processManager = "supervisord"; 25 | } // extraParams); 26 | 27 | processes = if exprFile == null then {} else processesFun processesArgs; 28 | in 29 | pkgs.buildEnv { 30 | name = "supervisord.d"; 31 | paths = map (processName: 32 | let 33 | process = builtins.getAttr processName processes; 34 | in 35 | process.pkg 36 | ) (builtins.attrNames processes); 37 | postBuild = '' 38 | cp ${./supervisord.conf} $out/supervisord.conf 39 | ''; 40 | } 41 | -------------------------------------------------------------------------------- /nixproc/backends/sysvinit/image-steps/sysvinit-static.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | sysvinitTools = (import ../../../../tools { 5 | inherit pkgs; 6 | inherit (common) system; 7 | }).sysvinit; 8 | 9 | generateCompoundProxy = import ../../util/generate-compound-proxy.nix { 10 | inherit (pkgs) stdenv lib writeTextFile; 11 | }; 12 | 13 | runlevel = "3"; 14 | 15 | script = generateCompoundProxy { 16 | startCommand = "${sysvinitTools}/bin/nixproc-sysvinit-runactivity start ${profile}"; 17 | stopCommand = "${sysvinitTools}/bin/nixproc-sysvinit-runactivity -r stop ${profile}"; 18 | }; 19 | 20 | profile = import ../build-sysvinit-env.nix { 21 | inherit (input) exprFile stateDir runtimeDir forceDisableUserChange extraParams; 22 | }; 23 | in 24 | result // { 25 | runAsRoot = result.runAsRoot or "" + '' 26 | ln -s ${profile}/etc/rc.d /etc/rc.d 27 | 28 | ${pkgs.lib.optionalString (!input.forceDisableUserChange) '' 29 | export PATH=$PATH:${pkgs.findutils}/bin:${pkgs.glibc.bin}/bin 30 | ${pkgs.dysnomia}/bin/dysnomia-addgroups ${profile} 31 | ${pkgs.dysnomia}/bin/dysnomia-addusers ${profile} 32 | ''} 33 | ''; 34 | config = result.config or {} // { 35 | Cmd = [ script ]; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /tests/services/docker/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, testService, processManagers, profiles }: 2 | 3 | testService { 4 | name = "docker"; 5 | exprFile = ./processes.nix; 6 | systemPackages = [ pkgs.docker ]; 7 | 8 | readiness = {instanceName, instance, runtimeDir, ...}: 9 | '' 10 | machine.wait_for_file("${runtimeDir}/${instanceName}.sock") 11 | ''; 12 | 13 | tests = {instanceName, instance, stateDir, runtimeDir, forceDisableUserChange, ...}: 14 | # The primary instance should be connectible with the default parameters 15 | if instanceName == "docker" && !forceDisableUserChange then '' 16 | machine.succeed("docker info | grep 'Docker Root Dir: ${stateDir}/lib/${instanceName}'") 17 | '' else '' 18 | machine.succeed( 19 | "docker --host=unix://${runtimeDir}/${instanceName}.sock info | grep 'Docker Root Dir: ${stateDir}/lib/${instanceName}'" 20 | ) 21 | ''; 22 | 23 | # It is useless to run Docker in Docker 24 | processManagers = builtins.filter (processManager: processManager != "docker") processManagers; 25 | 26 | # There's an experimental rootless feature for Docker, but a hassle to setup. As a result, we disable unprivileged mode 27 | profiles = builtins.filter (profile: profile == "privileged") profiles; 28 | } 29 | -------------------------------------------------------------------------------- /tests/services/supervisord/processes.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , libDir ? "${stateDir}/lib" 8 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 9 | , forceDisableUserChange ? false 10 | , processManager 11 | }: 12 | 13 | let 14 | constructors = import ../../../examples/services-agnostic/constructors/constructors.nix { 15 | inherit pkgs stateDir runtimeDir logDir tmpDir cacheDir libDir forceDisableUserChange processManager; 16 | }; 17 | in 18 | rec { 19 | supervisord-primary = rec { 20 | # Special situation: we can only bootstrap supervisord with supervisord if we don't conflict with the managing supervisord's port 21 | port = if processManager == "supervisord" then 9003 else 9001; 22 | 23 | pkg = constructors.extendableSupervisord { 24 | inetHTTPServerPort = port; 25 | instanceSuffix = "-primary"; 26 | }; 27 | }; 28 | 29 | supervisord-secondary = rec { 30 | port = 9002; 31 | 32 | pkg = constructors.extendableSupervisord { 33 | inetHTTPServerPort = port; 34 | instanceSuffix = "-secondary"; 35 | }; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /nixproc/backends/s6-rc/create-log-service-for-longrun-service.nix: -------------------------------------------------------------------------------- 1 | {stdenv, lib, execline, logDir, logDirUser, logDirGroup, forceDisableUserChange}: 2 | {name}: 3 | 4 | let 5 | serviceName = "${name}-log"; 6 | 7 | util = import ./util.nix { 8 | inherit lib; 9 | }; 10 | 11 | serviceLogDir = "${logDir}/s6-log/${name}"; 12 | 13 | notificationFd = 3; 14 | in 15 | stdenv.mkDerivation { 16 | name = serviceName; 17 | buildCommand = '' 18 | mkdir -p $out/etc/s6/sv/${serviceName} 19 | cd $out/etc/s6/sv/${serviceName} 20 | '' 21 | + util.generateStringProperty { value = "longrun"; filename = "type"; } 22 | + '' 23 | cat > run < 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | if(argc < 3) 10 | { 11 | printf("Usage: %s username command [args]\n", argv[0]); 12 | return 1; 13 | } 14 | else 15 | { 16 | char *username = argv[1]; 17 | 18 | /* Query password entry for the user */ 19 | struct passwd *pwentry = getpwnam(username); 20 | 21 | if(pwentry == NULL) 22 | { 23 | fprintf(stderr, "Cannot find password entry for user: %s\n", username); 24 | return 1; 25 | } 26 | 27 | /* Change user permissions to the requested user */ 28 | if(setgid(pwentry->pw_gid) == 0 && setuid(pwentry->pw_uid) == 0) 29 | { 30 | /* Exec into the requested process */ 31 | char **cmd_argv = argv + 2; 32 | execvp(cmd_argv[0], cmd_argv); 33 | fprintf(stderr, "Cannot execute command: %s\n", cmd_argv[0]); 34 | return 1; 35 | } 36 | else 37 | { 38 | fprintf(stderr, "Cannot change into user: %s with corresponding group!\n", username); 39 | return 1; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tools/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | }: 4 | 5 | rec { 6 | chainload-user = import ./chainload-user { 7 | inherit (pkgs) stdenv; 8 | }; 9 | 10 | common = import ./common { 11 | inherit (pkgs) stdenv getopt; 12 | }; 13 | 14 | generate-config = import ./generate-config { 15 | inherit (pkgs) stdenv getopt; 16 | }; 17 | 18 | bsdrc = import ./bsdrc { 19 | inherit (pkgs) stdenv getopt; 20 | }; 21 | 22 | cygrunsrv = import ./cygrunsrv { 23 | inherit (pkgs) stdenv getopt; 24 | }; 25 | 26 | disnix = import ./disnix { 27 | inherit (pkgs) stdenv getopt; 28 | }; 29 | 30 | docker = import ./docker { 31 | inherit (pkgs) stdenv getopt; 32 | }; 33 | 34 | idassign = import ./idassign { 35 | inherit (pkgs) stdenv getopt; 36 | }; 37 | 38 | launchd = import ./launchd { 39 | inherit (pkgs) stdenv getopt; 40 | }; 41 | 42 | s6-rc = import ./s6-rc { 43 | inherit (pkgs) stdenv getopt; 44 | }; 45 | 46 | supervisord = import ./supervisord { 47 | inherit (pkgs) stdenv getopt; 48 | }; 49 | 50 | systemd = import ./systemd { 51 | inherit (pkgs) stdenv getopt; 52 | }; 53 | 54 | sysvinit = import ./sysvinit { 55 | inherit (pkgs) stdenv getopt; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /nixproc/backends/disnix/image-steps/disnix-dynamic.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | disnixTools = (import ../../../../tools { 5 | inherit pkgs; 6 | inherit (common) system; 7 | }).disnix; 8 | 9 | generateCompoundProxy = import ../../util/generate-compound-proxy.nix { 10 | inherit (pkgs) stdenv lib writeTextFile; 11 | }; 12 | 13 | disnixDataDir = "${pkgs.disnix}/share/disnix"; 14 | 15 | emptyProfile = import ../build-disnix-env.nix { 16 | inherit pkgs disnixDataDir; 17 | inherit (common) system; 18 | inherit (input) stateDir runtimeDir forceDisableUserChange extraParams; 19 | exprFile = null; 20 | }; 21 | 22 | profilePath = "/nix/var/nix/profiles/per-user/root/disnix-coordinator/default"; 23 | 24 | script = generateCompoundProxy { 25 | path = [ pkgs.dysnomia pkgs.disnix ]; 26 | startCommand = "disnix-activate -o ${emptyProfile} ${profilePath}"; 27 | stopCommand = "disnix-activate -o ${profilePath} ${emptyProfile}"; 28 | }; 29 | in 30 | result // { 31 | runAsRoot = result.runAsRoot or "" + '' 32 | mkdir -p "$(dirname ${profilePath})" 33 | ln -s ${emptyProfile} ${profilePath} 34 | ''; 35 | 36 | contents = result.contents or [] ++ [ disnixTools ]; 37 | config = result.config or {} // { 38 | Cmd = [ script ]; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /examples/services-agnostic/constructors/docker/default.nix: -------------------------------------------------------------------------------- 1 | {createManagedProcess, docker, kmod, runtimeDir, libDir}: 2 | {instanceSuffix ? "", instanceName ? "docker${instanceSuffix}", extraArgs ? []}: 3 | 4 | let 5 | user = instanceName; 6 | group = instanceName; 7 | in 8 | createManagedProcess { 9 | inherit instanceName; 10 | foregroundProcess = "${docker}/bin/dockerd"; 11 | args = [ 12 | "--group=${group}" 13 | "--host=unix://${runtimeDir}/${instanceName}.sock" 14 | # Add -alt suffix. We only need PID files for the backends that requires processes to daemonize on their own. 15 | # The `daemon` command will create PID files for them. Without the -alt suffix they will conflict causing the Docker daemon to refuse to start. 16 | "--pidfile=${runtimeDir}/${instanceName}-alt.pid" 17 | "--data-root=${libDir}/${instanceName}" 18 | "--exec-root=${runtimeDir}/${instanceName}" 19 | "--log-driver=json-file" 20 | ] ++ extraArgs; 21 | path = [ kmod ]; 22 | 23 | credentials = { 24 | groups = { 25 | "${group}" = {}; 26 | }; 27 | users = { 28 | "${user}" = { 29 | inherit group; 30 | description = "Docker user"; 31 | }; 32 | }; 33 | }; 34 | 35 | overrides = { 36 | sysvinit = { 37 | runlevels = [ 3 4 5 ]; 38 | }; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /examples/services-agnostic/constructors/nginx/default.nix: -------------------------------------------------------------------------------- 1 | {createManagedProcess, lib, nginx, stateDir, runtimeDir, cacheDir, forceDisableUserChange}: 2 | {configFile, dependencies ? [], instanceSuffix ? "", instanceName ? "nginx${instanceSuffix}"}: 3 | 4 | let 5 | user = instanceName; 6 | group = instanceName; 7 | 8 | nginxStateDir = "${stateDir}/${instanceName}"; 9 | nginxLogDir = "${nginxStateDir}/logs"; 10 | nginxCacheDir = "${cacheDir}/${instanceName}"; 11 | in 12 | createManagedProcess { 13 | description = "Nginx"; 14 | initialize = '' 15 | mkdir -p ${nginxLogDir} 16 | mkdir -p ${nginxCacheDir} 17 | ${lib.optionalString (!forceDisableUserChange) '' 18 | chown ${user}:${group} ${nginxLogDir} 19 | chown ${user}:${group} ${nginxCacheDir} 20 | ''} 21 | ''; 22 | process = "${nginx}/bin/nginx"; 23 | args = [ "-p" "${nginxStateDir}" "-c" configFile ]; 24 | foregroundProcessExtraArgs = [ "-g" "daemon off;" ]; 25 | 26 | inherit dependencies instanceName; 27 | 28 | credentials = { 29 | groups = { 30 | "${group}" = {}; 31 | }; 32 | users = { 33 | "${user}" = { 34 | inherit group; 35 | description = "Nginx user"; 36 | }; 37 | }; 38 | }; 39 | 40 | overrides = { 41 | sysvinit = { 42 | runlevels = [ 3 4 5 ]; 43 | }; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /nixproc/backends/s6-rc/test-module/default.nix: -------------------------------------------------------------------------------- 1 | {profileSettings, exprFile, extraParams, tools, pkgs, system}: 2 | 3 | let 4 | executeDeploy = import ../../../test-driver/util/execute-deploy.nix { 5 | inherit (pkgs) lib; 6 | }; 7 | 8 | processesEnvProcessManager = import ../../sysvinit/build-sysvinit-env.nix ({ 9 | inherit pkgs system; 10 | exprFile = ./processes-s6-svscan.nix; 11 | } // profileSettings.params); 12 | 13 | processesEnvSystem = import ../build-s6-rc-env.nix ({ 14 | inherit pkgs system exprFile extraParams; 15 | } // profileSettings.params); 16 | in 17 | { 18 | inherit (profileSettings) params; 19 | 20 | nixosModules = []; 21 | 22 | systemPackages = [ 23 | tools.sysvinit 24 | tools.s6-rc 25 | pkgs.s6-rc 26 | ]; 27 | 28 | additionalPaths = [ processesEnvProcessManager processesEnvSystem ]; 29 | 30 | deployProcessManager = '' 31 | machine.succeed( 32 | "${executeDeploy { inherit profileSettings; processManager = "sysvinit"; processesEnv = processesEnvProcessManager; }}" 33 | ) 34 | machine.wait_for_file("${profileSettings.params.runtimeDir}/service/.s6-svscan") 35 | ''; 36 | 37 | deploySystem = '' 38 | machine.succeed( 39 | "${executeDeploy { inherit profileSettings; processManager = "s6-rc"; processesEnv = processesEnvSystem; }}" 40 | ) 41 | ''; 42 | } 43 | -------------------------------------------------------------------------------- /webapp/service.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Sander van der Burg 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __SERVICE_H 24 | #define __SERVICE_H 25 | #include "daemonize.h" 26 | 27 | int run_foreground_process(int port); 28 | 29 | DaemonStatus run_daemon(int port, const char *pid_file); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /tools/supervisord/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, getopt}: 2 | 3 | stdenv.mkDerivation { 4 | name = "nixproc-supervisord-tools"; 5 | buildCommand = '' 6 | mkdir -p $out/bin 7 | 8 | sed -e "s|/bin/bash|$SHELL|" \ 9 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 10 | -e "s|@readlink@|$(type -p readlink)|" \ 11 | -e "s|@commonchecks@|${../commonchecks}|" \ 12 | -e "s|@supervisordchecks@|${./supervisordchecks}|" \ 13 | ${./nixproc-supervisord-switch.in} > $out/bin/nixproc-supervisord-switch 14 | chmod +x $out/bin/nixproc-supervisord-switch 15 | 16 | sed -e "s|/bin/bash|$SHELL|" \ 17 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 18 | -e "s|@readlink@|$(type -p readlink)|" \ 19 | -e "s|@commonchecks@|${../commonchecks}|" \ 20 | -e "s|@supervisordchecks@|${./supervisordchecks}|" \ 21 | ${./nixproc-supervisord-deploy.in} > $out/bin/nixproc-supervisord-deploy 22 | chmod +x $out/bin/nixproc-supervisord-deploy 23 | 24 | sed -e "s|/bin/bash|$SHELL|" \ 25 | -e "s|@getopt@|${getopt}/bin/getopt|" \ 26 | -e "s|@readlink@|$(type -p readlink)|" \ 27 | -e "s|@commonchecks@|${../commonchecks}|" \ 28 | -e "s|@supervisordchecks@|${./supervisordchecks}|" \ 29 | ${./nixproc-supervisord-deploy-stateless.in} > $out/bin/nixproc-supervisord-deploy-stateless 30 | chmod +x $out/bin/nixproc-supervisord-deploy-stateless 31 | ''; 32 | } 33 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/test-module/default.nix: -------------------------------------------------------------------------------- 1 | {profileSettings, exprFile, extraParams, tools, pkgs, system}: 2 | 3 | let 4 | executeDeploy = import ../../../test-driver/util/execute-deploy.nix { 5 | inherit (pkgs) lib; 6 | }; 7 | 8 | processesEnvProcessManager = import ../../sysvinit/build-sysvinit-env.nix ({ 9 | inherit pkgs system; 10 | exprFile = ./processes-supervisord.nix; 11 | } // profileSettings.params); 12 | 13 | processesEnvSystem = import ../build-supervisord-env.nix ({ 14 | inherit pkgs system exprFile extraParams; 15 | } // profileSettings.params); 16 | in 17 | { 18 | inherit (profileSettings) params; 19 | 20 | nixosModules = []; 21 | 22 | systemPackages = [ 23 | tools.sysvinit 24 | tools.supervisord 25 | pkgs.python3Packages.supervisor 26 | ]; 27 | 28 | additionalPaths = [ processesEnvProcessManager processesEnvSystem ]; 29 | 30 | deployProcessManager = '' 31 | machine.succeed( 32 | "${executeDeploy { inherit profileSettings; processManager = "sysvinit"; processesEnv = processesEnvProcessManager; }}" 33 | ) 34 | machine.wait_for_open_port(9001) 35 | ''; 36 | 37 | deploySystem = '' 38 | machine.succeed( 39 | "${executeDeploy { inherit profileSettings; processManager = "supervisord"; envVars = [ "SUPERVISORD_CONF_DIR=${profileSettings.params.stateDir}/lib/supervisord" ]; processesEnv = processesEnvSystem; }}" 40 | ) 41 | ''; 42 | } 43 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/network-logical.nix: -------------------------------------------------------------------------------- 1 | let 2 | nixproc-generate-config = (import ../../tools {}).generate-config; 3 | in 4 | { 5 | test1 = {pkgs, ...}: 6 | 7 | { 8 | dysnomia = { 9 | extraContainerProperties = { 10 | managed-process = { 11 | processManager = "systemd"; 12 | NIX_PATH = "/root/.nix-defexpr/channels:nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels"; 13 | }; 14 | }; 15 | }; 16 | 17 | services.disnix.enable = true; 18 | services.openssh.enable = true; 19 | networking.firewall.enable = false; 20 | environment.systemPackages = [ pkgs.python3Packages.supervisor nixproc-generate-config ]; 21 | }; 22 | 23 | test2 = {pkgs, ...}: 24 | 25 | { 26 | dysnomia = { 27 | extraContainerProperties = { 28 | managed-process = { 29 | processManager = "sysvinit"; 30 | NIX_PATH = "/root/.nix-defexpr/channels:nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels"; 31 | }; 32 | }; 33 | 34 | }; 35 | 36 | services.disnix.enable = true; 37 | services.openssh.enable = true; 38 | networking.firewall.enable = false; 39 | environment.systemPackages = [ pkgs.python3Packages.supervisor nixproc-generate-config ]; 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/steps/interactive.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | result // pkgs.lib.optionalAttrs (!(input ? interactive) || input.interactive) { 4 | contents = result.contents or [] ++ (with pkgs; [ 5 | # An interactive bash shell 6 | bashInteractive 7 | # Basic shell utilities for file management, text manipulation, user management, process management 8 | coreutils diffutils findutils gnugrep gnused glibc.bin less utillinux procps shadow 9 | ]); 10 | 11 | runAsRoot = result.runAsRoot or "" + '' 12 | mkdir -p /root 13 | cat > /root/.bashrc << "EOF" 14 | alias ls='ls --color=auto' 15 | 16 | if [ -n "$PS1" ] 17 | then 18 | if [ "$TERM" != "dumb" -o -n "$INSIDE_EMACS" ] 19 | then 20 | PROMPT_COLOR="1;31m" 21 | let $UID && PROMPT_COLOR="1;32m" 22 | if [ -n "$INSIDE_EMACS" -o "$TERM" == "eterm" -o "$TERM" == "eterm-color" ] 23 | then 24 | # Emacs term mode doesn't support xterm title escape sequence (\e]0;) 25 | PS1="\n\[\033[$PROMPT_COLOR\][\u@\h:\w]\\$\[\033[0m\] " 26 | else 27 | PS1="\n\[\033[$PROMPT_COLOR\][\[\e]0;\u@\h: \w\a\]\u@\h:\w]\\$\[\033[0m\] " 28 | fi 29 | if test "$TERM" = "xterm" 30 | then 31 | PS1="\[\033]2;\h:\u:\w\007\]$PS1" 32 | fi 33 | fi 34 | fi 35 | EOF 36 | ''; 37 | } 38 | -------------------------------------------------------------------------------- /nixproc/backends/s6-rc/build-s6-rc-env.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , spoolDir ? "${stateDir}/spool" 8 | , lockDir ? "${stateDir}/lock" 9 | , libDir ? "${stateDir}/lib" 10 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 11 | , forceDisableUserChange ? false 12 | , callingUser ? null 13 | , callingGroup ? null 14 | , extraParams ? {} 15 | , exprFile ? null 16 | , defaultBundleName ? "default" 17 | }@args: 18 | 19 | let 20 | processesFun = import exprFile; 21 | 22 | processesFormalArgs = builtins.functionArgs processesFun; 23 | 24 | processesArgs = builtins.intersectAttrs processesFormalArgs (args // { 25 | processManager = "s6-rc"; 26 | } // extraParams); 27 | 28 | processes = if exprFile == null then {} else processesFun processesArgs; 29 | 30 | createServiceBundle = import ./create-service-bundle.nix { 31 | inherit (pkgs) stdenv lib; 32 | }; 33 | 34 | processesList = map (processName: 35 | let 36 | process = builtins.getAttr processName processes; 37 | in 38 | process.pkg 39 | ) (builtins.attrNames processes); 40 | 41 | defaultBundle = createServiceBundle { 42 | name = defaultBundleName; 43 | contents = processesList; 44 | }; 45 | in 46 | pkgs.buildEnv { 47 | name = "s6-rc"; 48 | paths = [ defaultBundle ] ++ processesList; 49 | } 50 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/processes.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , libDir ? "${stateDir}/lib" 8 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 9 | , forceDisableUserChange ? false 10 | , processManager 11 | , webappMode ? null 12 | }: 13 | 14 | let 15 | ids = if builtins.pathExists ./ids.nix then (import ./ids.nix).ids else {}; 16 | 17 | sharedConstructors = import ../services-agnostic/constructors/constructors.nix { 18 | inherit pkgs stateDir runtimeDir logDir cacheDir libDir tmpDir forceDisableUserChange processManager ids; 19 | }; 20 | 21 | constructors = import ./constructors/constructors.nix { 22 | inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager webappMode ids; 23 | }; 24 | in 25 | rec { 26 | webapp = rec { 27 | port = ids.webappPorts.webapp or 0; 28 | dnsName = "webapp.local"; 29 | 30 | pkg = constructors.webapp { 31 | inherit port; 32 | }; 33 | 34 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 35 | }; 36 | 37 | nginx = rec { 38 | port = ids.nginxPorts.nginx or 0; 39 | 40 | pkg = sharedConstructors.nginxReverseProxyHostBased { 41 | webapps = [ webapp ]; 42 | inherit port; 43 | } {}; 44 | 45 | requiresUniqueIdsFor = [ "nginxPorts" "uids" "gids" ]; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /nixproc/backends/s6-rc/image-steps/s6-rc.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | skelDir = pkgs.stdenv.mkDerivation { 5 | name = "s6-skel-dir"; 6 | buildCommand = '' 7 | mkdir -p $out 8 | cd $out 9 | 10 | cat > rc.init < rc.shutdown < rc.shutdown.final <&2 13 | sleep 1 14 | done 15 | ''; 16 | executable = true; 17 | }; 18 | 19 | generateTestConf = instanceName: 20 | pkgs.writeTextFile { 21 | name = "test-${instanceName}.conf"; 22 | text = '' 23 | [program:test-${instanceName}] 24 | command=${generateTestExecutable instanceName} 25 | ''; 26 | }; 27 | in 28 | testService { 29 | name = "supervisord"; 30 | exprFile = ./processes.nix; 31 | systemPackages = [ pkgs.python3Packages.supervisor ]; 32 | 33 | readiness = {instanceName, instance, ...}: 34 | '' 35 | machine.wait_for_open_port(${toString instance.port}) 36 | ''; 37 | 38 | tests = {instanceName, instance, stateDir, ...}: 39 | '' 40 | machine.succeed( 41 | "cp ${generateTestConf instanceName} ${stateDir}/lib/${instanceName}/conf.d" 42 | ) 43 | machine.succeed("supervisorctl --serverurl http://localhost:${toString instance.port} reread") 44 | machine.succeed("supervisorctl --serverurl http://localhost:${toString instance.port} update") 45 | machine.succeed("sleep 1") 46 | machine.succeed( 47 | "pgrep -f '${generateTestExecutable instanceName}'" 48 | ) 49 | ''; 50 | 51 | inherit processManagers profiles; 52 | } 53 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/services.nix: -------------------------------------------------------------------------------- 1 | { pkgs, distribution, invDistribution, system 2 | , stateDir ? "/var" 3 | , runtimeDir ? "${stateDir}/run" 4 | , logDir ? "${stateDir}/log" 5 | , cacheDir ? "${stateDir}/cache" 6 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 7 | , forceDisableUserChange ? false 8 | , processManager ? "sysvinit" 9 | }: 10 | 11 | let 12 | ids = if builtins.pathExists ./ids.nix then (import ./ids.nix).ids else {}; 13 | 14 | sharedConstructors = import ../services-agnostic/constructors.nix { 15 | inherit pkgs stateDir runtimeDir logDir cacheDir tmpDir forceDisableUserChange processManager ids; 16 | }; 17 | 18 | constructors = import ./constructors.nix { 19 | inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager ids; 20 | webappMode = null; 21 | }; 22 | 23 | processType = import ../../nixproc/derive-dysnomia-process-type.nix { 24 | inherit processManager; 25 | }; 26 | in 27 | rec { 28 | webapp = rec { 29 | name = "webapp"; 30 | port = ids.webappPorts.webapp or 0; 31 | dnsName = "webapp.local"; 32 | pkg = constructors.webapp { 33 | inherit port; 34 | }; 35 | type = processType; 36 | 37 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 38 | }; 39 | 40 | nginx = rec { 41 | name = "nginx"; 42 | port = ids.nginxPorts.nginx or 0; 43 | pkg = sharedConstructors.nginxReverseProxyHostBased { 44 | inherit port; 45 | }; 46 | dependsOn = { 47 | inherit webapp; 48 | }; 49 | type = processType; 50 | 51 | requiresUniqueIdsFor = [ "nginxPorts" "uids" "gids" ]; 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /nixproc/backends/systemd/test-module/default.nix: -------------------------------------------------------------------------------- 1 | {profileSettings, exprFile, extraParams, tools, pkgs, system}: 2 | 3 | let 4 | executeDeploy = import ../../../test-driver/util/execute-deploy.nix { 5 | inherit (pkgs) lib; 6 | }; 7 | 8 | processesEnvSystem = import ../build-systemd-env.nix ({ 9 | inherit pkgs system exprFile extraParams; 10 | } // profileSettings.params); 11 | 12 | deployEnv = if profileSettings.params.forceDisableUserChange 13 | then "XDG_RUNTIME_DIR=/run/user/1000" 14 | else "SYSTEMD_TARGET_DIR=/etc/systemd-mutable/system"; 15 | in 16 | { 17 | inherit (profileSettings) params; 18 | 19 | nixosModules = pkgs.lib.optional profileSettings.params.forceDisableUserChange ./xserver-autologin-module.nix 20 | ++ pkgs.lib.optional (!profileSettings.params.forceDisableUserChange) ./systemd-path.nix; 21 | 22 | systemPackages = [ 23 | tools.systemd 24 | ]; 25 | 26 | additionalPaths = [ processesEnvSystem ]; 27 | 28 | deployProcessManager = '' 29 | machine.succeed("mkdir -p /etc/systemd-mutable/system") 30 | '' + pkgs.lib.optionalString profileSettings.params.forceDisableUserChange '' 31 | machine.wait_for_unit("display-manager.service") 32 | machine.wait_until_succeeds("pgrep -f 'systemd --user'") 33 | machine.wait_for_file("/run/user/1000/systemd") 34 | ''; 35 | 36 | deploySystem = '' 37 | machine.succeed( 38 | "${executeDeploy { inherit profileSettings; processManager = "systemd"; envVars = [ deployEnv ]; extraDeployArgs = pkgs.lib.optionalString profileSettings.params.forceDisableUserChange [ "--user" ]; processesEnv = processesEnvSystem; }}" 39 | ) 40 | ''; 41 | } 42 | -------------------------------------------------------------------------------- /tools/s6-rc/nixproc-s6-svscan.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | shopt -s nullglob 4 | 5 | showUsage() 6 | { 7 | me="$(basename "$0")" 8 | 9 | cat < { inherit system; }; 7 | 8 | processesFun = import (builtins.getEnv "PROCESSES_EXPR"); 9 | 10 | processesFormalArgs = builtins.functionArgs processesFun; 11 | 12 | args = pkgs.lib.optionalAttrs (builtins.getEnv "NIXPROC_STATE_DIR" != "") { 13 | stateDir = builtins.getEnv "NIXPROC_STATE_DIR"; 14 | } // pkgs.lib.optionalAttrs (builtins.getEnv "NIXPROC_RUNTIME_DIR" != "") { 15 | runtimeDir = builtins.getEnv "NIXPROC_RUNTIME_DIR"; 16 | } // pkgs.lib.optionalAttrs (builtins.getEnv "NIXPROC_LOG_DIR" != "") { 17 | logDir = builtins.getEnv "NIXPROC_LOG_DIR"; 18 | } // pkgs.lib.optionalAttrs (builtins.getEnv "NIXPROC_TMP_DIR" != "") { 19 | tmpDir = builtins.getEnv "NIXPROC_TMP_DIR"; 20 | } // pkgs.lib.optionalAttrs (builtins.getEnv "NIXPROC_CACHE_DIR" != "") { 21 | tmpDir = builtins.getEnv "NIXPROC_CACHE_DIR"; 22 | } // pkgs.lib.optionalAttrs (builtins.getEnv "NIXPROC_SPOOL_DIR" != "") { 23 | spoolDir = builtins.getEnv "NIXPROC_SPOOL_DIR"; 24 | } // pkgs.lib.optionalAttrs (builtins.getEnv "NIXPROC_LOCK_DIR" != "") { 25 | lockDir = builtins.getEnv "NIXPROC_LOCK_DIR"; 26 | } // pkgs.lib.optionalAttrs (builtins.getEnv "NIXPROC_LIB_DIR" != "") { 27 | libDir = builtins.getEnv "NIXPROC_LIB_DIR"; 28 | } // pkgs.lib.optionalAttrs (builtins.getEnv "NIXPROC_FORCE_DISABLE_USER_CHANGE" != "") { 29 | forceDisableUserChange = true; 30 | }; 31 | 32 | processesArgs = builtins.intersectAttrs processesFormalArgs (args // { 33 | processManager = "disnix"; 34 | inherit pkgs system; 35 | }); 36 | in 37 | {distribution, invDistribution, pkgs, system}: 38 | 39 | let 40 | processes = processesFun processesArgs; 41 | in 42 | pkgs.lib.mapAttrs (name: config: 43 | config // { 44 | inherit name; 45 | type = "process"; 46 | } 47 | ) processes 48 | -------------------------------------------------------------------------------- /tests/webapps-agnostic-supervisord-stateless.nix: -------------------------------------------------------------------------------- 1 | {nixpkgs ? }: 2 | 3 | with import "${nixpkgs}/nixos/lib/testing-python.nix" { system = builtins.currentSystem; }; 4 | 5 | let 6 | processesEnvAuto = import ../nixproc/backends/supervisord/build-supervisord-env.nix { 7 | exprFile = ../examples/webapps-agnostic/processes.nix; 8 | }; 9 | 10 | tools = import ../tools {}; 11 | 12 | nix-processmgmt = ./..; 13 | 14 | env = "NIX_PATH=nixpkgs=${nixpkgs}"; 15 | in 16 | makeTest { 17 | name = "webapps-agnostic-supervisord-stateless"; 18 | 19 | nodes.machine = 20 | {pkgs, ...}: 21 | 22 | { 23 | virtualisation.additionalPaths = [ pkgs.stdenv ] ++ pkgs.coreutils.all ++ [ processesEnvAuto ]; 24 | virtualisation.writableStore = true; 25 | virtualisation.memorySize = 1024; 26 | 27 | # We can't download any substitutes in a test environment. To make tests 28 | # faster, we disable substitutes so that Nix does not waste any time by 29 | # attempting to download them. 30 | nix.extraOptions = '' 31 | substitute = false 32 | ''; 33 | 34 | environment.systemPackages = [ 35 | pkgs.stdenv 36 | pkgs.daemon 37 | pkgs.python3Packages.supervisor 38 | pkgs.dysnomia 39 | tools.common 40 | tools.systemd 41 | tools.supervisord 42 | ]; 43 | }; 44 | 45 | testScript = '' 46 | def check_nginx_redirection(): 47 | machine.succeed( 48 | "curl --fail -H 'Host: webapp.local' http://localhost:8080 | grep 'listening on port: 5000'" 49 | ) 50 | 51 | 52 | start_all() 53 | 54 | # Deploy the advanced example with multiple instances and see if it works 55 | 56 | machine.succeed( 57 | "${env} daemon --inherit --unsafe -- nixproc-supervisord-deploy-stateless ${nix-processmgmt}/examples/webapps-agnostic/processes.nix" 58 | ) 59 | 60 | machine.wait_for_open_port(9001) 61 | machine.succeed("sleep 30") 62 | check_nginx_redirection() 63 | ''; 64 | } 65 | -------------------------------------------------------------------------------- /tools/bsdrc/nixproc-bsdrc-runactivity.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | shopt -s nullglob 4 | 5 | # Shows the usage of this command to the user 6 | 7 | showUsage() 8 | { 9 | me="$(basename "$0")" 10 | 11 | cat <&2 69 | exit 1 70 | fi 71 | 72 | source @commonchecks@ 73 | 74 | checkNixStateDir 75 | checkProfile 76 | composeOldProfilePath 77 | 78 | # Execute the activities 79 | 80 | if [ "$2" = "" ] 81 | then 82 | rcpath="$oldProfilePath/etc/rc.d" 83 | else 84 | rcpath="$2/etc/rc.d" 85 | fi 86 | 87 | if [ "$reverse" = "1" ] 88 | then 89 | for i in $(rcorder $rcpath/* | tail -r) 90 | do 91 | $i $activity 92 | done 93 | else 94 | for i in $(rcorder $rcpath/*) 95 | do 96 | $i $activity 97 | done 98 | fi 99 | -------------------------------------------------------------------------------- /nixproc/create-managed-process/agnostic/create-managed-process-from-config.nix: -------------------------------------------------------------------------------- 1 | { configFile 2 | , processManager 3 | , createManagedProcessExpr 4 | , system ? builtins.currentSystem 5 | , pkgs ? import { inherit system; } 6 | , stateDir ? "/var" 7 | , runtimeDir ? "${stateDir}/run" 8 | , logDir ? "${stateDir}/log" 9 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 10 | , forceDisableUserChange ? false 11 | }: 12 | 13 | let 14 | createManagedProcessFromConfig = configFile: 15 | let 16 | createManagedProcess = import createManagedProcessExpr { 17 | inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager; 18 | }; 19 | 20 | configFileString = builtins.readFile configFile; 21 | 22 | properties = builtins.fromJSON (builtins.unsafeDiscardStringContext configFileString); 23 | 24 | # This attribute is a hack. It readds the dependencies of the JSON file as context to a frequently used string property so that the generated configuration artifact retains the runtime dependencies of the original JSON file. 25 | # This hack is needed because builtins.fromJSON can't work with strings that have context. 26 | 27 | propertiesWithContext = properties // pkgs.lib.optionalAttrs (properties ? process) { 28 | process = pkgs.lib.addContextFrom configFileString properties.process; 29 | } // pkgs.lib.optionalAttrs (properties ? foregroundProcess) { 30 | foregroundProcess = pkgs.lib.addContextFrom configFileString properties.foregroundProcess; 31 | } // pkgs.lib.optionalAttrs (properties ? daemon) { 32 | daemon = pkgs.lib.addContextFrom configFileString properties.daemon; 33 | }; 34 | 35 | normalizedProperties = propertiesWithContext // pkgs.lib.optionalAttrs (properties ? dependencies) { 36 | dependencies = map (dependency: createManagedProcessFromConfig "${dependency}/${builtins.substring 33 (builtins.stringLength dependency) (baseNameOf dependency)}.json") properties.dependencies; 37 | }; 38 | in 39 | createManagedProcess normalizedProperties; 40 | in 41 | createManagedProcessFromConfig configFile 42 | -------------------------------------------------------------------------------- /nixproc/backends/disnix/build-disnix-env.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${cacheDir}/cache" 7 | , spoolDir ? "${stateDir}/spool" 8 | , lockDir ? "${stateDir}/lock" 9 | , libDir ? "${stateDir}/lib" 10 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 11 | , forceDisableUserChange ? false 12 | , callingUser ? null 13 | , callingGroup ? null 14 | , clientInterface ? (if builtins.getEnv "DISNIX_CLIENT_INTERFACE" == "" then "disnix-run-activity" else builtins.getEnv "DISNIX_CLIENT_INTERFACE") 15 | , disnixDataDir ? (if builtins.getEnv "DISNIX_DATA_DIR" == "" then throw "Set DISNIX_DATA_DIR to the data directory of Disnix" else builtins.getEnv "DISNIX_DATA_DIR") 16 | , extraParams ? {} 17 | , exprFile ? null 18 | }@args: 19 | 20 | let 21 | processesFun = import exprFile; 22 | 23 | processesFormalArgs = builtins.functionArgs processesFun; 24 | 25 | processesArgs = builtins.intersectAttrs processesFormalArgs (args // { 26 | processManager = "disnix"; 27 | } // extraParams); 28 | 29 | processes = if exprFile == null then {} else processesFun processesArgs; 30 | 31 | localhostTarget = { 32 | properties.hostname = "localhost"; 33 | inherit system; 34 | }; 35 | 36 | services = pkgs.lib.mapAttrs (processName: process: { 37 | name = processName; 38 | inherit (process) pkg; 39 | 40 | activatesAfter = builtins.listToAttrs (map (dependency: { 41 | inherit (dependency) name; 42 | value = builtins.getAttr dependency.name services; 43 | }) process.pkg.dependencies); 44 | 45 | type = "process"; 46 | targets = [ localhostTarget ]; 47 | }) processes; 48 | 49 | architectureFun = {system, pkgs}: 50 | { 51 | infrastructure.localhost = localhostTarget; 52 | inherit services; 53 | }; 54 | 55 | manifest = import "${disnixDataDir}/manifest.nix"; 56 | in 57 | manifest.generateManifestFromArchitectureFun { 58 | inherit pkgs clientInterface architectureFun; 59 | targetProperty = "hostname"; 60 | deployState = false; 61 | } 62 | -------------------------------------------------------------------------------- /nixproc/backends/docker/test-module/default.nix: -------------------------------------------------------------------------------- 1 | {profileSettings, exprFile, extraParams, tools, pkgs, system}: 2 | 3 | let 4 | executeDeploy = import ../../../test-driver/util/execute-deploy.nix { 5 | inherit (pkgs) lib; 6 | }; 7 | 8 | # We cannot deploy Docker as unprivileged user. Use a privileged installation instead 9 | profileSettingsProcessManager = import ../../../test-driver/profiles/privileged.nix; 10 | 11 | # For privileged deployments, use a different directory than /var, because it does not have the right SELinux context to work with containers 12 | profileSettingsSystem = if profileSettings.params.stateDir == "/var" then profileSettings // { 13 | params = profileSettings.params // rec { 14 | stateDir = "/dockervar"; 15 | runtimeDir = "${stateDir}/run"; 16 | }; 17 | } else profileSettings; 18 | 19 | processesEnvProcessManager = import ../../sysvinit/build-sysvinit-env.nix ({ 20 | inherit pkgs system; 21 | exprFile = ./processes-docker.nix; 22 | } // profileSettingsProcessManager.params); 23 | 24 | processesEnvSystem = import ../build-docker-env.nix ({ 25 | inherit pkgs system exprFile extraParams; 26 | } // profileSettingsSystem.params); 27 | in 28 | { 29 | inherit (profileSettingsSystem) params; 30 | 31 | nixosModules = []; 32 | 33 | systemPackages = [ 34 | tools.sysvinit 35 | tools.docker 36 | pkgs.docker 37 | ]; 38 | 39 | additionalPaths = [ processesEnvProcessManager processesEnvSystem ]; 40 | 41 | deployProcessManager = '' 42 | machine.succeed( 43 | "${executeDeploy { profileSettings = profileSettingsProcessManager; processManager = "sysvinit"; processesEnv = processesEnvProcessManager; }}" 44 | ) 45 | machine.wait_for_file("${profileSettingsProcessManager.params.stateDir}/run/docker.sock") 46 | '' + pkgs.lib.optionalString profileSettings.params.forceDisableUserChange '' 47 | machine.succeed("usermod -a -G docker unprivileged") 48 | ''; 49 | 50 | deploySystem = '' 51 | machine.succeed( 52 | "${executeDeploy { profileSettings = profileSettingsSystem; processManager = "docker"; processesEnv = processesEnvSystem; }}" 53 | ) 54 | ''; 55 | } 56 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/steps/nix-support.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | mkDbExtraCommand = contents: let 5 | contentsList = if builtins.isList contents then pkgs.lib.unique contents else [ contents ]; in 6 | '' 7 | echo "Generating the nix database..." 8 | echo "Warning: only the database of the deepest Nix layer is loaded." 9 | echo " If you want to use nix commands in the container, it would" 10 | echo " be better to only have one layer that contains a nix store." 11 | 12 | export NIX_REMOTE=local?root=$PWD 13 | # A user is required by nix 14 | # https://github.com/NixOS/nix/blob/9348f9291e5d9e4ba3c4347ea1b235640f54fd79/src/libutil/util.cc#L478 15 | export USER=nobody 16 | ${pkgs.nix}/bin/nix-store --load-db < ${pkgs.closureInfo {rootPaths = contentsList;}}/registration 17 | 18 | mkdir -p nix/var/nix/gcroots/docker/ 19 | for i in ${pkgs.lib.concatStringsSep " " contentsList} 20 | do 21 | ln -s $i nix/var/nix/gcroots/docker/$(basename $i) 22 | done; 23 | ''; 24 | in 25 | result // rec { 26 | contents = result.contents or [] ++ (with pkgs; [ 27 | # Nix 28 | nix.out 29 | # Needed for SSL authentication 30 | cacert 31 | # Needed for fetchgit 32 | git 33 | # Needed for downloading compressed tarballs 34 | gnutar gzip bzip2 xz 35 | ]); 36 | 37 | extraCommands = result.extraCommands or "" 38 | + mkDbExtraCommand contents; 39 | 40 | runAsRoot = result.runAsRoot or "" + '' 41 | # Initialize groups for Nix 42 | groupadd -g 30000 nixbld 43 | for i in $(seq 1 30) 44 | do 45 | groupadd -g $((30000 + i)) nixbld$i 46 | useradd -d /var/empty -c "Nix build user $i" -u $((30000 + i)) -g nixbld$i -G nixbld nixbld$i 47 | done 48 | ''; 49 | 50 | config = result.config or {} // { 51 | Env = result.config.Env or [] ++ [ 52 | "NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt" 53 | "GIT_SSL_CAINFO=/etc/ssl/certs/ca-bundle.crt" 54 | "NIX_PATH=/nix/var/nix/profiles/per-user/root/channels" 55 | "USER=root" 56 | "PATH=/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default/sbin:/bin:/sbin" 57 | ]; 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /release.nix: -------------------------------------------------------------------------------- 1 | { nixpkgs ? 2 | , system ? builtins.currentSystem 3 | , nix-processmgmt ? { outPath = ./.; rev = 1234; } 4 | }: 5 | 6 | let 7 | pkgs = import nixpkgs {}; 8 | in 9 | rec { 10 | tools = import ./tools { 11 | pkgs = import nixpkgs { inherit system; }; 12 | inherit system; 13 | }; 14 | 15 | tests = { 16 | builds = import ./tests/builds.nix { 17 | inherit pkgs nix-processmgmt; 18 | }; 19 | 20 | services = import ./tests/services { 21 | inherit nixpkgs system; 22 | }; 23 | 24 | multi-process-images = import ./tests/multi-process-images.nix { 25 | inherit nixpkgs; 26 | }; 27 | 28 | webapps-agnostic = { 29 | config = import ./tests/webapps-agnostic-config.nix { 30 | inherit nixpkgs; 31 | }; 32 | 33 | disnix = import ./tests/webapps-agnostic-disnix.nix { 34 | inherit nixpkgs; 35 | }; 36 | 37 | docker = import ./tests/webapps-agnostic-docker.nix { 38 | inherit nixpkgs; 39 | }; 40 | 41 | s6-rc = import ./tests/webapps-agnostic-s6-rc.nix { 42 | inherit nixpkgs; 43 | }; 44 | 45 | supervisord = import ./tests/webapps-agnostic-supervisord.nix { 46 | inherit nixpkgs; 47 | }; 48 | 49 | supervisord-stateless = import ./tests/webapps-agnostic-supervisord-stateless.nix { 50 | inherit nixpkgs; 51 | }; 52 | 53 | systemd = import ./tests/webapps-agnostic-systemd.nix { 54 | inherit nixpkgs; 55 | }; 56 | 57 | systemd-user = import ./tests/webapps-agnostic-systemd-user.nix { 58 | inherit nixpkgs; 59 | }; 60 | 61 | sysvinit = import ./tests/webapps-agnostic-sysvinit.nix { 62 | inherit nixpkgs; 63 | }; 64 | }; 65 | 66 | webapps-sysvinit = import ./tests/webapps-sysvinit.nix { 67 | inherit nixpkgs; 68 | }; 69 | }; 70 | 71 | release = pkgs.releaseTools.aggregate { 72 | name = "nix-processmgmt"; 73 | constituents = builtins.attrValues tools 74 | ++ builtins.attrValues tests.builds 75 | ++ builtins.attrValues tests.webapps-agnostic 76 | ++ [ 77 | tests.webapps-sysvinit 78 | tests.multi-process-images 79 | ]; 80 | meta.description = "Release-critical builds"; 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /tests/webapps-agnostic-config.nix: -------------------------------------------------------------------------------- 1 | {nixpkgs ? }: 2 | 3 | with import "${nixpkgs}/nixos/lib/testing-python.nix" { system = builtins.currentSystem; }; 4 | 5 | let 6 | webappUnprivilegedAutoModeConfig = (import ../examples/webapps-agnostic/processes.nix { 7 | forceDisableUserChange = true; 8 | processManager = null; 9 | webappMode = null; 10 | }).webapp.pkg; 11 | 12 | webappUnprivilegedAutoModeSysvinit = (import ../examples/webapps-agnostic/processes.nix { 13 | forceDisableUserChange = true; 14 | processManager = "sysvinit"; 15 | webappMode = null; 16 | }).webapp.pkg; 17 | 18 | tools = import ../tools {}; 19 | 20 | nix-processmgmt = ./..; 21 | 22 | env = "NIX_PATH=nixpkgs=${nixpkgs}"; 23 | in 24 | makeTest { 25 | name = "webapps-agnostic-config"; 26 | 27 | nodes.machine = 28 | {pkgs, ...}: 29 | 30 | { 31 | virtualisation.additionalPaths = [ pkgs.stdenv pkgs.stdenvNoCC ] ++ pkgs.coreutils.all ++ [ 32 | webappUnprivilegedAutoModeConfig 33 | webappUnprivilegedAutoModeSysvinit 34 | ]; 35 | 36 | virtualisation.writableStore = true; 37 | 38 | # We can't download any substitutes in a test environment. To make tests 39 | # faster, we disable substitutes so that Nix does not waste any time by 40 | # attempting to download them. 41 | nix.extraOptions = '' 42 | substitute = false 43 | ''; 44 | 45 | environment.systemPackages = [ 46 | pkgs.stdenv 47 | pkgs.dysnomia 48 | tools.common 49 | tools.generate-config 50 | ]; 51 | }; 52 | 53 | testScript = '' 54 | start_all() 55 | 56 | # Make sure the unprivileged user can deploy 57 | machine.succeed("mkdir -p var/run var/tmp") 58 | 59 | result = machine.succeed( 60 | "cat ${webappUnprivilegedAutoModeConfig}/webapp.json >&2" 61 | ) 62 | 63 | result = machine.succeed( 64 | "${env} nixproc-generate-config --process-manager sysvinit --force-disable-user-change ${webappUnprivilegedAutoModeConfig}/webapp.json" 65 | ) 66 | 67 | machine.succeed("{}/etc/rc.d/init.d/webapp start".format(result[:-1])) 68 | machine.succeed("pgrep -f '/bin/webapp -D$'") 69 | machine.succeed("curl --fail http://localhost:5000 | grep 'Simple test webapp'") 70 | ''; 71 | } 72 | -------------------------------------------------------------------------------- /webapp/daemonize.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Sander van der Burg 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __DAEMONIZE_H 24 | #define __DAEMONIZE_H 25 | #include 26 | 27 | typedef enum 28 | { 29 | STATUS_INIT_SUCCESS = 0x0, 30 | STATUS_CANNOT_ATTACH_STD_FDS_TO_NULL = 0x1, 31 | STATUS_CANNOT_CHDIR = 0x2, 32 | STATUS_CANNOT_CREATE_PID_FILE = 0x3, 33 | STATUS_CANNOT_INIT_DAEMON = 0x4, 34 | STATUS_CANNOT_UNLINK_PID_FILE = 0x5, 35 | STATUS_CANNOT_CLOSE_NON_STD_FDS = 0x6, 36 | STATUS_CANNOT_RESET_SIGNAL_HANDLERS = 0x7, 37 | STATUS_CANNOT_CLEAR_SIGNAL_MASK = 0x8, 38 | STATUS_CANNOT_CREATE_PIPE = 0x9, 39 | STATUS_CANNOT_FORK_HELPER_PROCESS = 0xa, 40 | STATUS_CANNOT_READ_FROM_PIPE = 0xb, 41 | STATUS_CANNOT_SET_SID = 0xc, 42 | STATUS_CANNOT_FORK_DAEMON_PROCESS = 0xd, 43 | STATUS_UNKNOWN_DAEMON_ERROR = 0xe 44 | } 45 | DaemonStatus; 46 | 47 | DaemonStatus daemonize(const char *pid_file, void *data, int (*initialize_daemon) (void *data), int (*run_main_loop) (void *data)); 48 | 49 | void print_daemon_status(DaemonStatus status, FILE *file); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/generate-supervisord-program.nix: -------------------------------------------------------------------------------- 1 | { createSupervisordProgram, stdenv, lib, writeTextFile, runtimeDir, forceDisableUserChange }: 2 | 3 | { name 4 | , description 5 | , initialize 6 | , daemon 7 | , daemonArgs 8 | , instanceName 9 | , pidFile 10 | , foregroundProcess 11 | , foregroundProcessArgs 12 | , path 13 | , environment 14 | , directory 15 | , umask 16 | , nice 17 | , user 18 | , dependencies 19 | , credentials 20 | , overrides 21 | , postInstall 22 | }: 23 | 24 | let 25 | generateForegroundProxy = import ../util/generate-foreground-proxy.nix { 26 | inherit stdenv lib writeTextFile; 27 | }; 28 | 29 | chainLoadUser = if initialize == "" || forceDisableUserChange then null 30 | else user; 31 | 32 | command = if foregroundProcess != null then 33 | (if initialize == "" 34 | then foregroundProcess 35 | else generateForegroundProxy ({ 36 | user = chainLoadUser; 37 | wrapDaemon = false; 38 | executable = foregroundProcess; 39 | inherit name initialize runtimeDir stdenv; 40 | } // lib.optionalAttrs (instanceName != null) { 41 | inherit instanceName; 42 | } // lib.optionalAttrs (pidFile != null) { 43 | inherit pidFile; 44 | })) + " ${lib.escapeShellArgs foregroundProcessArgs}" 45 | else (generateForegroundProxy ({ 46 | wrapDaemon = true; 47 | user = chainLoadUser; 48 | executable = daemon; 49 | inherit name initialize runtimeDir stdenv; 50 | } // lib.optionalAttrs (instanceName != null) { 51 | inherit instanceName; 52 | } // lib.optionalAttrs (pidFile != null) { 53 | inherit pidFile; 54 | })) + " ${lib.escapeShellArgs daemonArgs}"; 55 | 56 | generatedTargetSpecificArgs = { 57 | inherit name command path environment dependencies credentials postInstall; 58 | } // lib.optionalAttrs (umask != null) { 59 | inherit umask; 60 | } // lib.optionalAttrs (nice != null) { 61 | inherit nice; 62 | } // lib.optionalAttrs (pidFile != null) { 63 | inherit pidFile; 64 | } // lib.optionalAttrs (user != null && chainLoadUser == null) { 65 | inherit user; 66 | }; 67 | 68 | targetSpecificArgs = 69 | if builtins.isFunction overrides then overrides generatedTargetSpecificArgs 70 | else lib.recursiveUpdate generatedTargetSpecificArgs overrides; 71 | in 72 | createSupervisordProgram targetSpecificArgs 73 | -------------------------------------------------------------------------------- /webapp/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Sander van der Burg 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include "service.h" 27 | 28 | #define TRUE 1 29 | #define FALSE 0 30 | 31 | int main(int argc, char *argv[]) 32 | { 33 | unsigned int i; 34 | int run_as_daemon = FALSE; 35 | int port; 36 | char *port_value = getenv("PORT"); 37 | 38 | if(port_value == NULL) 39 | { 40 | fprintf(stderr, "We need a PORT environment variable that specifies to which port the HTTP service should bind to!\n"); 41 | return 1; 42 | } 43 | 44 | port = atoi(port_value); 45 | 46 | for(i = 1; i < argc; i++) 47 | { 48 | if(strcmp(argv[i], "-D") == 0) 49 | run_as_daemon = TRUE; 50 | } 51 | 52 | if(run_as_daemon) 53 | { 54 | char *pid_file = getenv("PID_FILE"); 55 | 56 | if(pid_file == NULL) 57 | { 58 | fprintf(stderr, "We need the PID_FILE environment variable that specifies the path to a file storing the PID of the daemon process!\n"); 59 | return 1; 60 | } 61 | 62 | return run_daemon(port, pid_file); 63 | } 64 | else 65 | return run_foreground_process(port); 66 | } 67 | -------------------------------------------------------------------------------- /nixproc/create-credentials/default.nix: -------------------------------------------------------------------------------- 1 | {stdenv, lib, ids ? {}, forceDisableUserChange}: 2 | {groups ? {}, users ? {}}: 3 | 4 | stdenv.mkDerivation { 5 | name = "credentials"; 6 | buildCommand = '' 7 | ${lib.optionalString (!forceDisableUserChange && groups != {}) '' 8 | mkdir -p $out/dysnomia-support/groups 9 | 10 | ${lib.concatMapStrings (groupname: 11 | let 12 | group = builtins.getAttr groupname groups; 13 | in 14 | '' 15 | ${lib.optionalString (!(group ? gid) && ids ? gids && builtins.hasAttr groupname ids.gids) ''echo "gid=${toString ids.gids."${groupname}"}" > $out/dysnomia-support/groups/${groupname}''} 16 | 17 | cat >> $out/dysnomia-support/groups/${groupname} < $out/dysnomia-support/users/${username} < $out/dysnomia-support/users/${username}''} 46 | 47 | cat >> $out/dysnomia-support/users/${username} < b) (map (dependency: dependency.priority) dependencies)) + 1; 30 | 31 | sequenceNumberToString = number: 32 | if number < 10 then "0${toString number}" 33 | else toString number; 34 | in 35 | stdenv.mkDerivation { 36 | inherit name priority; 37 | buildCommand = '' 38 | mkdir -p $out 39 | 40 | cat > $out/${name}-docker-settings < $out/${name}-docker-createparams < 1 then "--" else "-"}${nameValuePair.name}" 48 | + (if nameValuePair ? value then "\n${toString nameValuePair.value}" else "") 49 | ) _dockerCreateParameters} 50 | EOF 51 | 52 | touch $out/${sequenceNumberToString priority}-${name}-docker-priority 53 | 54 | ${lib.optionalString useHostNixStore '' 55 | # Add configuration files with Nix store paths used from the host system so that they will not be garbage collected 56 | ${lib.optionalString (cmd != "") '' 57 | cat > $out/${name}-docker-cmd < $out/${name}-storepaths <&2 74 | exit 1 75 | fi 76 | 77 | source @commonchecks@ 78 | 79 | checkNixStateDir 80 | checkProfile 81 | composeOldProfilePath 82 | 83 | source @sysvinitchecks@ 84 | 85 | checkRunlevel 86 | 87 | if [ "$2" = "" ] 88 | then 89 | rcpath="$oldProfilePath/etc/rc.d/rc${runlevel}.d" 90 | else 91 | rcpath="$2/etc/rc.d/rc${runlevel}.d" 92 | fi 93 | 94 | # Execute the activities 95 | 96 | if [ "$reverse" = "1" ] 97 | then 98 | if [ -n "$(ls -A $rcpath 2> /dev/null)" ] 99 | then 100 | for i in $(ls $rcpath/S* | sort -r) 101 | do 102 | $i $activity 103 | done 104 | fi 105 | else 106 | if [ -n "$(ls -A $rcpath 2> /dev/null)" ] 107 | then 108 | for i in $(ls $rcpath/S*) 109 | do 110 | $i $activity 111 | done 112 | fi 113 | fi 114 | -------------------------------------------------------------------------------- /tests/services/nginx-reverse-proxy-hostbased/processes.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , libDir ? "${stateDir}/lib" 8 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 9 | , forceDisableUserChange ? false 10 | , processManager 11 | }: 12 | 13 | let 14 | sharedConstructors = import ../../../examples/services-agnostic/constructors/constructors.nix { 15 | inherit pkgs stateDir runtimeDir logDir cacheDir libDir tmpDir forceDisableUserChange processManager; 16 | }; 17 | 18 | constructors = import ../../../examples/webapps-agnostic/constructors/constructors.nix { 19 | inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager; 20 | webappMode = null; 21 | }; 22 | in 23 | rec { 24 | webapp1 = rec { 25 | port = 5000; 26 | dnsName = "webapp1.local"; 27 | 28 | pkg = constructors.webapp { 29 | inherit port; 30 | instanceSuffix = "1"; 31 | }; 32 | }; 33 | 34 | webapp2 = rec { 35 | port = 5001; 36 | dnsName = "webapp2.local"; 37 | 38 | pkg = constructors.webapp { 39 | inherit port; 40 | instanceSuffix = "2"; 41 | }; 42 | }; 43 | 44 | webapp3 = rec { 45 | port = 5002; 46 | dnsName = "webapp3.local"; 47 | 48 | pkg = constructors.webapp { 49 | inherit port; 50 | instanceSuffix = "3"; 51 | }; 52 | }; 53 | 54 | webapp4 = rec { 55 | port = 5003; 56 | dnsName = "webapp4.local"; 57 | 58 | pkg = constructors.webapp { 59 | inherit port; 60 | instanceSuffix = "4"; 61 | }; 62 | }; 63 | 64 | nginx = rec { 65 | port = if forceDisableUserChange then 8080 else 80; 66 | webapps = [ webapp1 webapp2 webapp3 webapp4 ]; 67 | 68 | pkg = sharedConstructors.nginxReverseProxyHostBased { 69 | inherit port webapps; 70 | } {}; 71 | }; 72 | 73 | webapp5 = rec { 74 | port = 5004; 75 | dnsName = "webapp5.local"; 76 | 77 | pkg = constructors.webapp { 78 | inherit port; 79 | instanceSuffix = "5"; 80 | }; 81 | }; 82 | 83 | webapp6 = rec { 84 | port = 5005; 85 | dnsName = "webapp6.local"; 86 | 87 | pkg = constructors.webapp { 88 | inherit port; 89 | instanceSuffix = "6"; 90 | }; 91 | }; 92 | 93 | nginx2 = rec { 94 | port = if forceDisableUserChange then 8081 else 81; 95 | webapps = [ webapp5 webapp6 ]; 96 | 97 | pkg = sharedConstructors.nginxReverseProxyHostBased { 98 | inherit port webapps; 99 | instanceSuffix = "2"; 100 | } {}; 101 | }; 102 | } 103 | -------------------------------------------------------------------------------- /nixproc/create-image-from-steps/steps/nix-processmgmt-dynamic.nix: -------------------------------------------------------------------------------- 1 | {pkgs, common, input, result}: 2 | 3 | let 4 | commonTools = (import ../../../tools { 5 | inherit pkgs; 6 | inherit (common) system; 7 | }).common; 8 | 9 | # If no processes.nix parameter was provided, generate a template 10 | templateFile = pkgs.writeTextFile { 11 | name = "processes.nix"; 12 | text = '' 13 | { pkgs ? import { inherit system; } 14 | , system ? builtins.currentSystem 15 | , stateDir ? "/var" 16 | , runtimeDir ? "''${stateDir}/run" 17 | , logDir ? "''${stateDir}/log" 18 | , cacheDir ? "''${stateDir}/cache" 19 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "''${stateDir}/tmp") 20 | , forceDisableUserChange ? false 21 | , processManager 22 | }: 23 | 24 | let 25 | nix-processmgmt-services = builtins.fetchGit { 26 | url = https://github.com/svanderburg/nix-processmgmt-services.git; 27 | ref = "master"; 28 | }; 29 | 30 | sharedConstructors = import "''${nix-processmgmt-services}/examples/services-agnostic/constructors.nix" { 31 | inherit pkgs stateDir runtimeDir logDir cacheDir tmpDir forceDisableUserChange processManager; 32 | }; 33 | in 34 | rec { 35 | /*nginx = rec { 36 | port = 8080; 37 | 38 | pkg = sharedConstructors.nginxReverseProxyHostBased { 39 | webapps = []; 40 | inherit port; 41 | } {}; 42 | };*/ 43 | } 44 | ''; 45 | }; 46 | in 47 | result // { 48 | contents = result.contents or [] 49 | ++ [ pkgs.dysnomia commonTools ]; 50 | 51 | runAsRoot = result.runAsRoot or "" + '' 52 | nixproc-init-state --state-dir ${input.stateDir} --runtime-dir ${input.runtimeDir} 53 | 54 | # Provide a processes.nix expression 55 | mkdir -p /etc/nixproc 56 | '' + (if input ? exprFile then '' 57 | cp ${input.exprFile} /etc/nixproc/processes.nix 58 | '' else '' 59 | cp ${templateFile} /etc/nixproc/processes.nix 60 | '') 61 | + '' 62 | chmod 644 /etc/nixproc/processes.nix 63 | '' + pkgs.lib.optionalString (input ? idResourcesFile) '' 64 | idResourcesFileName=$(basename ${input.idResourcesFile}) 65 | idResourcesTarget=/etc/nixproc/''${idResourcesFileName:33} 66 | 67 | cp ${input.idResourcesFile} $idResourcesTarget 68 | chmod 644 $idResourcesTarget 69 | '' + pkgs.lib.optionalString (input ? idsFile) '' 70 | idsFileName=$(basename ${input.idsFile}) 71 | idsFileTarget=/etc/nixproc/''${idsFileName:33} 72 | 73 | cp ${input.idsFile} $idsFileTarget 74 | chmod 644 $idsFileTarget 75 | ''; 76 | 77 | config = result.config or {} // { 78 | Env = result.config.Env or [] ++ [ 79 | "NIXPROC_PROCESSES=/etc/nixproc/processes.nix" 80 | ]; 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /tests/multi-process-images.nix: -------------------------------------------------------------------------------- 1 | {nixpkgs ? }: 2 | 3 | with import "${nixpkgs}/nixos/lib/testing-python.nix" { system = builtins.currentSystem; }; 4 | 5 | let 6 | pkgs = import nixpkgs {}; 7 | 8 | dockerProcessEnv = import ../nixproc/backends/systemd/build-systemd-env.nix { 9 | exprFile = ../nixproc/backends/docker/test-module/processes-docker.nix; 10 | }; 11 | 12 | processManagers = [ "supervisord" "sysvinit" "disnix" "s6-rc" ]; 13 | userManagementPolicies = [ "privileged" "unprivileged" ]; 14 | 15 | images = pkgs.lib.genAttrs processManagers (processManager: 16 | pkgs.lib.genAttrs userManagementPolicies (userManagementPolicy: 17 | import ../examples/multi-process-image { 18 | inherit processManager; 19 | forceDisableUserChange = userManagementPolicy == "unprivileged"; 20 | } 21 | ) 22 | ); 23 | 24 | nix-processmgmt = ./..; 25 | 26 | tools = import ../tools {}; 27 | 28 | env = "NIX_PATH=nixpkgs=${nixpkgs} SYSTEMD_TARGET_DIR=/etc/systemd-mutable/system"; 29 | in 30 | makeTest { 31 | name = "multi-process-images"; 32 | 33 | nodes.machine = 34 | {pkgs, ...}: 35 | 36 | { 37 | virtualisation.additionalPaths = [ pkgs.stdenv ] ++ pkgs.coreutils.all ++ [ dockerProcessEnv ]; 38 | virtualisation.writableStore = true; 39 | virtualisation.diskSize = 8192; 40 | virtualisation.memorySize = 8192; 41 | 42 | dysnomia = { 43 | enable = true; 44 | enableLegacyModules = false; 45 | }; 46 | 47 | environment.systemPackages = [ 48 | tools.common 49 | tools.systemd 50 | pkgs.docker 51 | ]; 52 | }; 53 | 54 | testScript = '' 55 | start_all() 56 | 57 | machine.succeed("mkdir -p /etc/systemd-mutable/system") 58 | 59 | # Deploy Docker as a systemd unit 60 | 61 | machine.succeed( 62 | "${env} nixproc-systemd-switch ${nix-processmgmt}/nixproc/backends/docker/test-module/processes-docker.nix" 63 | ) 64 | 65 | machine.wait_for_unit("nix-process-docker") 66 | machine.succeed("sleep 10") 67 | 68 | ${pkgs.lib.concatMapStrings (processManager: 69 | pkgs.lib.concatMapStrings (userManagementPolicy: 70 | let 71 | image = images."${processManager}"."${userManagementPolicy}"; 72 | in 73 | '' 74 | machine.succeed( 75 | "docker load -i ${image}" 76 | ) 77 | machine.succeed( 78 | "docker run --name multiprocess --detach --rm --network host multiprocess:test" 79 | ) 80 | machine.succeed("sleep 30") 81 | machine.succeed("curl --fail -H 'Host: webapp.local' http://localhost:8080") 82 | machine.succeed("docker stop multiprocess") 83 | machine.succeed("docker rmi multiprocess:test") 84 | '') userManagementPolicies 85 | ) processManagers}''; 86 | } 87 | -------------------------------------------------------------------------------- /nixproc/backends/supervisord/create-supervisord-program.nix: -------------------------------------------------------------------------------- 1 | {writeTextFile, stdenv, lib, createCredentials, supervisor, basePackages, forceDisableUserChange ? false, runtimeDir}: 2 | 3 | { 4 | # A name that identifies the process instance 5 | name 6 | # Indicates whether we want to use the pidproxy 7 | , useProxy ? false 8 | # Command line instruction to execute 9 | , command ? null 10 | # Name of the PID file that contains the PID of the running process 11 | , pidFile ? "${name}.pid" 12 | # Specifies which packages need to be in the PATH 13 | , path ? [] 14 | # An attribute set specifying arbitrary environment variables 15 | , environment ? {} 16 | # List of supervisord programs that this configuration depends on. This is used to derive the activation order. 17 | , dependencies ? [] 18 | # Specifies which groups and users that need to be created. 19 | , credentials ? {} 20 | # Arbitrary commands executed after generating the configuration files 21 | , postInstall ? "" 22 | # The remainder of the parameters directly translate to the properties described in: http://supervisord.org/configuration.html 23 | , ... 24 | }@params: 25 | 26 | let 27 | util = import ../util { 28 | inherit lib; 29 | }; 30 | 31 | properties = removeAttrs params ([ "name" "command" "useProxy" "pidFile" "path" "environment" "dependencies" "credentials" "postInstall" ] ++ lib.optional forceDisableUserChange "user"); 32 | 33 | priority = if dependencies == [] then 1 34 | else builtins.head (builtins.sort (a: b: a > b) (map (dependency: dependency.priority) dependencies)) + 1; 35 | 36 | _command = (lib.optionalString useProxy "${supervisor}/bin/pidproxy ${runtimeDir}/${pidFile} ") + command; 37 | 38 | _environment = util.appendPathToEnvironment { 39 | inherit environment; 40 | path = basePackages ++ path; 41 | }; 42 | 43 | confFile = writeTextFile { 44 | name = "${name}.conf"; 45 | text = '' 46 | [program:${name}] 47 | command=${_command} 48 | priority=${toString priority} 49 | '' 50 | + (if _environment == {} then "" else "environment=" + lib.concatMapStringsSep "," (name: 51 | let 52 | value = builtins.getAttr name _environment; 53 | in 54 | "${name}=\"${lib.escape [ "\"" ] (toString value)}\"" 55 | ) (builtins.attrNames _environment)) + 56 | "\n" 57 | + lib.concatMapStrings (name: 58 | let 59 | value = builtins.getAttr name properties; 60 | in 61 | ''${name}=${toString value} 62 | '' 63 | ) (builtins.attrNames properties); 64 | }; 65 | 66 | credentialsSpec = createCredentials credentials; 67 | in 68 | stdenv.mkDerivation { 69 | inherit name priority; 70 | buildCommand = '' 71 | mkdir -p $out/conf.d 72 | ln -s ${confFile} $out/conf.d/${name}.conf 73 | ln -s ${credentialsSpec}/dysnomia-support $out/dysnomia-support 74 | 75 | ${postInstall} 76 | ''; 77 | } 78 | -------------------------------------------------------------------------------- /nixproc/backends/launchd/generate-launchd-daemon.nix: -------------------------------------------------------------------------------- 1 | { createLaunchdDaemon 2 | , stdenv 3 | , lib 4 | , writeTextFile 5 | , runtimeDir ? "/var/run" 6 | , forceDisableUserChange 7 | }: 8 | 9 | { name 10 | , description 11 | , initialize 12 | , daemon 13 | , daemonArgs 14 | , instanceName 15 | , pidFile 16 | , foregroundProcess 17 | , foregroundProcessArgs 18 | , path 19 | , environment 20 | , directory 21 | , umask 22 | , nice 23 | , user 24 | , dependencies 25 | , credentials 26 | , overrides 27 | , postInstall 28 | }: 29 | 30 | let 31 | generateForegroundProxy = import ../util/generate-foreground-proxy.nix { 32 | inherit stdenv lib writeTextFile; 33 | }; 34 | 35 | chainLoadUser = if initialize == "" || forceDisableUserChange then null 36 | else user; 37 | 38 | Program = if foregroundProcess != null then 39 | if initialize == "" then foregroundProcess 40 | else generateForegroundProxy ({ 41 | wrapDaemon = false; 42 | user = chainLoadUser; 43 | executable = foregroundProcess; 44 | inherit name initialize runtimeDir stdenv; 45 | } // lib.optionalAttrs (instanceName != null) { 46 | inherit instanceName; 47 | } // lib.optionalAttrs (pidFile != null) { 48 | inherit pidFile; 49 | }) 50 | else generateForegroundProxy ({ 51 | wrapDaemon = true; 52 | user = chainLoadUser; 53 | executable = daemon; 54 | inherit name initialize runtimeDir stdenv; 55 | } // lib.optionalAttrs (instanceName != null) { 56 | inherit instanceName; 57 | } // lib.optionalAttrs (pidFile != null) { 58 | inherit pidFile; 59 | }); 60 | ProgramArguments = [ Program ] ++ (if foregroundProcess != null then foregroundProcessArgs else daemonArgs); 61 | 62 | generatedTargetSpecificArgs = { 63 | inherit name credentials postInstall Program; 64 | } // lib.optionalAttrs (ProgramArguments != [ Program ]) { 65 | inherit ProgramArguments; 66 | } // lib.optionalAttrs (environment != {}) { 67 | EnvironmentVariables = environment; 68 | } // lib.optionalAttrs (path != []) { 69 | inherit path; 70 | } // lib.optionalAttrs (directory != null) { 71 | WorkingDirectory = directory; 72 | } // lib.optionalAttrs (umask != null) { 73 | Umask = umask; 74 | } // lib.optionalAttrs (nice != null) { 75 | Nice = nice; 76 | } // lib.optionalAttrs (user != null && chainLoadUser == null) { 77 | UserName = user; 78 | }; 79 | 80 | targetSpecificArgs = 81 | if builtins.isFunction overrides then overrides generatedTargetSpecificArgs 82 | else lib.recursiveUpdate generatedTargetSpecificArgs overrides; 83 | 84 | daemonConfig = createLaunchdDaemon targetSpecificArgs; 85 | in 86 | if dependencies == [] then daemonConfig else 87 | builtins.trace "WARNING: dependencies have been specified for process: ${name}, but launchd has no notion of process dependencies. Proper activation ordering cannot be guaranteed!" daemonConfig 88 | -------------------------------------------------------------------------------- /tests/services/s6-svscan/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, testService, processManagers, profiles }: 2 | 3 | let 4 | generateTestExecutable = instanceName: 5 | pkgs.writeTextFile { 6 | name = "test-${instanceName}"; 7 | text = '' 8 | #! ${pkgs.stdenv.shell} -e 9 | 10 | while true 11 | do 12 | echo "Hello ${instanceName}!" >&2 13 | sleep 1 14 | done 15 | ''; 16 | executable = true; 17 | }; 18 | 19 | generateTestConfigDir = instanceName: 20 | pkgs.stdenv.mkDerivation { 21 | name = "sv"; 22 | buildCommand = '' 23 | mkdir -p $out/test-${instanceName} 24 | cd $out/test-${instanceName} 25 | 26 | # Generate longrun service for test process 27 | echo "longrun" > type 28 | cat > run < type 37 | cat > contents < { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , cacheDir ? "${stateDir}/cache" 6 | , logDir ? "${stateDir}/log" 7 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 8 | , forceDisableUserChange ? false 9 | }: 10 | 11 | let 12 | ids = if builtins.pathExists ./ids-advanced.nix then (import ./ids-advanced.nix).ids else {}; 13 | 14 | constructors = import ./constructors/constructors.nix { 15 | inherit pkgs stateDir runtimeDir logDir cacheDir tmpDir forceDisableUserChange ids; 16 | }; 17 | in 18 | rec { 19 | webapp1 = rec { 20 | port = ids.webappPorts.webapp1 or 0; 21 | dnsName = "webapp1.local"; 22 | 23 | pkg = constructors.webapp { 24 | inherit port; 25 | instanceSuffix = "1"; 26 | }; 27 | 28 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 29 | }; 30 | 31 | webapp2 = rec { 32 | port = ids.webappPorts.webapp2 or 0; 33 | dnsName = "webapp2.local"; 34 | 35 | pkg = constructors.webapp { 36 | inherit port; 37 | instanceSuffix = "2"; 38 | }; 39 | 40 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 41 | }; 42 | 43 | webapp3 = rec { 44 | port = ids.webappPorts.webapp3 or 0; 45 | dnsName = "webapp3.local"; 46 | 47 | pkg = constructors.webapp { 48 | inherit port; 49 | instanceSuffix = "3"; 50 | }; 51 | 52 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 53 | }; 54 | 55 | webapp4 = rec { 56 | port = ids.webappPorts.webapp4 or 0; 57 | dnsName = "webapp4.local"; 58 | 59 | pkg = constructors.webapp { 60 | inherit port; 61 | instanceSuffix = "4"; 62 | }; 63 | 64 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 65 | }; 66 | 67 | nginx = rec { 68 | port = ids.nginxPorts.nginx or 0; 69 | 70 | pkg = constructors.nginxReverseProxy { 71 | webapps = [ webapp1 webapp2 webapp3 webapp4 ]; 72 | inherit port; 73 | } {}; 74 | 75 | requiresUniqueIdsFor = [ "nginxPorts" "uids" "gids" ]; 76 | }; 77 | 78 | webapp5 = rec { 79 | port = ids.webappPorts.webapp5 or 0; 80 | dnsName = "webapp5.local"; 81 | 82 | pkg = constructors.webapp { 83 | inherit port; 84 | instanceSuffix = "5"; 85 | }; 86 | 87 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 88 | }; 89 | 90 | webapp6 = rec { 91 | port = ids.webappPorts.webapp6 or 0; 92 | dnsName = "webapp6.local"; 93 | 94 | pkg = constructors.webapp { 95 | inherit port; 96 | instanceSuffix = "6"; 97 | }; 98 | 99 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 100 | }; 101 | 102 | nginx2 = rec { 103 | port = ids.nginxPorts.nginx2 or 0; 104 | 105 | pkg = constructors.nginxReverseProxy { 106 | webapps = [ webapp5 webapp6 ]; 107 | inherit port; 108 | instanceSuffix = "2"; 109 | } {}; 110 | 111 | requiresUniqueIdsFor = [ "nginxPorts" "uids" "gids" ]; 112 | }; 113 | } 114 | -------------------------------------------------------------------------------- /nixproc/backends/util/generate-foreground-proxy.nix: -------------------------------------------------------------------------------- 1 | {stdenv, lib, writeTextFile}: 2 | 3 | { name 4 | , wrapDaemon 5 | , initialize 6 | , executable 7 | , stdenv 8 | , runtimeDir 9 | , instanceName ? null 10 | , pidFile ? (if instanceName == null then null else "${runtimeDir}/${instanceName}.pid") 11 | , user ? null 12 | , nice ? null 13 | , directory ? null 14 | , umask ? null 15 | }: 16 | 17 | let 18 | chainload-user = (import ../../../tools {}).chainload-user; 19 | 20 | _pidFile = if pidFile == null then "${runtimeDir}/$(basename ${executable}).pid" else pidFile; 21 | in 22 | writeTextFile { 23 | name = "${name}-foregroundproxy.sh"; 24 | text = '' 25 | #! ${stdenv.shell} -e 26 | 27 | ${initialize} 28 | 29 | ${if wrapDaemon then '' 30 | export _TOP_PID=$$ 31 | 32 | # Handle to SIGTERM and SIGINT signals and forward them to the daemon process 33 | _term() 34 | { 35 | trap "exit 0" TERM 36 | kill -TERM "$pid" 37 | kill $_TOP_PID 38 | } 39 | 40 | _interrupt() 41 | { 42 | kill -INT "$pid" 43 | } 44 | 45 | trap _term SIGTERM 46 | trap _interrupt SIGINT 47 | 48 | ${lib.optionalString (directory != null) '' 49 | cd ${directory} 50 | ''} 51 | ${lib.optionalString (umask != null) '' 52 | umask ${umask} 53 | ''} 54 | 55 | # Start process in the background as a daemon 56 | ${lib.optionalString (user != null) "${chainload-user}/bin/nixproc-chainload-user ${user} "}${lib.optionalString (nice != null) "nice -n ${nice} "}${executable} "$@" 57 | 58 | # Wait for the PID file to become available. Useful to work with daemons that don't behave well enough. 59 | count=1 # Start with 1, because 0 returns a non-zero exit status when incrementing it 60 | 61 | while [ ! -f "${_pidFile}" ] 62 | do 63 | if [ $count -eq 10 ] 64 | then 65 | echo "It does not seem that there isn't any pid file! Giving up!" 66 | exit 1 67 | fi 68 | 69 | echo "Waiting for ${_pidFile} to become available..." 70 | sleep 1 71 | 72 | ((count++)) 73 | done 74 | 75 | # Determine the daemon's PID by using the PID file 76 | pid=$(cat ${_pidFile}) 77 | 78 | # Wait in the background for the PID to terminate 79 | ${if stdenv.isDarwin then '' 80 | lsof -p $pid +r 3 &>/dev/null & 81 | '' else if stdenv.isLinux || stdenv.isCygwin then '' 82 | tail --pid=$pid -f /dev/null & 83 | '' else if stdenv.isBSD || stdenv.isSunOS then '' 84 | pwait $pid & 85 | '' else throw "Don't know how to wait for process completion on system: ${stdenv.system}"} 86 | 87 | # Wait for the blocker process to complete. We use wait, so that bash can still 88 | # handle the SIGTERM and SIGINT signals that may be sent to it by a process 89 | # manager 90 | blocker_pid=$! 91 | wait $blocker_pid 92 | '' else '' 93 | exec ${lib.optionalString (user != null) "${chainload-user}/bin/nixproc-chainload-user ${user} "}${lib.optionalString (nice != null) "nice -n ${nice} "}"${executable}" "$@" 94 | ''} 95 | ''; 96 | executable = true; 97 | } 98 | -------------------------------------------------------------------------------- /nixproc/backends/disnix/generate-process-script.nix: -------------------------------------------------------------------------------- 1 | { createProcessScript, writeTextFile, stdenv, lib, daemon, basePackages 2 | , runtimeDir, logDir, tmpDir, forceDisableUserChange 3 | }: 4 | 5 | let 6 | daemonPkg = daemon; # Circumvent name conflict with the parameter in the next function header 7 | in 8 | 9 | { name 10 | , description 11 | , initialize 12 | , daemon 13 | , daemonArgs 14 | , instanceName 15 | , pidFile 16 | , foregroundProcess 17 | , foregroundProcessArgs 18 | , path 19 | , environment 20 | , directory 21 | , umask 22 | , nice 23 | , user 24 | , dependencies 25 | , credentials 26 | , overrides 27 | , postInstall 28 | }: 29 | 30 | let 31 | util = import ../util { 32 | inherit lib; 33 | }; 34 | 35 | _environment = util.appendPathToEnvironment { 36 | inherit environment; 37 | path = basePackages ++ [ daemonPkg ] ++ path; 38 | }; 39 | 40 | _user = util.determineUser { 41 | inherit user forceDisableUserChange; 42 | }; 43 | 44 | pidFilesDir = util.determinePIDFilesDir { 45 | inherit user runtimeDir tmpDir; # We can't use _user because we want to keep the path convention the same 46 | }; 47 | 48 | _pidFile = util.autoGeneratePIDFilePath { 49 | inherit pidFile instanceName pidFilesDir; 50 | }; 51 | 52 | invocationCommand = 53 | if daemon != null then util.invokeDaemon { 54 | process = daemon; 55 | args = daemonArgs; 56 | su = "su"; 57 | user = _user; 58 | } 59 | else if foregroundProcess != null then util.daemonizeForegroundProcess { 60 | daemon = "daemon"; 61 | process = foregroundProcess; 62 | args = foregroundProcessArgs; 63 | pidFile = _pidFile; 64 | user = _user; 65 | outputLogFile = util.autoGenerateDaemonLogFilePath { 66 | inherit name instanceName logDir tmpDir; 67 | user = _user; 68 | enableDaemonOutputLogging = true; 69 | }; 70 | inherit pidFilesDir; 71 | } 72 | else throw "I don't know how to start this process!"; 73 | 74 | generatedTargetSpecificArgs = { 75 | inherit name dependencies credentials postInstall; 76 | 77 | process = writeTextFile { 78 | name = "${name}-process-wrapper"; 79 | executable = true; 80 | text = '' 81 | #! ${stdenv.shell} -e 82 | '' 83 | + util.printShellEnvironmentVariables { 84 | environment = _environment; 85 | allowSystemPath = true; 86 | } 87 | + lib.optionalString (umask != null) '' 88 | umask ${umask} 89 | '' 90 | + lib.optionalString (initialize != null) '' 91 | ${initialize} 92 | '' 93 | + lib.optionalString (directory != null) '' 94 | cd ${directory} 95 | '' 96 | + "exec ${lib.optionalString (nice != null) "nice -n ${toString nice}"} ${invocationCommand}"; 97 | }; 98 | } // lib.optionalAttrs (_pidFile != null) { 99 | pidFile = _pidFile; 100 | }; 101 | 102 | targetSpecificArgs = 103 | if builtins.isFunction overrides then overrides generatedTargetSpecificArgs 104 | else lib.recursiveUpdate generatedTargetSpecificArgs overrides; 105 | in 106 | createProcessScript targetSpecificArgs 107 | -------------------------------------------------------------------------------- /examples/services-agnostic/constructors/nginx/nginx-reverse-proxy-hostbased.nix: -------------------------------------------------------------------------------- 1 | {createManagedProcess, stdenv, lib, writeTextFile, nginx, runtimeDir, stateDir, cacheDir, forceDisableUserChange}: 2 | 3 | { port ? 80 4 | , webapps ? [] 5 | , instanceSuffix ? "" 6 | , instanceName ? "nginx${instanceSuffix}" 7 | , workerConnections ? 190000 8 | }: 9 | 10 | interDependencies: 11 | 12 | let 13 | user = instanceName; 14 | group = instanceName; 15 | 16 | nginxStateDir = "${stateDir}/${instanceName}"; 17 | nginxLogDir = "${nginxStateDir}/logs"; 18 | nginxCacheDir = "${cacheDir}/${instanceName}"; 19 | in 20 | import ./default.nix { 21 | inherit createManagedProcess lib nginx stateDir forceDisableUserChange runtimeDir cacheDir; 22 | } { 23 | inherit instanceName; 24 | 25 | dependencies = map (webapp: webapp.pkg) webapps 26 | ++ map (interDependency: interDependency.pkgs."${stdenv.system}") (builtins.attrValues interDependencies); 27 | 28 | configFile = writeTextFile { 29 | name = "nginx.conf"; 30 | text = '' 31 | pid ${runtimeDir}/${instanceName}.pid; 32 | error_log ${nginxLogDir}/error.log; 33 | 34 | ${lib.optionalString (!forceDisableUserChange) '' 35 | user ${user} ${group}; 36 | ''} 37 | 38 | events { 39 | worker_connections ${toString workerConnections}; 40 | } 41 | 42 | http { 43 | access_log ${nginxLogDir}/access.log; 44 | error_log ${nginxLogDir}/error.log; 45 | 46 | proxy_temp_path ${nginxCacheDir}/proxy; 47 | client_body_temp_path ${nginxCacheDir}/client_body; 48 | fastcgi_temp_path ${nginxCacheDir}/fastcgi; 49 | uwsgi_temp_path ${nginxCacheDir}/uwsgi; 50 | scgi_temp_path ${nginxCacheDir}/scgi; 51 | 52 | ${lib.concatMapStrings (dependency: '' 53 | upstream webapp${toString dependency.port} { 54 | server localhost:${toString dependency.port}; 55 | } 56 | '') webapps} 57 | 58 | ${lib.concatMapStrings (paramName: 59 | let 60 | dependency = builtins.getAttr paramName interDependencies; 61 | in 62 | '' 63 | upstream webapp${toString dependency.port} { 64 | server ${dependency.target.properties.hostname}:${toString dependency.port}; 65 | } 66 | '') (builtins.attrNames interDependencies)} 67 | 68 | # Fallback virtual host displaying an error page. This is what users see 69 | # if they connect to a non-deployed web application. 70 | # Without it, nginx redirects to the first available virtual host, giving 71 | # unpredictable results. This could happen while an upgrade is in progress. 72 | 73 | server { 74 | listen ${toString port}; 75 | server_name aaaa; 76 | root ${./errorpage}; 77 | } 78 | 79 | ${lib.concatMapStrings (dependency: '' 80 | server { 81 | listen ${toString port}; 82 | server_name ${dependency.dnsName}; 83 | 84 | location / { 85 | proxy_pass http://webapp${toString dependency.port}; 86 | } 87 | } 88 | '') (webapps ++ builtins.attrValues interDependencies)} 89 | } 90 | ''; 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /examples/webapps-sysvinit/constructors/nginx/nginx-reverse-proxy.nix: -------------------------------------------------------------------------------- 1 | {createSystemVInitScript, stdenv, lib, writeTextFile, nginx, runtimeDir, stateDir, cacheDir, logDir, forceDisableUserChange}: 2 | 3 | { port ? 80 4 | , webapps ? [] 5 | , instanceSuffix ? "" 6 | , instanceName ? "nginx${instanceSuffix}" 7 | , workerConnections ? 190000 8 | }: 9 | 10 | interDependencies: 11 | 12 | let 13 | user = instanceName; 14 | group = instanceName; 15 | 16 | nginxStateDir = "${stateDir}/${instanceName}"; 17 | nginxLogDir = "${nginxStateDir}/logs"; 18 | nginxCacheDir = "${cacheDir}/${instanceName}"; 19 | in 20 | import ./default.nix { 21 | inherit createSystemVInitScript lib nginx runtimeDir cacheDir forceDisableUserChange; 22 | stateDir = nginxStateDir; 23 | } { 24 | inherit instanceName; 25 | 26 | dependencies = map (webapp: webapp.pkg) webapps 27 | ++ map (interDependency: interDependency.pkgs."${stdenv.system}") (builtins.attrValues interDependencies); 28 | 29 | configFile = writeTextFile { 30 | name = "nginx.conf"; 31 | text = '' 32 | pid ${runtimeDir}/${instanceName}.pid; 33 | error_log ${nginxLogDir}/error.log; 34 | 35 | ${lib.optionalString (!forceDisableUserChange) '' 36 | user ${user} ${group}; 37 | ''} 38 | 39 | events { 40 | worker_connections ${toString workerConnections}; 41 | } 42 | 43 | http { 44 | access_log ${nginxLogDir}/access.log; 45 | error_log ${nginxLogDir}/error.log; 46 | 47 | proxy_temp_path ${nginxCacheDir}/proxy; 48 | client_body_temp_path ${nginxCacheDir}/client_body; 49 | fastcgi_temp_path ${nginxCacheDir}/fastcgi; 50 | uwsgi_temp_path ${nginxCacheDir}/uwsgi; 51 | scgi_temp_path ${nginxCacheDir}/scgi; 52 | 53 | ${lib.concatMapStrings (dependency: '' 54 | upstream webapp${toString dependency.port} { 55 | server localhost:${toString dependency.port}; 56 | } 57 | '') webapps} 58 | 59 | ${lib.concatMapStrings (paramName: 60 | let 61 | dependency = builtins.getAttr paramName interDependencies; 62 | in 63 | '' 64 | upstream webapp${toString dependency.port} { 65 | server ${dependency.target.properties.hostname}:${toString dependency.port}; 66 | } 67 | '') (builtins.attrNames interDependencies)} 68 | 69 | # Fallback virtual host displaying an error page. This is what users see 70 | # if they connect to a non-deployed web application. 71 | # Without it, nginx redirects to the first available virtual host, giving 72 | # unpredictable results. This could happen while an upgrade is in progress. 73 | 74 | server { 75 | client_body_temp_path ${nginxCacheDir}/client_body; 76 | listen ${toString port}; 77 | server_name aaaa; 78 | root ${./errorpage}; 79 | } 80 | 81 | ${lib.concatMapStrings (dependency: '' 82 | server { 83 | client_body_temp_path ${nginxCacheDir}/client_body; 84 | listen ${toString port}; 85 | server_name ${dependency.dnsName}; 86 | 87 | location / { 88 | proxy_pass http://webapp${toString dependency.port}; 89 | } 90 | } 91 | '') (webapps ++ builtins.attrValues interDependencies)} 92 | } 93 | ''; 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /nixproc/create-managed-process/agnostic/create-managed-process.nix: -------------------------------------------------------------------------------- 1 | { processManager, generators ? {}, stdenv }: 2 | 3 | { 4 | # A name that identifies the process instance 5 | name ? instanceName 6 | # A more human-readable description of the process 7 | , description ? name 8 | # Shell commands that specify how the state should be initialized. This script runs as root. 9 | , initialize ? "" 10 | # Path to a process to execute (both in foreground and daemon mode) 11 | , process ? null 12 | # Generic command-line parameters propagated to the process 13 | , args ? [] 14 | # The executable that needs to run to start the process is daemon mode 15 | , daemon ? process 16 | # Extra arguments appended to args when the process runs in daemon mode 17 | , daemonExtraArgs ? [] 18 | # Command-line arguments propagated to the daemon 19 | , daemonArgs ? (args ++ daemonExtraArgs) 20 | # A name that uniquely identifies each process instance. It is used to generate a unique PID file and as a process name when none was specified. 21 | , instanceName ? null 22 | # Path to a PID file that the system should use to manage the process. If null, it will use the default path (typically the global runtime dir or temp dir). 23 | , pidFile ? null 24 | # The executable that needs to run to start the process in foreground mode 25 | , foregroundProcess ? process 26 | # Extra arguments appended to args when the process runs in foreground mode 27 | , foregroundProcessExtraArgs ? [] 28 | # Command-line arguments propagated to the foreground process 29 | , foregroundProcessArgs ? (args ++ foregroundProcessExtraArgs) 30 | # Specifies which packages need to be in the PATH 31 | , path ? [] 32 | # An attribute set specifying arbitrary environment variables 33 | , environment ? {} 34 | # If not null, the current working directory will be changed before executing any activities 35 | , directory ? null 36 | # If not null, the umask will be changed before executing any activities 37 | , umask ? null 38 | # If not null, the nice level be changed before executing any activities 39 | , nice ? null 40 | # Specifies as which user the process should run. If null, the user privileges will not be changed. 41 | , user ? null 42 | # Dependencies on other processes. Typically, this specification is used to derive the activation order. 43 | , dependencies ? [] 44 | # Specifies which groups and users that need to be created. 45 | , credentials ? {} 46 | # Specifies process manager specific properties that augmented to the generated function parameters 47 | , overrides ? {} 48 | # Arbitrary build commands executed after generating the configuration files 49 | , postInstall ? "" 50 | }@properties: 51 | 52 | if name == null then throw "No process name was specified or can be inferred!" 53 | else 54 | 55 | let 56 | createAgnosticConfig = import ./create-agnostic-config.nix { 57 | inherit stdenv; 58 | }; 59 | 60 | generateProcessFun = if builtins.hasAttr processManager generators 61 | then builtins.getAttr processManager generators 62 | else throw "Unknown process manager: ${processManager}"; 63 | in 64 | if processManager == null then createAgnosticConfig properties 65 | else generateProcessFun { 66 | inherit name description initialize daemon daemonArgs instanceName pidFile foregroundProcess foregroundProcessArgs path environment directory umask nice user dependencies credentials postInstall; 67 | overrides = if builtins.hasAttr processManager overrides then builtins.getAttr processManager overrides else {}; 68 | } 69 | -------------------------------------------------------------------------------- /examples/webapps-agnostic/processes-advanced.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { inherit system; } 2 | , system ? builtins.currentSystem 3 | , stateDir ? "/var" 4 | , runtimeDir ? "${stateDir}/run" 5 | , logDir ? "${stateDir}/log" 6 | , cacheDir ? "${stateDir}/cache" 7 | , libDir ? "${stateDir}/lib" 8 | , tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") 9 | , forceDisableUserChange ? false 10 | , processManager 11 | , webappMode ? null 12 | }: 13 | 14 | let 15 | ids = if builtins.pathExists ./ids-advanced.nix then (import ./ids-advanced.nix).ids else {}; 16 | 17 | sharedConstructors = import ../services-agnostic/constructors/constructors.nix { 18 | inherit pkgs stateDir runtimeDir logDir cacheDir libDir tmpDir forceDisableUserChange processManager ids; 19 | }; 20 | 21 | constructors = import ./constructors/constructors.nix { 22 | inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager webappMode ids; 23 | }; 24 | in 25 | rec { 26 | webapp1 = rec { 27 | port = ids.webappPorts.webapp1 or 0; 28 | dnsName = "webapp1.local"; 29 | 30 | pkg = constructors.webapp { 31 | inherit port; 32 | instanceSuffix = "1"; 33 | }; 34 | 35 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 36 | }; 37 | 38 | webapp2 = rec { 39 | port = ids.webappPorts.webapp2 or 0; 40 | dnsName = "webapp2.local"; 41 | 42 | pkg = constructors.webapp { 43 | inherit port; 44 | instanceSuffix = "2"; 45 | }; 46 | 47 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 48 | }; 49 | 50 | webapp3 = rec { 51 | port = ids.webappPorts.webapp3 or 0; 52 | dnsName = "webapp3.local"; 53 | 54 | pkg = constructors.webapp { 55 | inherit port; 56 | instanceSuffix = "3"; 57 | }; 58 | 59 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 60 | }; 61 | 62 | webapp4 = rec { 63 | port = ids.webappPorts.webapp4 or 0; 64 | dnsName = "webapp4.local"; 65 | 66 | pkg = constructors.webapp { 67 | inherit port; 68 | instanceSuffix = "4"; 69 | }; 70 | 71 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 72 | }; 73 | 74 | nginx = rec { 75 | port = ids.nginxPorts.nginx or 0; 76 | 77 | pkg = sharedConstructors.nginxReverseProxyHostBased { 78 | webapps = [ webapp1 webapp2 webapp3 webapp4 ]; 79 | inherit port; 80 | } {}; 81 | 82 | requiresUniqueIdsFor = [ "nginxPorts" "uids" "gids" ]; 83 | }; 84 | 85 | webapp5 = rec { 86 | port = ids.webappPorts.webapp5 or 0; 87 | dnsName = "webapp5.local"; 88 | 89 | pkg = constructors.webapp { 90 | inherit port; 91 | instanceSuffix = "5"; 92 | }; 93 | 94 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 95 | }; 96 | 97 | webapp6 = rec { 98 | port = ids.webappPorts.webapp6 or 0; 99 | dnsName = "webapp6.local"; 100 | 101 | pkg = constructors.webapp { 102 | inherit port; 103 | instanceSuffix = "6"; 104 | }; 105 | 106 | requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; 107 | }; 108 | 109 | nginx2 = rec { 110 | port = ids.nginxPorts.nginx2 or 0; 111 | 112 | pkg = sharedConstructors.nginxReverseProxyHostBased { 113 | webapps = [ webapp5 webapp6 ]; 114 | inherit port; 115 | instanceSuffix = "2"; 116 | } {}; 117 | 118 | requiresUniqueIdsFor = [ "nginxPorts" "uids" "gids" ]; 119 | }; 120 | } 121 | -------------------------------------------------------------------------------- /nixproc/backends/launchd/create-launchd-daemon.nix: -------------------------------------------------------------------------------- 1 | { writeTextFile 2 | , stdenv 3 | , lib 4 | , createCredentials 5 | 6 | # Specifies whether user changing functionality should be disabled or not 7 | , forceDisableUserChange ? false 8 | # Prefix that is in front of all launchd plist files generated by this function 9 | , prefix ? "org.nixos." 10 | }: 11 | 12 | { 13 | # A name that identifies the process instance 14 | name 15 | # Specifies which packages need to be in the PATH 16 | , path ? [] 17 | # Specifies which groups and users that need to be created. 18 | , credentials ? {} 19 | # Arbitrary build commands executed after generating the configuration files 20 | , postInstall ? "" 21 | # The remaining parameters are directly translated to plist XML properties. 22 | # Possible configuration options can be found here: https://www.launchd.info 23 | , ... 24 | }@args: 25 | 26 | let 27 | util = import ../util { 28 | inherit lib; 29 | }; 30 | 31 | environment = util.appendPathToEnvironment { 32 | environment = lib.mapAttrs (name: value: toString value) args.EnvironmentVariables or {}; # Convert all environment variables to strings 33 | inherit path; 34 | }; 35 | 36 | label = if args ? Label then args.Label else "${prefix}${name}"; 37 | 38 | properties = { 39 | Label = label; 40 | } // removeAttrs args ([ "name" "path" "credentials" "postInstall" ] ++ lib.optional forceDisableUserChange "UserName") // lib.optionalAttrs (environment != {}) { 41 | EnvironmentVariables = environment; 42 | }; 43 | 44 | attrsToPList = attrs: 45 | "\n" 46 | + lib.concatMapStrings (name: 47 | let 48 | value = builtins.getAttr name attrs; 49 | in 50 | '' 51 | ${name} 52 | ${exprToPList value} 53 | '' 54 | ) (builtins.attrNames attrs) 55 | + "\n"; 56 | 57 | listToPList = list: 58 | "\n" 59 | + lib.concatMapStrings (value: exprToPList value + "\n") list 60 | + "\n"; 61 | 62 | exprToPList = expr: 63 | let 64 | exprType = builtins.typeOf expr; 65 | in 66 | if exprType == "bool" then 67 | if expr then "" else "" 68 | else if exprType == "int" then "${toString expr}" 69 | else if exprType == "float" then "${toString expr}" 70 | else if exprType == "string" then "${expr}" 71 | else if exprType == "set" then 72 | if lib.isDerivation expr 73 | then "${expr}" 74 | else attrsToPList expr 75 | else if exprType == "list" then listToPList expr 76 | else if exprType == "null" then "" 77 | else if exprType == "lambda" then throw "Cannot convert a lambda to a plist property" 78 | else "${expr}"; 79 | 80 | launchdDaemonConfig = writeTextFile { 81 | name = "${label}.plist"; 82 | text = '' 83 | 84 | 85 | 86 | 87 | ${exprToPList properties} 88 | 89 | ''; 90 | }; 91 | 92 | credentialsSpec = createCredentials credentials; 93 | in 94 | stdenv.mkDerivation { 95 | inherit name; 96 | buildCommand = '' 97 | mkdir -p $out/Library/LaunchDaemons 98 | ln -s ${launchdDaemonConfig} $out/Library/LaunchDaemons/${label}.plist 99 | ln -s ${credentialsSpec}/dysnomia-support $out/dysnomia-support 100 | 101 | ${postInstall} 102 | ''; 103 | } 104 | -------------------------------------------------------------------------------- /nixproc/backends/util/default.nix: -------------------------------------------------------------------------------- 1 | {lib}: 2 | 3 | rec { 4 | /* 5 | * Composes a PATH environment variable from a collection of packages by 6 | * translating their paths to bin/ sub folders 7 | */ 8 | composePathEnvVariable = {path}: 9 | builtins.concatStringsSep ":" (map (package: "${package}/bin") path); 10 | 11 | /* 12 | * Appends the bin/ sub folders in the packages in path as a PATH environment 13 | * variable to the environment. 14 | */ 15 | appendPathToEnvironment = {environment, path}: 16 | lib.optionalAttrs (path != []) { 17 | PATH = composePathEnvVariable { 18 | inherit path; 19 | }; 20 | } // environment; 21 | 22 | /* 23 | * Prints escaped export statements that configure environment variables 24 | * 25 | * Parameters: 26 | * environment: attribute set in which the keys are the environment variables names and the values to environment variable values 27 | * allowSystemPath: whether to allow access to the original system PATH 28 | */ 29 | printShellEnvironmentVariables = {environment, allowSystemPath ? true}: 30 | lib.concatMapStrings (name: 31 | let 32 | value = builtins.getAttr name environment; 33 | in 34 | '' 35 | export ${name}=${lib.escapeShellArg value}${lib.optionalString (allowSystemPath && name == "PATH") ":$PATH"} 36 | '' 37 | ) (builtins.attrNames environment); 38 | 39 | /* 40 | * Determines the actual user name 41 | */ 42 | determineUser = {user, forceDisableUserChange}: 43 | if forceDisableUserChange then null else user; 44 | 45 | /* 46 | * Determines the preferred directory in which PID files should be stored. 47 | * For privileged users it is in the runtime dir, unprivileged users use the 48 | * temp dir. 49 | */ 50 | determinePIDFilesDir = {user, runtimeDir, tmpDir}: 51 | if user == null then runtimeDir else tmpDir; 52 | 53 | /* 54 | * Auto-generates the path to the preferred PID file if none has been 55 | * specified. 56 | */ 57 | autoGeneratePIDFilePath = {pidFile, instanceName, pidFilesDir}: 58 | if pidFile == null then 59 | if instanceName == null then null 60 | else "${pidFilesDir}/${instanceName}.pid" 61 | else pidFile; 62 | 63 | /* 64 | * Auto-generates the path to the log file that captures the 65 | * output of a process invoked with the daemon command 66 | */ 67 | autoGenerateDaemonLogFilePath = {name, instanceName, logDir, tmpDir, user, enableDaemonOutputLogging ? true}: 68 | if enableDaemonOutputLogging then 69 | if instanceName == null then 70 | if user == null then "${logDir}/nixproc-${name}.log" 71 | else "${tmpDir}/nixproc-${name}.log" 72 | else 73 | if user == null then "${logDir}/nixproc-${instanceName}.log" 74 | else "${tmpDir}/nixproc-${instanceName}.log" 75 | else null; 76 | 77 | /* 78 | * Creates a shell command invocation that deamonizes a foreground process by 79 | * using libslack's daemon command. 80 | */ 81 | daemonizeForegroundProcess = {daemon, process, args, pidFile ? null, pidFilesDir, user ? null, outputLogFile ? null}: 82 | "${daemon} --unsafe --inherit" 83 | + (if outputLogFile == null then "" else " --output ${outputLogFile}") 84 | + (if pidFile == null then " --pidfiles ${pidFilesDir} --name $(basename ${process})" else " --pidfile ${pidFile}") 85 | + lib.optionalString (user != null) " --user ${user}" 86 | + " -- ${process} ${lib.escapeShellArgs args}"; 87 | 88 | /* 89 | * Creates a daemon command invocation that escapes parameters and changes the 90 | * user, if needed. 91 | */ 92 | invokeDaemon = {process, args, su, user ? null}: 93 | let 94 | invocation = "${process} ${lib.escapeShellArgs args}"; 95 | in 96 | if user == null then invocation 97 | else "${su} ${user} -c ${lib.escapeShellArgs [ invocation ]}"; 98 | } 99 | -------------------------------------------------------------------------------- /nixproc/backends/cygrunsrv/create-cygrunsrv-params.nix: -------------------------------------------------------------------------------- 1 | { stdenv 2 | , lib 3 | , writeTextFile 4 | 5 | # Prefix that is in front of all Windows services generated by this function 6 | , prefix ? "nix-process-" 7 | }: 8 | 9 | { 10 | # A name that identifies the process instance 11 | name 12 | # A more human readable name that identifies the process 13 | , displayName ? "${prefix}${name}" 14 | # Path to the executable to run 15 | , path 16 | # Command-line arguments propagated to the executable 17 | , args ? [] 18 | # An attribute set specifying arbitrary environment variables 19 | , environment ? {} 20 | # Specifies whether this service needs to be automatically started or not. 21 | # 'manual' indicates manual start, 'auto' indicates automatic start 22 | , type ? "auto" 23 | # Specifies as which user the process should run. If null, the user privileges will not be changed. 24 | , user ? null 25 | # The password of the user so that the user privileges can be changed 26 | , password ? null 27 | # File where the stdin should read from. null indicates that no file should be read 28 | , stdin ? null 29 | # File where the stdout should write to. null discards output 30 | , stdout ? null 31 | # File where the stderr should write to. null discards output 32 | , stderr ? null 33 | # The signal that needs to be sent to the process to terminate it 34 | , terminateSignal ? "TERM" 35 | # Indicates whether the process should be terminated on shutdown 36 | , terminateOnShutdown ? false 37 | # Dependencies on other Windows services. The service manager makes sure that dependencies are activated first. 38 | , dependencies ? [] 39 | # Specifies which packages need to be in the PATH 40 | , environmentPath ? [] 41 | # Arbitrary commands executed after generating the configuration files 42 | , postInstall ? "" 43 | }: 44 | 45 | let 46 | util = import ../util { 47 | inherit lib; 48 | }; 49 | 50 | _environment = util.appendPathToEnvironment { 51 | inherit environment; 52 | path = environmentPath; 53 | }; 54 | 55 | cygrunsrvConfig = writeTextFile { 56 | name = "${prefix}${name}-cygrunsrv-params"; 57 | text = '' 58 | --path 59 | ${path} 60 | --disp 61 | ${displayName} 62 | '' 63 | + lib.optionalString (type != "auto") '' 64 | --type 65 | ${type} 66 | '' 67 | + lib.optionalString (args != []) '' 68 | --args 69 | ${builtins.concatStringsSep " " (map (arg: lib.escapeShellArg arg) args)} 70 | '' 71 | + 72 | lib.concatMapStrings (variableName: 73 | let 74 | value = builtins.getAttr variableName _environment; 75 | in 76 | '' 77 | --env 78 | '${variableName}=${lib.escape [ "'" ] (toString value)}' 79 | '') (builtins.attrNames _environment) 80 | + lib.optionalString (user != null) '' 81 | --user 82 | ${user} 83 | '' 84 | + lib.optionalString (password != null) '' 85 | --passwd 86 | ${password} 87 | '' 88 | + lib.optionalString (stdin != null) '' 89 | --stdin 90 | ${stdin} 91 | '' 92 | + lib.optionalString (stdout != null) '' 93 | --stdout 94 | ${stdout} 95 | '' 96 | + lib.optionalString (stderr != null) '' 97 | --stderr 98 | ${stderr} 99 | '' 100 | + lib.optionalString (terminateSignal != "TERM") '' 101 | --termsig 102 | ${terminateSignal} 103 | '' 104 | + lib.optionalString terminateOnShutdown '' 105 | --shutdown 106 | '' 107 | + lib.concatMapStrings (dependency: '' 108 | --dep 109 | ${dependency.name} 110 | '') dependencies; 111 | }; 112 | in 113 | stdenv.mkDerivation { 114 | name = "${prefix}${name}"; 115 | 116 | buildCommand = '' 117 | mkdir -p $out 118 | ln -s ${cygrunsrvConfig} $out/${prefix}${name}-cygrunsrvparams 119 | ${postInstall} 120 | ''; 121 | } 122 | -------------------------------------------------------------------------------- /nixproc/test-driver/agnostic.nix: -------------------------------------------------------------------------------- 1 | { nixpkgs ? 2 | , system ? builtins.currentSystem 3 | , processManagerModules ? {} 4 | , profileSettingModules ? {} 5 | }: 6 | 7 | let 8 | pkgs = import nixpkgs { inherit system; }; 9 | 10 | tools = import ../../tools { 11 | inherit pkgs system; 12 | }; 13 | 14 | testSystemVariantForProcessManager = {name, processManager, profileSettings, exprFile, extraParams ? {}, nixosConfig ? null, systemPackages ? [], initialTests ? null, readiness ? null, tests ? null, postTests ? null}: 15 | let 16 | processManagerModule = builtins.getAttr processManager processManagerModules; 17 | 18 | processManagerSettings = import processManagerModule { 19 | inherit profileSettings exprFile extraParams pkgs system tools; 20 | }; 21 | 22 | processesFun = import exprFile; 23 | processesFormalArgs = builtins.functionArgs processesFun; 24 | 25 | processesArgs = builtins.intersectAttrs processesFormalArgs ({ 26 | inherit pkgs system processManager; 27 | } // processManagerSettings.params // extraParams); 28 | 29 | processes = processesFun processesArgs; 30 | in 31 | with import "${nixpkgs}/nixos/lib/testing-python.nix" { inherit system; }; 32 | 33 | makeTest { 34 | inherit name; 35 | 36 | nodes.machine = 37 | {pkgs, lib, ...}: 38 | 39 | { 40 | imports = 41 | profileSettings.nixosModules 42 | ++ processManagerSettings.nixosModules 43 | ++ lib.optional (nixosConfig != null) nixosConfig; 44 | 45 | virtualisation.additionalPaths = processManagerSettings.additionalPaths; 46 | 47 | nix.extraOptions = '' 48 | substitute = false 49 | ''; 50 | 51 | environment.systemPackages = [ 52 | pkgs.dysnomia 53 | tools.common 54 | ] 55 | ++ processManagerSettings.systemPackages 56 | ++ systemPackages; 57 | }; 58 | 59 | testScript = 60 | '' 61 | start_all() 62 | '' 63 | + processManagerSettings.deployProcessManager 64 | + processManagerSettings.deploySystem 65 | + pkgs.lib.optionalString (initialTests != null) (initialTests (processManagerSettings.params // { inherit processes; })) 66 | 67 | # Execute readiness check for all process instances 68 | + pkgs.lib.optionalString (readiness != null) 69 | (pkgs.lib.concatMapStrings (instanceName: 70 | let 71 | instance = builtins.getAttr instanceName processes; 72 | in 73 | readiness ({ inherit instanceName instance; } // processManagerSettings.params) 74 | ) (builtins.attrNames processes)) 75 | 76 | # Execute tests for all process instances 77 | + pkgs.lib.optionalString (tests != null) 78 | (pkgs.lib.concatMapStrings (instanceName: 79 | let 80 | instance = builtins.getAttr instanceName processes; 81 | in 82 | tests ({ inherit instanceName instance processManager; } // processManagerSettings.params) 83 | ) (builtins.attrNames processes)) 84 | 85 | + pkgs.lib.optionalString (postTests != null) (postTests (processManagerSettings.params // { inherit processes; })); 86 | }; 87 | in 88 | { name 89 | , processManagers 90 | , profiles 91 | , exprFile 92 | , extraParams ? {} 93 | , nixosConfig ? null 94 | , systemPackages ? [] 95 | , initialTests ? null 96 | , readiness ? null 97 | , tests ? null 98 | , postTests ? null 99 | }: 100 | 101 | pkgs.lib.genAttrs profiles (profile: 102 | let 103 | profileSettingsModule = builtins.getAttr profile profileSettingModules; 104 | profileSettings = import profileSettingsModule; 105 | in 106 | pkgs.lib.genAttrs processManagers (processManager: 107 | testSystemVariantForProcessManager { 108 | inherit name processManager profileSettings exprFile extraParams nixosConfig systemPackages initialTests readiness tests postTests; 109 | } 110 | ) 111 | ) 112 | --------------------------------------------------------------------------------