Add an option ‘boot.systemd.services’

This option makes it more convenient to define services because it
automates stuff like setting $PATH, having a pre-start script, and so on.
This commit is contained in:
Eelco Dolstra 2012-06-18 15:28:31 -04:00
parent 673bf12b1d
commit 352510c208
2 changed files with 256 additions and 94 deletions

View file

@ -39,6 +39,7 @@ let
);
userOptions = {
openssh.authorizedKeys = {
preserveExistingKeys = mkOption {
@ -77,6 +78,7 @@ let
};
};
};
mkAuthkeyScript =
@ -127,19 +129,6 @@ let
${userLoop}
'';
preStart = pkgs.writeScript "openssh-pre-start"
''
#! ${pkgs.stdenv.shell}
${mkAuthkeyScript}
mkdir -m 0755 -p /etc/ssh
if ! test -f ${cfg.hostKeyPath}; then
ssh-keygen -t ${hktn} -b ${toString hktb} -f ${cfg.hostKeyPath} -N ""
fi
'';
in
{
@ -317,27 +306,52 @@ in
}
];
boot.systemd.units."sshd.service".text =
''
[Unit]
Description=SSH daemon
boot.systemd.services."set-ssh-keys.service" =
{ description = "Update authorized SSH keys";
[Service]
Environment=PATH=${pkgs.coreutils}/bin:${pkgs.openssh}/bin
Environment=LD_LIBRARY_PATH=${nssModulesPath}
Environment=LOCALE_ARCHIVE=/var/run/current-system/sw/lib/locale/locale-archive
ExecStartPre=${preStart}
ExecStart=\
${pkgs.openssh}/sbin/sshd -h ${cfg.hostKeyPath} \
-f ${pkgs.writeText "sshd_config" cfg.extraConfig}
Restart=always
Type=forking
KillMode=process
PIDFile=/run/sshd.pid
'';
wantedBy = [ "multi-user.target" ];
boot.systemd.units."sshd.service".wantedBy = [ "multi-user.target" ];
script = mkAuthkeyScript;
serviceConfig =
''
Type=oneshot
RemainAfterExit=true
'';
};
boot.systemd.services."sshd.service" =
{ description = "SSH daemon";
wantedBy = [ "multi-user.target" ];
after = [ "set-ssh-keys.service" ];
path = [ pkgs.openssh ];
environment.LD_LIBRARY_PATH = nssModulesPath;
environment.LOCALE_ARCHIVE = "/var/run/current-system/sw/lib/locale/locale-archive";
preStart =
''
mkdir -m 0755 -p /etc/ssh
if ! test -f ${cfg.hostKeyPath}; then
ssh-keygen -t ${hktn} -b ${toString hktb} -f ${cfg.hostKeyPath} -N ""
fi
'';
serviceConfig =
''
ExecStart=\
${pkgs.openssh}/sbin/sshd -h ${cfg.hostKeyPath} \
-f ${pkgs.writeText "sshd_config" cfg.extraConfig}
Restart=always
Type=forking
KillMode=process
PIDFile=/run/sshd.pid
'';
};
networking.firewall.allowedTCPPorts = cfg.ports;
services.openssh.extraConfig =

View file

@ -4,6 +4,102 @@ with pkgs.lib;
let
servicesOptions = {
description = mkOption {
default = "";
types = types.uniq types.string;
description = "Description of this unit used in systemd messages and progress indicators.";
};
after = mkOption {
default = [];
types = types.listOf types.string;
description = ''
If the specified units are started at the same time as
this unit, delay this unit until they have started.
'';
};
before = mkOption {
default = [];
types = types.listOf types.string;
description = ''
If the specified units are started at the same time as
this unit, delay them until this unit has started.
'';
};
wantedBy = mkOption {
default = [];
types = types.listOf types.string;
description = "Units that want (i.e. depend on) this unit.";
};
environment = mkOption {
default = {};
type = types.attrs;
example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
description = "Environment variables passed to the services's processes.";
};
path = mkOption {
default = [];
apply = ps: "${makeSearchPath "bin" ps}:${makeSearchPath "sbin" ps}";
description = ''
Packages added to the service's <envar>PATH</envar>
environment variable. Both the <filename>bin</filename>
and <filename>sbin</filename> subdirectories of each
package are added.
'';
};
serviceConfig = mkOption {
default = "";
type = types.string;
description = ''
Contents of the <literal>[Service]</literal> section of the unit.
See <citerefentry><refentrytitle>systemd.unit</refentrytitle>
<manvolnum>5</manvolnum></citerefentry> for details.
'';
};
script = mkOption {
type = types.uniq types.string;
default = "";
description = "Shell commands executed as the service's main process.";
};
preStart = mkOption {
type = types.string;
default = "";
description = ''
Shell commands executed before the service's main process
is started.
'';
};
};
servicesConfig = { name, config, ... }: {
config = {
# Default path for systemd services. Should be quite minimal.
path =
[ pkgs.coreutils
pkgs.findutils
pkgs.gnugrep
pkgs.gnused
systemd
];
};
};
cfg = config.boot.systemd;
systemd = pkgs.systemd;
@ -111,6 +207,103 @@ let
"shutdown.target.wants"
];
rescueService =
''
[Unit]
Description=Rescue Shell
DefaultDependencies=no
Conflicts=shutdown.target
After=sysinit.target
Before=shutdown.target
[Service]
Environment=HOME=/root
WorkingDirectory=/root
ExecStartPre=-${pkgs.coreutils}/bin/echo 'Welcome to rescue mode. Use "systemctl default" or ^D to enter default mode.'
#ExecStart=-/sbin/sulogin
ExecStart=-${pkgs.bashInteractive}/bin/bash --login
ExecStopPost=-${systemd}/bin/systemctl --fail --no-block default
Type=idle
StandardInput=tty-force
StandardOutput=inherit
StandardError=inherit
KillMode=process
# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash
# terminates cleanly.
KillSignal=SIGHUP
'';
gettyService =
''
[Unit]
Description=Getty on %I
Documentation=man:agetty(8)
After=systemd-user-sessions.service plymouth-quit-wait.service
# If additional gettys are spawned during boot then we should make
# sure that this is synchronized before getty.target, even though
# getty.target didn't actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes
[Service]
Environment=TERM=linux
ExecStart=-${pkgs.utillinux}/sbin/agetty --noclear --login-program ${pkgs.shadow}/bin/login %I 38400
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
KillMode=process
IgnoreSIGPIPE=no
# Unset locale for the console getty since the console has problems
# displaying some internationalized messages.
Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION=
# Some login implementations ignore SIGTERM, so we send SIGHUP
# instead, to ensure that login terminates cleanly.
KillSignal=SIGHUP
'';
serviceToUnit = name: def:
{ inherit (def) wantedBy;
text =
''
[Unit]
${optionalString (def.description != "") ''
Description=${def.description}
''}
Before=${concatStringsSep " " def.before}
After=${concatStringsSep " " def.after}
[Service]
Environment=PATH=${def.path}
${concatMapStrings (n: "Environment=${n}=\"${getAttr n def.environment}\"\n") (attrNames def.environment)}
${optionalString (def.preStart != "") ''
ExecStartPre=${pkgs.writeScript "${name}-prestart.sh" ''
#! ${pkgs.stdenv.shell} -e
${def.preStart}
''}
''}
${optionalString (def.script != "") ''
ExecStart=${pkgs.writeScript "${name}.sh" ''
#! ${pkgs.stdenv.shell} -e
${def.script}
''}
''}
${def.serviceConfig}
'';
};
nixosUnits = mapAttrsToList makeUnit cfg.units;
units = pkgs.runCommand "units" { preferLocalBuild = true; }
@ -160,20 +353,32 @@ in
options = {
boot.systemd.units = mkOption {
description = "Definition of systemd units.";
default = {};
type = types.attrsOf types.optionSet;
options = {
text = mkOption {
types = types.uniq types.string;
description = "Text of this systemd unit.";
};
wantedBy = mkOption {
default = [];
types = types.listOf types.string;
description = "Units that want (i.e. depend on) this unit.";
};
};
description = "Definition of systemd units.";
};
boot.systemd.services = mkOption {
description = "Definition of systemd services.";
default = {};
type = types.attrsOf types.optionSet;
options = [ servicesOptions servicesConfig ];
};
boot.systemd.defaultUnit = mkOption {
@ -201,68 +406,11 @@ in
}
];
boot.systemd.units."getty@.service".text =
''
[Unit]
Description=Getty on %I
Documentation=man:agetty(8)
After=systemd-user-sessions.service plymouth-quit-wait.service
# If additional gettys are spawned during boot then we should make
# sure that this is synchronized before getty.target, even though
# getty.target didn't actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes
[Service]
Environment=TERM=linux
ExecStart=-${pkgs.utillinux}/sbin/agetty --noclear --login-program ${pkgs.shadow}/bin/login %I 38400
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
KillMode=process
IgnoreSIGPIPE=no
# Unset locale for the console getty since the console has problems
# displaying some internationalized messages.
Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION=
# Some login implementations ignore SIGTERM, so we send SIGHUP
# instead, to ensure that login terminates cleanly.
KillSignal=SIGHUP
'';
boot.systemd.units."rescue.service".text =
''
[Unit]
Description=Rescue Shell
DefaultDependencies=no
Conflicts=shutdown.target
After=sysinit.target
Before=shutdown.target
[Service]
Environment=HOME=/root
WorkingDirectory=/root
ExecStartPre=-${pkgs.coreutils}/bin/echo 'Welcome to rescue mode. Use "systemctl default" or ^D to enter default mode.'
#ExecStart=-/sbin/sulogin
ExecStart=-${pkgs.bashInteractive}/bin/bash --login
ExecStopPost=-${systemd}/bin/systemctl --fail --no-block default
Type=idle
StandardInput=tty-force
StandardOutput=inherit
StandardError=inherit
KillMode=process
# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash
# terminates cleanly.
KillSignal=SIGHUP
'';
boot.systemd.units =
{ "rescue.service".text = rescueService;
"getty@.service".text = gettyService;
}
// mapAttrs serviceToUnit cfg.services;
};