at 17.09-beta 3.3 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 makeScript = name: service: pkgs.writeScript "${name}-runner" 8 '' 9 #! ${pkgs.perl}/bin/perl -w -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl 10 11 use File::Slurp; 12 13 sub run { 14 my ($cmd) = @_; 15 my @args = split " ", $cmd; 16 my $prog; 17 if (substr($args[0], 0, 1) eq "@") { 18 $prog = substr($args[0], 1); 19 shift @args; 20 } else { 21 $prog = $args[0]; 22 } 23 my $pid = fork; 24 if ($pid == 0) { 25 setpgrp; # don't receive SIGINT etc. from terminal 26 exec { $prog } @args; 27 die "failed to exec $prog\n"; 28 } elsif (!defined $pid) { 29 die "failed to fork: $!\n"; 30 } 31 return $pid; 32 }; 33 34 sub run_wait { 35 my ($cmd) = @_; 36 my $pid = run $cmd; 37 die if waitpid($pid, 0) != $pid; 38 return $?; 39 }; 40 41 # Set the environment. FIXME: escaping. 42 foreach my $key (keys %ENV) { 43 next if $key eq 'LOCALE_ARCHIVE'; 44 delete $ENV{$key}; 45 } 46 ${concatStrings (mapAttrsToList (n: v: '' 47 $ENV{'${n}'} = '${v}'; 48 '') service.environment)} 49 50 # Run the ExecStartPre program. FIXME: this could be a list. 51 my $preStart = '${service.serviceConfig.ExecStartPre or ""}'; 52 if ($preStart ne "") { 53 print STDERR "running ExecStartPre: $preStart\n"; 54 my $res = run_wait $preStart; 55 die "$0: ExecStartPre failed with status $res\n" if $res; 56 }; 57 58 # Run the ExecStart program. 59 my $cmd = '${service.serviceConfig.ExecStart}'; 60 print STDERR "running ExecStart: $cmd\n"; 61 my $mainPid = run $cmd; 62 $ENV{'MAINPID'} = $mainPid; 63 64 # Catch SIGINT, propagate to the main program. 65 sub intHandler { 66 print STDERR "got SIGINT, stopping service...\n"; 67 kill 'INT', $mainPid; 68 }; 69 $SIG{'INT'} = \&intHandler; 70 $SIG{'QUIT'} = \&intHandler; 71 72 # Run the ExecStartPost program. 73 my $postStart = '${service.serviceConfig.ExecStartPost or ""}'; 74 if ($postStart ne "") { 75 print STDERR "running ExecStartPost: $postStart\n"; 76 my $res = run_wait $postStart; 77 die "$0: ExecStartPost failed with status $res\n" if $res; 78 } 79 80 # Wait for the main program to exit. 81 die if waitpid($mainPid, 0) != $mainPid; 82 my $mainRes = $?; 83 84 # Run the ExecStopPost program. 85 my $postStop = '${service.serviceConfig.ExecStopPost or ""}'; 86 if ($postStop ne "") { 87 print STDERR "running ExecStopPost: $postStop\n"; 88 my $res = run_wait $postStop; 89 die "$0: ExecStopPost failed with status $res\n" if $res; 90 } 91 92 exit($mainRes & 127 ? 255 : $mainRes << 8); 93 ''; 94 95in 96 97{ 98 options = { 99 systemd.services = mkOption { 100 options = 101 { config, name, ... }: 102 { options.runner = mkOption { 103 internal = true; 104 description = '' 105 A script that runs the service outside of systemd, 106 useful for testing or for using NixOS services outside 107 of NixOS. 108 ''; 109 }; 110 config.runner = makeScript name config; 111 }; 112 }; 113 }; 114}