{config, pkgs, ...}: with pkgs.lib; let upstart = pkgs.upstart06; # Path for Upstart jobs. Should be quite minimal. upstartPath = [ pkgs.coreutils pkgs.findutils pkgs.gnugrep pkgs.gnused upstart ]; # From a job description, generate an Upstart job file. makeJob = job: let jobText = '' # Upstart job `${job.name}'. This is a generated file. Do not edit. description "${job.description}" console output ${if isList job.startOn then # This is a hack to support or-dependencies on Upstart 0.3. concatMapStrings (x: "start on ${x}\n") job.startOn else if job.startOn != "" then "start on ${job.startOn}" else "" } ${optionalString (job.stopOn != "") "stop on ${job.stopOn}"} env PATH=${makeSearchPath "bin" upstartPath}:${makeSearchPath "sbin" upstartPath} ${concatMapStrings (n: "env ${n}=${getAttr n job.environment}\n") (attrNames job.environment)} ${optionalString (job.preStart != "") '' pre-start script ${job.preStart} end script ''} ${if job.script != "" && job.exec != "" then abort "Job ${job.name} has both a `script' and `exec' attribute." else if job.script != "" then '' script ${job.script} end script '' else if job.exec != "" then '' exec ${job.exec} '' else "" } ${optionalString (job.postStart != "") '' post-start script ${job.postStart} end script ''} ${optionalString job.task "task"} ${optionalString job.respawn "respawn"} ${optionalString (job.postStop != "") '' post-stop script ${job.postStop} end script ''} ${optionalString (!job.task) ( if job.daemonType == "fork" then "expect fork" else if job.daemonType == "daemon" then "expect daemon" else if job.daemonType == "stop" then "expect stop" else if job.daemonType == "none" then "" else throw "invalid daemon type `${job.daemonType}'" )} ${job.extraConfig} ''; in pkgs.runCommand ("upstart-" + job.name + ".conf") { inherit (job) buildHook; inherit jobText; } '' eval "$buildHook" echo "$jobText" > $out ''; jobOptions = { name = mkOption { # !!! The type should ensure that this could be a filename. type = types.string; example = "sshd"; description = '' Name of the Upstart job. ''; }; buildHook = mkOption { type = types.string; default = "true"; description = '' Command run while building the Upstart job. Can be used to perform simple regression tests (e.g., the Apache Upstart job uses it to check the syntax of the generated httpd.conf. ''; }; description = mkOption { type = types.string; default = "(no description given)"; description = '' A short description of this job. ''; }; startOn = mkOption { # !!! Re-enable this once we're on Upstart >= 0.6. #type = types.string; default = ""; description = '' The Upstart event that triggers this job to be started. If empty, the job will not start automatically. ''; }; stopOn = mkOption { type = types.string; default = "shutdown"; description = '' The Upstart event that triggers this job to be stopped. ''; }; preStart = mkOption { type = types.string; default = ""; description = '' Shell commands executed before the job is started (i.e. before the job's main process is started). ''; }; postStart = mkOption { type = types.string; 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”. ''; }; postStop = mkOption { type = types.string; default = ""; description = '' Shell commands executed after the job has stopped (i.e. after the job's main process has terminated). ''; }; exec = mkOption { type = types.string; 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. ''; }; script = mkOption { type = types.string; default = ""; description = '' Shell commands executed as the job's main process. Can be specified instead of the exec attribute. ''; }; 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. ''; }; environment = mkOption { type = types.attrs; default = {}; example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; }; description = '' Environment variables passed to the job's processes. ''; }; daemonType = mkOption { type = types.string; default = "none"; description = '' Determines how Upstart detects when a daemon should be considered “running”. The value none means that the daemon is considered ready immediately. The value fork means that the daemon will fork once. The value daemon means that the daemon will fork twice. The value stop means that the daemon will raise the SIGSTOP signal to indicate readiness. ''; }; extraConfig = mkOption { type = types.string; default = ""; example = "limit nofile 4096 4096"; description = '' Additional Upstart stanzas not otherwise supported. ''; }; }; upstartJob = {name, config, ...}: { options = { jobDrv = mkOption { default = makeJob config; type = types.uniq types.package; description = '' Derivation that builds the Upstart job file. The default value is generated from other options. ''; }; }; config = { # The default name is the name extracted from the attribute path. name = mkDefaultValue ( replaceChars ["<" ">" "*"] ["_" "_" "_name_"] name ); }; }; in { ###### interface options = { jobs = mkOption { default = {}; description = '' This option defines the system jobs started and managed by the Upstart daemon. ''; type = types.attrsOf types.optionSet; options = [ jobOptions upstartJob ]; }; tests.upstartJobs = mkOption { internal = true; default = {}; description = '' Make it easier to build individual Upstart jobs. (e.g., nix-build /etc/nixos/nixos -A tests.upstartJobs.xserver). ''; }; }; ###### implementation config = { system.build.upstart = upstart; environment.etc = flip map (attrValues config.jobs) (job: { source = job.jobDrv; target = "init/${job.name}.conf"; } ); # !!! fix this /* tests.upstartJobs = { recurseForDerivations = true; } // builtins.listToAttrs (map (job: { name = removePrefix "upstart-" job.name; value = job; }) jobs); */ }; }