248 lines
9.7 KiB
Nix
248 lines
9.7 KiB
Nix
{ stdenv
|
|
, writeTextFile
|
|
, daemon
|
|
, initFunctions
|
|
, createCredentials
|
|
|
|
# Instructions that are carried out before anything else gets executed
|
|
, initialInstructions ? ". ${initFunctions}"
|
|
# Command that specifies how to start a daemon
|
|
, startDaemon ? "start_daemon"
|
|
# Path to the executable that daemonizes a foreground process
|
|
, startProcessAsDaemon ? "${daemon}/bin/daemon"
|
|
# Command that specifies how to stop a daemon
|
|
, stopDaemon ? "killproc"
|
|
# Command that specifies how to reload a daemon
|
|
, reloadDaemon ? "killproc"
|
|
# Command that evaluates the return value of the previous command
|
|
, evaluateCommand ? "evaluate_retval"
|
|
# Command that shows the status of a daemon
|
|
, statusCommand ? "statusproc"
|
|
# Default run time directory where PID files are stored
|
|
, runtimeDir ? "/var/run"
|
|
# Directory in which temp files are stored
|
|
, tmpDir ? "/tmp"
|
|
# Specifies the implementation of the restart activity that is used for all sysvinit scripts. Typically, there is little reason to change it.
|
|
, restartActivity ? ''
|
|
$0 stop
|
|
sleep 1
|
|
$0 start
|
|
''
|
|
# Specifies which runlevels are supported
|
|
, supportedRunlevels ? stdenv.lib.range 0 6
|
|
# The minimum start/stop sequence number
|
|
, minSequence ? 0
|
|
# The maximum start/stop sequence number
|
|
, maxSequence ? 99
|
|
# Specifies whether user changing functionality should be disabled or not
|
|
, forceDisableUserChange ? false
|
|
}:
|
|
|
|
{
|
|
# A name that identifies the process instance
|
|
name
|
|
# A name that uniquely identifies each process instance. It is used to generate a unique PID file.
|
|
, instanceName ? null
|
|
# A description that is added to the comments section
|
|
, description ? name
|
|
# Global instructions that are executed before any activity gets executed
|
|
, globalInstructions ? ""
|
|
# If not null, the umask will be changed before executing any activities
|
|
, umask ? null
|
|
# If not null, the nice level be changed before executing any activities
|
|
, nice ? null
|
|
# If not null, the current working directory will be changed before executing any activities
|
|
, directory ? null
|
|
# Specifies which packages need to be in the PATH
|
|
, path ? []
|
|
# An attribute set specifying arbitrary environment variables
|
|
, environment ? {}
|
|
# Shell instructions that specify how the state of the process should be initialized
|
|
, initialize ? ""
|
|
# A high level property to specify what process needs to be managed. From this property, the start, stop, reload, status and restart activities are derived.
|
|
, process ? null
|
|
# Specifies that the process is a daemon. If a process is not a daemon, then the generator will automatically daemonize it.
|
|
, processIsDaemon ? true
|
|
# Command-line arguments passed to the process.
|
|
, args ? []
|
|
# Path to a PID file that the system should use to manage the process. If null, it will use a default path.
|
|
, pidFile ? (if instanceName == null then null else if user == null || user == "root" || forceDisableUserChange then "${runtimeDir}/${instanceName}.pid" else "${tmpDir}/${instanceName}.pid")
|
|
# Specifies as which user the process should run. If null, the user privileges will not be changed.
|
|
, user ? null
|
|
# Specifies which signal should be send to the process to reload its configuration
|
|
, reloadSignal ? "-HUP"
|
|
# Specifies arbitrary instructions to carry out. Before each instruction the activity will be printed, and after the execution it will evaluate the return status
|
|
, instructions ? {}
|
|
# Specifies the raw implementation of each activity.
|
|
, activities ? {}
|
|
# Specifies activities to remove from the generated activities attribute set
|
|
, removeActivities ? []
|
|
# Specifies in which runlevels the script should be started. From this value, the script will automatically be stopped in the remaining runlevels
|
|
, runlevels ? []
|
|
# Specifies in which runlevels the script should be started.
|
|
, defaultStart ? []
|
|
# Specifies in which runlevels the script should be stopped.
|
|
, defaultStop ? []
|
|
# A list of sysvinit scripts that this scripts depends on. This is used to automatically derive the start and stop sequence. Dependencies will be started first and stopped last.
|
|
, dependencies ? []
|
|
# Specifies which groups and users that need to be created.
|
|
, credentials ? {}
|
|
}:
|
|
|
|
let
|
|
# Enumerates the activities in a logical order -- the common activities first, then the remaining activities in alphabetical order
|
|
enumerateActivities = activities:
|
|
stdenv.lib.optional (activities ? start) "start"
|
|
++ stdenv.lib.optional (activities ? stop) "stop"
|
|
++ stdenv.lib.optional (activities ? reload) "reload"
|
|
++ stdenv.lib.optional (activities ? restart) "restart"
|
|
++ stdenv.lib.optional (activities ? status) "status"
|
|
++ builtins.filter (activityName: activityName != "start" && activityName != "stop" && activityName != "reload" && activityName != "restart" && activityName != "status" && activityName != "*") (builtins.attrNames activities)
|
|
++ stdenv.lib.optional (activities ? "*") "*";
|
|
|
|
_user = if forceDisableUserChange then null else user;
|
|
|
|
_instructions = (stdenv.lib.optionalAttrs (process != null) {
|
|
start = {
|
|
activity = "Starting";
|
|
instruction =
|
|
initialize +
|
|
(if processIsDaemon then "${startDaemon} ${stdenv.lib.optionalString (pidFile != null) "-f -p ${pidFile}"} ${stdenv.lib.optionalString (nice != null) "-n ${nice}"} ${stdenv.lib.optionalString (_user != null) "su ${_user} -c '"} ${process} ${toString args} ${stdenv.lib.optionalString (_user != null) "'"}"
|
|
else "${startProcessAsDaemon} -U -i ${if pidFile == null then "-P ${runtimeDir} -n $(basename ${process})" else "-F ${pidFile}"} ${stdenv.lib.optionalString (_user != null) "-u ${_user}"} -- ${process} ${toString args}");
|
|
};
|
|
stop = {
|
|
activity = "Stopping";
|
|
instruction = "${stopDaemon} ${stdenv.lib.optionalString (pidFile != null) "-p ${pidFile}"} ${process}";
|
|
};
|
|
reload = {
|
|
activity = "Reloading";
|
|
instruction = "${reloadDaemon} ${stdenv.lib.optionalString (pidFile != null) "-p ${pidFile}"} ${process} ${reloadSignal}";
|
|
};
|
|
}) // instructions;
|
|
|
|
_activities =
|
|
let
|
|
convertedInstructions = stdenv.lib.mapAttrs (name: instruction:
|
|
''
|
|
log_info_msg "${instruction.activity} ${description}..."
|
|
${instruction.instruction}
|
|
${evaluateCommand}
|
|
''
|
|
) _instructions;
|
|
|
|
defaultActivities = stdenv.lib.optionalAttrs (process != null) {
|
|
status = "${statusCommand} ${stdenv.lib.optionalString (pidFile != null) "-p ${pidFile}"} ${process}";
|
|
restart = restartActivity;
|
|
} // {
|
|
"*" = ''
|
|
echo "Usage: $0 {${builtins.concatStringsSep "|" (builtins.filter (activityName: activityName != "*") (enumerateActivities _activities))}}"
|
|
exit 1
|
|
'';
|
|
};
|
|
in
|
|
removeAttrs (convertedInstructions // defaultActivities // activities) removeActivities;
|
|
|
|
_defaultStart = if runlevels != [] then stdenv.lib.intersectLists runlevels supportedRunlevels
|
|
else defaultStart;
|
|
|
|
_defaultStop = if runlevels != [] then stdenv.lib.subtractLists _defaultStart supportedRunlevels
|
|
else defaultStop;
|
|
|
|
_environment = stdenv.lib.optionalAttrs (path != []) {
|
|
PATH = "${builtins.concatStringsSep ":" (map(package: "${package}/bin" ) path)}:$PATH";
|
|
} // environment;
|
|
|
|
initdScript = writeTextFile {
|
|
inherit name;
|
|
executable = true;
|
|
text = ''
|
|
#! ${stdenv.shell}
|
|
|
|
## BEGIN INIT INFO
|
|
# Provides: ${name}
|
|
''
|
|
+ stdenv.lib.optionalString (_defaultStart != []) "# Default-Start: ${toString _defaultStart}\n"
|
|
+ stdenv.lib.optionalString (_defaultStop != []) "# Default-Stop: ${toString _defaultStop}\n"
|
|
+ stdenv.lib.optionalString (dependencies != []) ''
|
|
# Should-Start: ${toString (map (dependency: dependency.name) dependencies)}
|
|
# Should-Stop: ${toString (map (dependency: dependency.name) dependencies)}
|
|
''
|
|
+ ''
|
|
# Description: ${description}
|
|
## END INIT INFO
|
|
|
|
${initialInstructions}
|
|
${globalInstructions}
|
|
''
|
|
+ stdenv.lib.optionalString (umask != null) ''
|
|
umask ${umask}
|
|
''
|
|
+ stdenv.lib.optionalString (directory != null) ''
|
|
cd ${directory}
|
|
''
|
|
+ stdenv.lib.concatMapStrings (name:
|
|
let
|
|
value = builtins.getAttr name _environment;
|
|
in
|
|
''
|
|
export ${name}=${stdenv.lib.escapeShellArg value}
|
|
''
|
|
) (builtins.attrNames _environment)
|
|
+ ''
|
|
|
|
case "$1" in
|
|
${stdenv.lib.concatMapStrings (activityName:
|
|
let
|
|
instructions = builtins.getAttr activityName _activities;
|
|
in
|
|
''
|
|
${activityName})
|
|
${instructions}
|
|
;;
|
|
|
|
''
|
|
) (enumerateActivities _activities)}
|
|
esac
|
|
'';
|
|
};
|
|
|
|
startSequenceNumber =
|
|
if dependencies == [] then minSequence
|
|
else builtins.head (builtins.sort (a: b: a > b) (map (dependency: dependency.sequence) dependencies)) + 1;
|
|
|
|
stopSequenceNumber = maxSequence - startSequenceNumber + minSequence;
|
|
|
|
sequenceNumberToString = number:
|
|
if number < 10 then "0${toString number}"
|
|
else toString number;
|
|
|
|
credentialsSpec = if credentials == {} || forceDisableUserChange then null else createCredentials credentials;
|
|
in
|
|
stdenv.mkDerivation {
|
|
inherit name;
|
|
|
|
sequence = startSequenceNumber;
|
|
|
|
buildCommand = ''
|
|
mkdir -p $out/etc/rc.d
|
|
cd $out/etc/rc.d
|
|
|
|
mkdir -p init.d
|
|
ln -s ${initdScript} init.d/${name}
|
|
|
|
${stdenv.lib.concatMapStrings (runlevel: ''
|
|
mkdir -p rc${toString runlevel}.d
|
|
ln -s ../init.d/${name} rc${toString runlevel}.d/S${sequenceNumberToString startSequenceNumber}${name}
|
|
'') _defaultStart}
|
|
|
|
${stdenv.lib.concatMapStrings (runlevel: ''
|
|
mkdir -p rc${toString runlevel}.d
|
|
ln -s ../init.d/${name} rc${toString runlevel}.d/K${sequenceNumberToString stopSequenceNumber}${name}
|
|
'') _defaultStop}
|
|
|
|
${stdenv.lib.optionalString (credentialsSpec != null) ''
|
|
ln -s ${credentialsSpec}/dysnomia-support $out/dysnomia-support
|
|
''}
|
|
'';
|
|
}
|