diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 09625511bba5..caba5ef18c3c 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -274,6 +274,7 @@ ./tasks/network-interfaces.nix ./tasks/scsi-link-power-management.nix ./tasks/swraid.nix + ./testing/service-runner.nix ./virtualisation/libvirtd.nix #./virtualisation/nova.nix ./virtualisation/virtualbox-guest.nix diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index 73447e3cf0d8..a1ab1c92b8f9 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -181,8 +181,13 @@ in # Initialise the database. if ! test -e ${cfg.dataDir}; then mkdir -m 0700 -p ${cfg.dataDir} - chown -R postgres ${cfg.dataDir} - su -s ${pkgs.stdenv.shell} postgres -c 'initdb -U root' + if [ "$(id -u)" = 0 ]; then + chown -R postgres ${cfg.dataDir} + su -s ${pkgs.stdenv.shell} postgres -c 'initdb -U root' + else + # For non-root operation. + initdb + fi rm -f ${cfg.dataDir}/*.conf touch "${cfg.dataDir}/.first_startup" fi diff --git a/nixos/modules/testing/service-runner.nix b/nixos/modules/testing/service-runner.nix new file mode 100644 index 000000000000..6f17ed77dad9 --- /dev/null +++ b/nixos/modules/testing/service-runner.nix @@ -0,0 +1,114 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + makeScript = name: service: pkgs.writeScript "${name}-runner" + '' + #! ${pkgs.perl}/bin/perl -w -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl + + use File::Slurp; + + sub run { + my ($cmd) = @_; + my @args = split " ", $cmd; + my $prog; + if (substr($args[0], 0, 1) eq "@") { + $prog = substr($args[0], 1); + shift @args; + } else { + $prog = $args[0]; + } + my $pid = fork; + if ($pid == 0) { + setpgrp; # don't receive SIGINT etc. from terminal + exec { $prog } @args; + die "failed to exec $prog\n"; + } elsif (!defined $pid) { + die "failed to fork: $!\n"; + } + return $pid; + }; + + sub run_wait { + my ($cmd) = @_; + my $pid = run $cmd; + die if waitpid($pid, 0) != $pid; + return $?; + }; + + # Set the environment. FIXME: escaping. + foreach my $key (keys %ENV) { + next if $key eq 'LOCALE_ARCHIVE'; + delete $ENV{$key}; + } + ${concatStrings (mapAttrsToList (n: v: '' + $ENV{'${n}'} = '${v}'; + '') service.environment)} + + # Run the ExecStartPre program. FIXME: this could be a list. + my $preStart = '${service.serviceConfig.ExecStartPre or ""}'; + if ($preStart ne "") { + print STDERR "running ExecStartPre: $preStart\n"; + my $res = run_wait $preStart; + die "$0: ExecStartPre failed with status $res\n" if $res; + }; + + # Run the ExecStart program. + my $cmd = '${service.serviceConfig.ExecStart}'; + print STDERR "running ExecStart: $cmd\n"; + my $mainPid = run $cmd; + $ENV{'MAINPID'} = $mainPid; + + # Catch SIGINT, propagate to the main program. + sub intHandler { + print STDERR "got SIGINT, stopping service...\n"; + kill 'INT', $mainPid; + }; + $SIG{'INT'} = \&intHandler; + $SIG{'QUIT'} = \&intHandler; + + # Run the ExecStartPost program. + my $postStart = '${service.serviceConfig.ExecStartPost or ""}'; + if ($postStart ne "") { + print STDERR "running ExecStartPost: $postStart\n"; + my $res = run_wait $postStart; + die "$0: ExecStartPost failed with status $res\n" if $res; + } + + # Wait for the main program to exit. + die if waitpid($mainPid, 0) != $mainPid; + my $mainRes = $?; + + # Run the ExecStopPost program. + my $postStop = '${service.serviceConfig.ExecStopPost or ""}'; + if ($postStop ne "") { + print STDERR "running ExecStopPost: $postStop\n"; + my $res = run_wait $postStop; + die "$0: ExecStopPost failed with status $res\n" if $res; + } + + exit($mainRes & 127 ? 255 : $mainRes << 8); + ''; + +in + +{ + options = { + systemd.services = mkOption { + options = + { config, name, ... }: + { options.runner = mkOption { + internal = true; + description = '' + A script that runs the service outside of systemd, + useful for testing or for using NixOS services outside + of NixOS. + ''; + }; + config.runner = makeScript name config; + }; + }; + }; +}