nixpkgs/nixos/modules/system/upstart/upstart.nix
2014-04-16 01:04:52 +02:00

290 lines
8.4 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{ config, lib, pkgs, ... }:
with lib;
with import ../boot/systemd-unit-options.nix { inherit config lib; };
let
userExists = u:
(u == "") || any (uu: uu.name == u) (attrValues config.users.extraUsers);
groupExists = g:
(g == "") || any (gg: gg.name == g) (attrValues config.users.extraGroups);
makeJobScript = name: content: "${pkgs.writeScriptBin name content}/bin/${name}";
# From a job description, generate an systemd unit file.
makeUnit = job:
let
hasMain = job.script != "" || job.exec != "";
env = job.environment;
preStartScript = makeJobScript "${job.name}-pre-start"
''
#! ${pkgs.stdenv.shell} -e
${job.preStart}
'';
startScript = makeJobScript "${job.name}-start"
''
#! ${pkgs.stdenv.shell} -e
${if job.script != "" then job.script else ''
exec ${job.exec}
''}
'';
postStartScript = makeJobScript "${job.name}-post-start"
''
#! ${pkgs.stdenv.shell} -e
${job.postStart}
'';
preStopScript = makeJobScript "${job.name}-pre-stop"
''
#! ${pkgs.stdenv.shell} -e
${job.preStop}
'';
postStopScript = makeJobScript "${job.name}-post-stop"
''
#! ${pkgs.stdenv.shell} -e
${job.postStop}
'';
in {
inherit (job) description requires before partOf environment path restartIfChanged unitConfig;
after =
(if job.startOn == "stopped udevtrigger" then [ "systemd-udev-settle.service" ] else
if job.startOn == "started udev" then [ "systemd-udev.service" ] else
if job.startOn == "started network-interfaces" then [ "network-interfaces.target" ] else
if job.startOn == "started networking" then [ "network.target" ] else
if job.startOn == "ip-up" then [] else
if job.startOn == "" || job.startOn == "startup" then [] else
builtins.trace "Warning: job ${job.name} has unknown startOn value ${job.startOn}." []
) ++ job.after;
wants =
(if job.startOn == "stopped udevtrigger" then [ "systemd-udev-settle.service" ] else []
) ++ job.wants;
wantedBy =
(if job.startOn == "" then [] else
if job.startOn == "ip-up" then [ "ip-up.target" ] else
[ "multi-user.target" ]) ++ job.wantedBy;
serviceConfig =
job.serviceConfig
// optionalAttrs (job.preStart != "" && (job.script != "" || job.exec != ""))
{ ExecStartPre = preStartScript; }
// optionalAttrs (job.preStart != "" && job.script == "" && job.exec == "")
{ ExecStart = preStartScript; }
// optionalAttrs (job.script != "" || job.exec != "")
{ ExecStart = startScript; }
// optionalAttrs (job.postStart != "")
{ ExecStartPost = postStartScript; }
// optionalAttrs (job.preStop != "")
{ ExecStop = preStopScript; }
// optionalAttrs (job.postStop != "")
{ ExecStopPost = postStopScript; }
// (if job.script == "" && job.exec == "" then { Type = "oneshot"; RemainAfterExit = true; } else
if job.daemonType == "fork" || job.daemonType == "daemon" then { Type = "forking"; GuessMainPID = true; } else
if job.daemonType == "none" then { } else
throw "invalid daemon type `${job.daemonType}'")
// optionalAttrs (!job.task && !(job.script == "" && job.exec == "") && job.respawn)
{ Restart = "always"; }
// optionalAttrs job.task
{ Type = "oneshot"; RemainAfterExit = false; };
};
jobOptions = serviceOptions // {
name = mkOption {
# !!! The type should ensure that this could be a filename.
type = types.str;
example = "sshd";
description = ''
Name of the job, mapped to the systemd unit
<literal><replaceable>name</replaceable>.service</literal>.
'';
};
startOn = mkOption {
#type = types.str;
default = "";
description = ''
The Upstart event that triggers this job to be started. Some
are mapped to systemd dependencies; otherwise you will get a
warning. If empty, the job will not start automatically.
'';
};
stopOn = mkOption {
type = types.str;
default = "starting shutdown";
description = ''
Ignored; this was the Upstart event that triggers this job to be stopped.
'';
};
postStart = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed after the job is started (i.e. after
the job's main process is started), but before the job is
considered running.
'';
};
preStop = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed before the job is stopped
(i.e. before systemd kills the job's main process). This can
be used to cleanly shut down a daemon.
'';
};
postStop = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed after the job has stopped
(i.e. after the job's main process has terminated).
'';
};
exec = mkOption {
type = types.str;
default = "";
description = ''
Command to start the job's main process. If empty, the
job has no main process, but can still have pre/post-start
and pre/post-stop scripts, and is considered running
until it is stopped.
'';
};
respawn = mkOption {
type = types.bool;
default = true;
description = ''
Whether to restart the job automatically if its process
ends unexpectedly.
'';
};
task = mkOption {
type = types.bool;
default = false;
description = ''
Whether this job is a task rather than a service. Tasks
are executed only once, while services are restarted when
they exit.
'';
};
daemonType = mkOption {
type = types.str;
default = "none";
description = ''
Determines how systemd detects when a daemon should be
considered running. The value <literal>none</literal> means
that the daemon is considered ready immediately. The value
<literal>fork</literal> means that the daemon will fork once.
The value <literal>daemon</literal> means that the daemon will
fork twice. The value <literal>stop</literal> means that the
daemon will raise the SIGSTOP signal to indicate readiness.
'';
};
setuid = mkOption {
type = types.addCheck types.str userExists;
default = "";
description = ''
Run the daemon as a different user.
'';
};
setgid = mkOption {
type = types.addCheck types.str groupExists;
default = "";
description = ''
Run the daemon as a different group.
'';
};
path = mkOption {
default = [];
description = ''
Packages added to the job's <envar>PATH</envar> environment variable.
Both the <filename>bin</filename> and <filename>sbin</filename>
subdirectories of each package are added.
'';
};
};
upstartJob = { name, config, ... }: {
options = {
unit = mkOption {
default = makeUnit config;
description = "Generated definition of the systemd unit corresponding to this job.";
};
};
config = {
# The default name is the name extracted from the attribute path.
name = mkDefault name;
};
};
in
{
###### interface
options = {
jobs = mkOption {
default = {};
description = ''
This option is a legacy method to define system services,
dating from the era where NixOS used Upstart instead of
systemd. You should use <option>systemd.services</option>
instead. Services defined using <option>jobs</option> are
mapped automatically to <option>systemd.services</option>, but
may not work perfectly; in particular, most
<option>startOn</option> conditions are not supported.
'';
type = types.loaOf types.optionSet;
options = [ jobOptions upstartJob ];
};
};
###### implementation
config = {
systemd.services =
flip mapAttrs' config.jobs (name: job:
nameValuePair job.name job.unit);
};
}