treewide: reformat nixfmt-rfc-style

Reformat repository using new nixfmt-rfc-style.
This commit is contained in:
Austin Horstman 2025-04-07 16:11:29 -05:00
parent 5df48c4255
commit cba2f9ce95
1051 changed files with 37028 additions and 26594 deletions

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (pkgs.stdenv.hostPlatform) isDarwin;
@ -8,45 +13,48 @@ let
labelPrefix = "org.nix-community.home.";
dstDir = "${config.home.homeDirectory}/Library/LaunchAgents";
launchdConfig = { config, name, ... }: {
options = {
enable = lib.mkEnableOption name;
config = lib.mkOption {
type = lib.types.submodule (import ./launchd.nix);
default = { };
example = lib.literalExpression ''
{
ProgramArguments = [ "/usr/bin/say" "Good afternoon" ];
StartCalendarInterval = [
{
Hour = 12;
Minute = 0;
}
];
}
'';
description = ''
Define a launchd job. See {manpage}`launchd.plist(5)` for details.
'';
launchdConfig =
{ config, name, ... }:
{
options = {
enable = lib.mkEnableOption name;
config = lib.mkOption {
type = lib.types.submodule (import ./launchd.nix);
default = { };
example = lib.literalExpression ''
{
ProgramArguments = [ "/usr/bin/say" "Good afternoon" ];
StartCalendarInterval = [
{
Hour = 12;
Minute = 0;
}
];
}
'';
description = ''
Define a launchd job. See {manpage}`launchd.plist(5)` for details.
'';
};
};
config = {
config.Label = lib.mkDefault "${labelPrefix}${name}";
};
};
config = { config.Label = lib.mkDefault "${labelPrefix}${name}"; };
};
toAgent = config: pkgs.writeText "${config.Label}.plist" (toPlist { } config);
agentPlists = lib.mapAttrs'
(n: v: lib.nameValuePair "${v.config.Label}.plist" (toAgent v.config))
(lib.filterAttrs (n: v: v.enable) cfg.agents);
agentPlists = lib.mapAttrs' (n: v: lib.nameValuePair "${v.config.Label}.plist" (toAgent v.config)) (
lib.filterAttrs (n: v: v.enable) cfg.agents
);
agentsDrv = pkgs.runCommand "home-manager-agents" { } ''
mkdir -p "$out"
declare -A plists
plists=(${
lib.concatStringsSep " "
(lib.mapAttrsToList (name: value: "['${name}']='${value}'") agentPlists)
lib.concatStringsSep " " (lib.mapAttrsToList (name: value: "['${name}']='${value}'") agentPlists)
})
for dest in "''${!plists[@]}"; do
@ -54,8 +62,12 @@ let
ln -s "$src" "$out/$dest"
done
'';
in {
meta.maintainers = with lib.maintainers; [ khaneliman midchildan ];
in
{
meta.maintainers = with lib.maintainers; [
khaneliman
midchildan
];
options.launchd = {
enable = lib.mkOption {
@ -77,12 +89,16 @@ in {
config = lib.mkMerge [
{
assertions = [{
assertion = (cfg.enable && agentPlists != { }) -> isDarwin;
message =
let names = lib.concatStringsSep ", " (lib.attrNames agentPlists);
in "Must use Darwin for modules that require Launchd: " + names;
}];
assertions = [
{
assertion = (cfg.enable && agentPlists != { }) -> isDarwin;
message =
let
names = lib.concatStringsSep ", " (lib.attrNames agentPlists);
in
"Must use Darwin for modules that require Launchd: " + names;
}
];
}
(lib.mkIf isDarwin {
@ -94,170 +110,170 @@ in {
# because it needs to be owned by the user running it.
home.activation.setupLaunchAgents =
lib.hm.dag.entryAfter [ "writeBoundary" ] # Bash
''
# Disable errexit to ensure we process all agents even if some fail
set +e
''
# Disable errexit to ensure we process all agents even if some fail
set +e
# Stop an agent if it's running
bootoutAgent() {
local domain="$1"
local agentName="$2"
# Stop an agent if it's running
bootoutAgent() {
local domain="$1"
local agentName="$2"
verboseEcho "Stopping agent '$domain/$agentName'..."
local bootout_output
bootout_output=$(run /bin/launchctl bootout "$domain/$agentName" 2>&1) || {
# Only show warning if it's not the common "No such process" error
if [[ "$bootout_output" != *"No such process"* ]]; then
warnEcho "Failed to stop agent '$domain/$agentName': $bootout_output"
else
verboseEcho "Agent '$domain/$agentName' was not running"
fi
verboseEcho "Stopping agent '$domain/$agentName'..."
local bootout_output
bootout_output=$(run /bin/launchctl bootout "$domain/$agentName" 2>&1) || {
# Only show warning if it's not the common "No such process" error
if [[ "$bootout_output" != *"No such process"* ]]; then
warnEcho "Failed to stop agent '$domain/$agentName': $bootout_output"
else
verboseEcho "Agent '$domain/$agentName' was not running"
fi
}
# Give the system a moment to fully unload the agent
sleep 1
}
# Give the system a moment to fully unload the agent
sleep 1
}
installAndBootstrapAgent() {
local srcPath="$1"
local dstPath="$2"
local domain="$3"
local agentName="$4"
installAndBootstrapAgent() {
local srcPath="$1"
local dstPath="$2"
local domain="$3"
local agentName="$4"
verboseEcho "Installing agent file to $dstPath"
if ! run install -Dm444 -T "$srcPath" "$dstPath"; then
errorEcho "Failed to install agent file for '$agentName'"
return 1
fi
verboseEcho "Starting agent '$domain/$agentName'"
local bootstrap_output
bootstrap_output=$(run /bin/launchctl bootstrap "$domain" "$dstPath" 2>&1) || {
local error_code=$?
if [[ "$bootstrap_output" == *"Bootstrap failed: 5: Input/output error"* ]]; then
errorEcho "Failed to start agent '$domain/$agentName' with I/O error (code 5)"
errorEcho "This typically happens when the agent wasn't unloaded before attempting to bootstrap the new agent."
else
errorEcho "Failed to start agent '$domain/$agentName' with error: $bootstrap_output"
verboseEcho "Installing agent file to $dstPath"
if ! run install -Dm444 -T "$srcPath" "$dstPath"; then
errorEcho "Failed to install agent file for '$agentName'"
return 1
fi
return 1
}
verboseEcho "Starting agent '$domain/$agentName'"
local bootstrap_output
bootstrap_output=$(run /bin/launchctl bootstrap "$domain" "$dstPath" 2>&1) || {
local error_code=$?
verboseEcho "Successfully started agent '$domain/$agentName'"
return 0
}
if [[ "$bootstrap_output" == *"Bootstrap failed: 5: Input/output error"* ]]; then
errorEcho "Failed to start agent '$domain/$agentName' with I/O error (code 5)"
errorEcho "This typically happens when the agent wasn't unloaded before attempting to bootstrap the new agent."
else
errorEcho "Failed to start agent '$domain/$agentName' with error: $bootstrap_output"
fi
processAgent() {
local srcPath="$1"
local dstDir="$2"
local domain="$3"
return 1
}
local agentFile="''${srcPath##*/}"
local agentName="''${agentFile%.plist}"
local dstPath="$dstDir/$agentFile"
# Skip if unchanged
if cmp -s "$srcPath" "$dstPath"; then
verboseEcho "Agent '$agentName' is already up-to-date"
verboseEcho "Successfully started agent '$domain/$agentName'"
return 0
fi
}
verboseEcho "Processing agent '$agentName'"
processAgent() {
local srcPath="$1"
local dstDir="$2"
local domain="$3"
# Stop/Unload agent if it's already running
if [[ -f "$dstPath" ]]; then
local agentFile="''${srcPath##*/}"
local agentName="''${agentFile%.plist}"
local dstPath="$dstDir/$agentFile"
# Skip if unchanged
if cmp -s "$srcPath" "$dstPath"; then
verboseEcho "Agent '$agentName' is already up-to-date"
return 0
fi
verboseEcho "Processing agent '$agentName'"
# Stop/Unload agent if it's already running
if [[ -f "$dstPath" ]]; then
bootoutAgent "$domain" "$agentName"
fi
installAndBootstrapAgent "$srcPath" "$dstPath" "$domain" "$agentName"
# Note: We continue processing even if this agent fails
return 0
}
removeAgent() {
local srcPath="$1"
local dstDir="$2"
local newDir="$3"
local domain="$4"
local agentFile="''${srcPath##*/}"
local agentName="''${agentFile%.plist}"
local dstPath="$dstDir/$agentFile"
if [[ -e "$newDir/$agentFile" ]]; then
verboseEcho "Agent '$agentName' still exists in new generation, skipping cleanup"
return 0
fi
if [[ ! -e "$dstPath" ]]; then
verboseEcho "Agent file '$dstPath' already removed"
return 0
fi
if ! cmp -s "$srcPath" "$dstPath"; then
warnEcho "Skipping deletion of '$dstPath', since its contents have diverged"
return 0
fi
# Stop and remove the agent
bootoutAgent "$domain" "$agentName"
fi
installAndBootstrapAgent "$srcPath" "$dstPath" "$domain" "$agentName"
# Note: We continue processing even if this agent fails
return 0
}
verboseEcho "Removing agent file '$dstPath'"
if run rm -f $VERBOSE_ARG "$dstPath"; then
verboseEcho "Successfully removed agent file for '$agentName'"
else
warnEcho "Failed to remove agent file '$dstPath'"
fi
removeAgent() {
local srcPath="$1"
local dstDir="$2"
local newDir="$3"
local domain="$4"
local agentFile="''${srcPath##*/}"
local agentName="''${agentFile%.plist}"
local dstPath="$dstDir/$agentFile"
if [[ -e "$newDir/$agentFile" ]]; then
verboseEcho "Agent '$agentName' still exists in new generation, skipping cleanup"
return 0
fi
}
if [[ ! -e "$dstPath" ]]; then
verboseEcho "Agent file '$dstPath' already removed"
return 0
fi
setupLaunchAgents() {
local oldDir newDir dstDir domain
if ! cmp -s "$srcPath" "$dstPath"; then
warnEcho "Skipping deletion of '$dstPath', since its contents have diverged"
return 0
fi
newDir="$(readlink -m "$newGenPath/LaunchAgents")"
dstDir=${lib.escapeShellArg dstDir}
domain="gui/$UID"
# Stop and remove the agent
bootoutAgent "$domain" "$agentName"
verboseEcho "Removing agent file '$dstPath'"
if run rm -f $VERBOSE_ARG "$dstPath"; then
verboseEcho "Successfully removed agent file for '$agentName'"
else
warnEcho "Failed to remove agent file '$dstPath'"
fi
return 0
}
setupLaunchAgents() {
local oldDir newDir dstDir domain
newDir="$(readlink -m "$newGenPath/LaunchAgents")"
dstDir=${lib.escapeShellArg dstDir}
domain="gui/$UID"
if [[ -n "''${oldGenPath:-}" ]]; then
oldDir="$(readlink -m "$oldGenPath/LaunchAgents")"
if [[ ! -d "$oldDir" ]]; then
verboseEcho "No previous LaunchAgents directory found"
if [[ -n "''${oldGenPath:-}" ]]; then
oldDir="$(readlink -m "$oldGenPath/LaunchAgents")"
if [[ ! -d "$oldDir" ]]; then
verboseEcho "No previous LaunchAgents directory found"
oldDir=""
fi
else
oldDir=""
fi
else
oldDir=""
verboseEcho "Setting up LaunchAgents in $dstDir"
[[ -d "$dstDir" ]] || run mkdir -p "$dstDir"
verboseEcho "Processing new/updated LaunchAgents..."
find -L "$newDir" -maxdepth 1 -name '*.plist' -type f | while read -r srcPath; do
processAgent "$srcPath" "$dstDir" "$domain"
done
# Skip cleanup if there's no previous generation
if [[ -z "$oldDir" || ! -d "$oldDir" ]]; then
verboseEcho "LaunchAgents setup complete"
return
fi
verboseEcho "Cleaning up removed LaunchAgents..."
find -L "$oldDir" -maxdepth 1 -name '*.plist' -type f | while read -r srcPath; do
removeAgent "$srcPath" "$dstDir" "$newDir" "$domain"
done
}
setupLaunchAgents
# Restore errexit
if [[ -o errexit ]]; then
set -e
fi
verboseEcho "Setting up LaunchAgents in $dstDir"
[[ -d "$dstDir" ]] || run mkdir -p "$dstDir"
verboseEcho "Processing new/updated LaunchAgents..."
find -L "$newDir" -maxdepth 1 -name '*.plist' -type f | while read -r srcPath; do
processAgent "$srcPath" "$dstDir" "$domain"
done
# Skip cleanup if there's no previous generation
if [[ -z "$oldDir" || ! -d "$oldDir" ]]; then
verboseEcho "LaunchAgents setup complete"
return
fi
verboseEcho "Cleaning up removed LaunchAgents..."
find -L "$oldDir" -maxdepth 1 -name '*.plist' -type f | while read -r srcPath; do
removeAgent "$srcPath" "$dstDir" "$newDir" "$domain"
done
}
setupLaunchAgents
# Restore errexit
if [[ -o errexit ]]; then
set -e
fi
'';
'';
})
];
}

View file

@ -29,7 +29,8 @@ let
inherit (lib) types mkOption; # added by Home Manager
launchdTypes = import ./types.nix { inherit config lib; };
in {
in
{
freeformType = with types; attrsOf anything; # added by Home Manager
options = {
@ -82,23 +83,27 @@ in {
inetdCompatibility = mkOption {
default = null;
example = { Wait = true; };
example = {
Wait = true;
};
description = ''
The presence of this key specifies that the daemon expects to be run as if it were launched from inetd.
'';
type = types.nullOr (types.submodule {
options = {
Wait = mkOption {
type = types.nullOr (types.either types.bool types.str);
default = null;
description = ''
This flag corresponds to the "wait" or "nowait" option of inetd. If true, then the listening
socket is passed via the standard in/out/error file descriptors. If false, then `accept(2)` is
called on behalf of the job, and the result is passed via the standard in/out/error descriptors.
'';
type = types.nullOr (
types.submodule {
options = {
Wait = mkOption {
type = types.nullOr (types.either types.bool types.str);
default = null;
description = ''
This flag corresponds to the "wait" or "nowait" option of inetd. If true, then the listening
socket is passed via the standard in/out/error file descriptors. If false, then `accept(2)` is
called on behalf of the job, and the result is passed via the standard in/out/error descriptors.
'';
};
};
};
});
}
);
};
LimitLoadToHosts = mkOption {
@ -120,7 +125,12 @@ in {
};
LimitLoadToSessionType = mkOption {
type = types.nullOr (types.oneOf [ types.str (types.listOf types.str) ]);
type = types.nullOr (
types.oneOf [
types.str
(types.listOf types.str)
]
);
default = null;
description = ''
This configuration file only applies to sessions of the type specified. This key is used in concert
@ -177,68 +187,72 @@ in {
};
KeepAlive = mkOption {
type = types.nullOr (types.either types.bool (types.submodule {
options = {
type = types.nullOr (
types.either types.bool (
types.submodule {
options = {
SuccessfulExit = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the job will be restarted as long as the program exits and with an exit status of zero.
If false, the job will be restarted in the inverse condition. This key implies that "RunAtLoad"
is set to true, since the job needs to run at least once before we can get an exit status.
'';
};
SuccessfulExit = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the job will be restarted as long as the program exits and with an exit status of zero.
If false, the job will be restarted in the inverse condition. This key implies that "RunAtLoad"
is set to true, since the job needs to run at least once before we can get an exit status.
'';
};
NetworkState = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the job will be kept alive as long as the network is up, where up is defined as at least
one non-loopback interface being up and having IPv4 or IPv6 addresses assigned to them. If
false, the job will be kept alive in the inverse condition.
'';
};
NetworkState = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the job will be kept alive as long as the network is up, where up is defined as at least
one non-loopback interface being up and having IPv4 or IPv6 addresses assigned to them. If
false, the job will be kept alive in the inverse condition.
'';
};
PathState = mkOption {
type = types.nullOr (types.attrsOf types.bool);
default = null;
description = ''
Each key in this dictionary is a file-system path. If the value of the key is true, then the job
will be kept alive as long as the path exists. If false, the job will be kept alive in the
inverse condition. The intent of this feature is that two or more jobs may create semaphores in
the file-system namespace.
'';
};
PathState = mkOption {
type = types.nullOr (types.attrsOf types.bool);
default = null;
description = ''
Each key in this dictionary is a file-system path. If the value of the key is true, then the job
will be kept alive as long as the path exists. If false, the job will be kept alive in the
inverse condition. The intent of this feature is that two or more jobs may create semaphores in
the file-system namespace.
'';
};
OtherJobEnabled = mkOption {
type = types.nullOr (types.attrsOf types.bool);
default = null;
description = ''
Each key in this dictionary is the label of another job. If the value of the key is true, then
this job is kept alive as long as that other job is enabled. Otherwise, if the value is false,
then this job is kept alive as long as the other job is disabled. This feature should not be
considered a substitute for the use of IPC.
'';
};
OtherJobEnabled = mkOption {
type = types.nullOr (types.attrsOf types.bool);
default = null;
description = ''
Each key in this dictionary is the label of another job. If the value of the key is true, then
this job is kept alive as long as that other job is enabled. Otherwise, if the value is false,
then this job is kept alive as long as the other job is disabled. This feature should not be
considered a substitute for the use of IPC.
'';
};
Crashed = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the the job will be restarted as long as it exited due to a signal which is typically
associated with a crash (SIGILL, SIGSEGV, etc.). If false, the job will be restarted in the
inverse condition.
'';
};
Crashed = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the the job will be restarted as long as it exited due to a signal which is typically
associated with a crash (SIGILL, SIGSEGV, etc.). If false, the job will be restarted in the
inverse condition.
'';
};
AfterInitialDemand = mkOption {
type = types.nullOr types.bool;
default = null;
};
AfterInitialDemand = mkOption {
type = types.nullOr types.bool;
default = null;
};
};
}));
};
}
)
);
default = null;
description = ''
This optional key is used to control whether your job is to be kept continuously running or to let
@ -371,10 +385,12 @@ in {
StartCalendarInterval = mkOption {
default = null;
example = [{
Hour = 2;
Minute = 30;
}];
example = [
{
Hour = 2;
Minute = 30;
}
];
description = ''
This optional key causes the job to be started every calendar interval as specified. The semantics are
much like {manpage}`crontab(5)`: Missing attributes are considered to be wildcard. Unlike cron which skips
@ -442,181 +458,187 @@ in {
Resource limits to be imposed on the job. These adjust variables set with `setrlimit(2)`. The following
keys apply:
'';
type = types.nullOr (types.submodule {
options = {
Core = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) core file that may be created.
'';
};
type = types.nullOr (
types.submodule {
options = {
Core = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) core file that may be created.
'';
};
CPU = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum amount of cpu time (in seconds) to be used by each process.
'';
};
CPU = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum amount of cpu time (in seconds) to be used by each process.
'';
};
Data = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the data segment for a process; this defines how far a program may
extend its break with the `sbrk(2)` system call.
'';
};
Data = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the data segment for a process; this defines how far a program may
extend its break with the `sbrk(2)` system call.
'';
};
FileSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) file that may be created.
'';
};
FileSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) file that may be created.
'';
};
MemoryLock = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) which a process may lock into memory using the mlock(2) function.
'';
};
MemoryLock = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) which a process may lock into memory using the mlock(2) function.
'';
};
NumberOfFiles = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of open files for this process. Setting this value in a system wide daemon
will set the `sysctl(3)` kern.maxfiles (SoftResourceLimits) or kern.maxfilesperproc (HardResourceLimits)
value in addition to the `setrlimit(2)` values.
'';
};
NumberOfFiles = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of open files for this process. Setting this value in a system wide daemon
will set the `sysctl(3)` kern.maxfiles (SoftResourceLimits) or kern.maxfilesperproc (HardResourceLimits)
value in addition to the `setrlimit(2)` values.
'';
};
NumberOfProcesses = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of simultaneous processes for this user id. Setting this value in a system
wide daemon will set the `sysctl(3)` kern.maxproc (SoftResourceLimits) or kern.maxprocperuid
(HardResourceLimits) value in addition to the `setrlimit(2)` values.
'';
};
NumberOfProcesses = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of simultaneous processes for this user id. Setting this value in a system
wide daemon will set the `sysctl(3)` kern.maxproc (SoftResourceLimits) or kern.maxprocperuid
(HardResourceLimits) value in addition to the `setrlimit(2)` values.
'';
};
ResidentSetSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) to which a process's resident set size may grow. This imposes a
limit on the amount of physical memory to be given to a process; if memory is tight, the system
will prefer to take memory from processes that are exceeding their declared resident set size.
'';
};
ResidentSetSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) to which a process's resident set size may grow. This imposes a
limit on the amount of physical memory to be given to a process; if memory is tight, the system
will prefer to take memory from processes that are exceeding their declared resident set size.
'';
};
Stack = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the stack segment for a process; this defines how far a program's
stack segment may be extended. Stack extension is performed automatically by the system.
'';
Stack = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the stack segment for a process; this defines how far a program's
stack segment may be extended. Stack extension is performed automatically by the system.
'';
};
};
};
});
}
);
};
HardResourceLimits = mkOption {
default = null;
example = { NumberOfFiles = 4096; };
example = {
NumberOfFiles = 4096;
};
description = ''
Resource limits to be imposed on the job. These adjust variables set with `setrlimit(2)`. The following
keys apply:
'';
type = types.nullOr (types.submodule {
options = {
Core = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) core file that may be created.
'';
};
type = types.nullOr (
types.submodule {
options = {
Core = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) core file that may be created.
'';
};
CPU = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum amount of cpu time (in seconds) to be used by each process.
'';
};
CPU = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum amount of cpu time (in seconds) to be used by each process.
'';
};
Data = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the data segment for a process; this defines how far a program may
extend its break with the `sbrk(2)` system call.
'';
};
Data = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the data segment for a process; this defines how far a program may
extend its break with the `sbrk(2)` system call.
'';
};
FileSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) file that may be created.
'';
};
FileSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) file that may be created.
'';
};
MemoryLock = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) which a process may lock into memory using the `mlock(2)` function.
'';
};
MemoryLock = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) which a process may lock into memory using the `mlock(2)` function.
'';
};
NumberOfFiles = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of open files for this process. Setting this value in a system wide daemon
will set the `sysctl(3)` kern.maxfiles (SoftResourceLimits) or kern.maxfilesperproc (HardResourceLimits)
value in addition to the `setrlimit(2)` values.
'';
};
NumberOfFiles = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of open files for this process. Setting this value in a system wide daemon
will set the `sysctl(3)` kern.maxfiles (SoftResourceLimits) or kern.maxfilesperproc (HardResourceLimits)
value in addition to the `setrlimit(2)` values.
'';
};
NumberOfProcesses = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of simultaneous processes for this user id. Setting this value in a system
wide daemon will set the `sysctl(3)` kern.maxproc (SoftResourceLimits) or kern.maxprocperuid
(HardResourceLimits) value in addition to the `setrlimit(2)` values.
'';
};
NumberOfProcesses = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of simultaneous processes for this user id. Setting this value in a system
wide daemon will set the `sysctl(3)` kern.maxproc (SoftResourceLimits) or kern.maxprocperuid
(HardResourceLimits) value in addition to the `setrlimit(2)` values.
'';
};
ResidentSetSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) to which a process's resident set size may grow. This imposes a
limit on the amount of physical memory to be given to a process; if memory is tight, the system
will prefer to take memory from processes that are exceeding their declared resident set size.
'';
};
ResidentSetSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) to which a process's resident set size may grow. This imposes a
limit on the amount of physical memory to be given to a process; if memory is tight, the system
will prefer to take memory from processes that are exceeding their declared resident set size.
'';
};
Stack = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the stack segment for a process; this defines how far a program's
stack segment may be extended. Stack extension is performed automatically by the system.
'';
Stack = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the stack segment for a process; this defines how far a program's
stack segment may be extended. Stack extension is performed automatically by the system.
'';
};
};
};
});
}
);
};
Nice = mkOption {
@ -628,8 +650,14 @@ in {
};
ProcessType = mkOption {
type = types.nullOr
(types.enum [ "Background" "Standard" "Adaptive" "Interactive" ]);
type = types.nullOr (
types.enum [
"Background"
"Standard"
"Adaptive"
"Interactive"
]
);
default = null;
example = "Background";
description = ''
@ -694,7 +722,11 @@ in {
MachServices = mkOption {
default = null;
example = { "org.nixos.service" = { ResetAtClose = true; }; };
example = {
"org.nixos.service" = {
ResetAtClose = true;
};
};
description = ''
This optional key is used to specify Mach services to be registered with the Mach bootstrap sub-system.
Each key in this dictionary should be the name of service to be advertised. The value of the key must
@ -703,32 +735,37 @@ in {
Finally, for the job itself, the values will be replaced with Mach ports at the time of check-in with
launchd.
'';
type = types.nullOr (types.attrsOf (types.either types.bool
(types.submodule {
options = {
ResetAtClose = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If this boolean is false, the port is recycled, thus leaving clients to remain oblivious to the
demand nature of job. If the value is set to true, clients receive port death notifications when
the job lets go of the receive right. The port will be recreated atomically with respect to bootstrap_look_up()
calls, so that clients can trust that after receiving a port death notification,
the new port will have already been recreated. Setting the value to true should be done with
care. Not all clients may be able to handle this behavior. The default value is false.
'';
};
type = types.nullOr (
types.attrsOf (
types.either types.bool (
types.submodule {
options = {
ResetAtClose = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If this boolean is false, the port is recycled, thus leaving clients to remain oblivious to the
demand nature of job. If the value is set to true, clients receive port death notifications when
the job lets go of the receive right. The port will be recreated atomically with respect to bootstrap_look_up()
calls, so that clients can trust that after receiving a port death notification,
the new port will have already been recreated. Setting the value to true should be done with
care. Not all clients may be able to handle this behavior. The default value is false.
'';
};
HideUntilCheckIn = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Reserve the name in the namespace, but cause bootstrap_look_up() to fail until the job has
checked in with launchd.
'';
};
};
})));
HideUntilCheckIn = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Reserve the name in the namespace, but cause bootstrap_look_up() to fail until the job has
checked in with launchd.
'';
};
};
}
)
)
);
};
LaunchEvents = mkOption {
@ -790,109 +827,123 @@ in {
The parameters below are used as inputs to call `getaddrinfo(3)`.
'';
type = types.nullOr (types.attrsOf (types.submodule {
options = {
SockType = mkOption {
type = types.nullOr (types.enum [ "stream" "dgram" "seqpacket" ]);
default = null;
description = ''
This optional key tells launchctl what type of socket to create. The default is "stream" and
other valid values for this key are "dgram" and "seqpacket" respectively.
'';
};
type = types.nullOr (
types.attrsOf (
types.submodule {
options = {
SockType = mkOption {
type = types.nullOr (
types.enum [
"stream"
"dgram"
"seqpacket"
]
);
default = null;
description = ''
This optional key tells launchctl what type of socket to create. The default is "stream" and
other valid values for this key are "dgram" and "seqpacket" respectively.
'';
};
SockPassive = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
This optional key specifies whether `listen(2)` or `connect(2)` should be called on the created file
descriptor. The default is true ("to listen").
'';
};
SockPassive = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
This optional key specifies whether `listen(2)` or `connect(2)` should be called on the created file
descriptor. The default is true ("to listen").
'';
};
SockNodeName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key specifies the node to `connect(2)` or `bind(2)` to.
'';
};
SockNodeName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key specifies the node to `connect(2)` or `bind(2)` to.
'';
};
SockServiceName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key specifies the service on the node to `connect(2)` or `bind(2)` to.
'';
};
SockServiceName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key specifies the service on the node to `connect(2)` or `bind(2)` to.
'';
};
SockFamily = mkOption {
type = types.nullOr (types.enum [ "IPv4" "IPv6" ]);
default = null;
description = ''
This optional key can be used to specifically request that "IPv4" or "IPv6" socket(s) be created.
'';
};
SockFamily = mkOption {
type = types.nullOr (
types.enum [
"IPv4"
"IPv6"
]
);
default = null;
description = ''
This optional key can be used to specifically request that "IPv4" or "IPv6" socket(s) be created.
'';
};
SockProtocol = mkOption {
type = types.nullOr (types.enum [ "TCP" ]);
default = null;
description = ''
This optional key specifies the protocol to be passed to `socket(2)`. The only value understood by
this key at the moment is "TCP".
'';
};
SockProtocol = mkOption {
type = types.nullOr (types.enum [ "TCP" ]);
default = null;
description = ''
This optional key specifies the protocol to be passed to `socket(2)`. The only value understood by
this key at the moment is "TCP".
'';
};
SockPathName = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
This optional key implies SockFamily is set to "Unix". It specifies the path to `connect(2)` or
`bind(2)` to.
'';
};
SockPathName = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
This optional key implies SockFamily is set to "Unix". It specifies the path to `connect(2)` or
`bind(2)` to.
'';
};
SecureSocketWithKey = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key is a variant of SockPathName. Instead of binding to a known path, a securely
generated socket is created and the path is assigned to the environment variable that is inherited
by all jobs spawned by launchd.
'';
};
SecureSocketWithKey = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key is a variant of SockPathName. Instead of binding to a known path, a securely
generated socket is created and the path is assigned to the environment variable that is inherited
by all jobs spawned by launchd.
'';
};
SockPathMode = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
This optional key specifies the mode of the socket. Known bug: Property lists don't support
octal, so please convert the value to decimal.
'';
};
SockPathMode = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
This optional key specifies the mode of the socket. Known bug: Property lists don't support
octal, so please convert the value to decimal.
'';
};
Bonjour = mkOption {
type =
types.nullOr (types.either types.bool (types.listOf types.str));
default = null;
description = ''
This optional key can be used to request that the service be registered with the
`mDNSResponder(8)`. If the value is boolean, the service name is inferred from the SockServiceName.
'';
};
Bonjour = mkOption {
type = types.nullOr (types.either types.bool (types.listOf types.str));
default = null;
description = ''
This optional key can be used to request that the service be registered with the
`mDNSResponder(8)`. If the value is boolean, the service name is inferred from the SockServiceName.
'';
};
MulticastGroup = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key can be used to request that the datagram socket join a multicast group. If the
value is a hostname, then `getaddrinfo(3)` will be used to join the correct multicast address for a
given socket family. If an explicit IPv4 or IPv6 address is given, it is required that the SockFamily
family also be set, otherwise the results are undefined.
'';
};
};
}));
MulticastGroup = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key can be used to request that the datagram socket join a multicast group. If the
value is a hostname, then `getaddrinfo(3)` will be used to join the correct multicast address for a
given socket family. If an explicit IPv4 or IPv6 address is given, it is required that the SockFamily
family also be set, otherwise the results are undefined.
'';
};
};
}
)
);
};
};

View file

@ -5,117 +5,146 @@
{ lib, ... }:
let
inherit (lib) imap1 types mkOption showOption mergeDefinitions;
inherit (builtins) map filter length deepSeq throw toString concatLists;
inherit (lib)
imap1
types
mkOption
showOption
mergeDefinitions
;
inherit (builtins)
map
filter
length
deepSeq
throw
toString
concatLists
;
inherit (lib.options) showDefs;
wildcardText = lib.literalMD "`*`";
/* *
A type of list which does not allow duplicate elements. The base/inner
list type to use (e.g. `types.listOf` or `types.nonEmptyListOf`) is passed
via argument `listType`, which must be the final type and not a function.
/*
*
A type of list which does not allow duplicate elements. The base/inner
list type to use (e.g. `types.listOf` or `types.nonEmptyListOf`) is passed
via argument `listType`, which must be the final type and not a function.
NOTE: The extra check for duplicates is quadratic and strict, so use this
type sparingly and only:
NOTE: The extra check for duplicates is quadratic and strict, so use this
type sparingly and only:
* when needed, and
* when the list is expected to be recursively short (e.g. < 10 elements)
and shallow (i.e. strict evaluation of the list won't take too long)
* when needed, and
* when the list is expected to be recursively short (e.g. < 10 elements)
and shallow (i.e. strict evaluation of the list won't take too long)
The implementation of this function is similar to that of
`types.nonEmptyListOf`.
The implementation of this function is similar to that of
`types.nonEmptyListOf`.
*/
types'.uniqueList = listType:
listType // {
description = "unique ${
types.optionDescriptionPhrase (class: class == "noun") listType
}";
types'.uniqueList =
listType:
listType
// {
description = "unique ${types.optionDescriptionPhrase (class: class == "noun") listType}";
substSubModules = m: types'.uniqueList (listType.substSubModules m);
# This has been taken from the implementation of `types.listOf`, but has
# been modified to throw on duplicates. This check cannot be done in the
# `check` fn as this check is deep/strict, and because `check` runs
# prior to merging.
merge = loc: defs:
merge =
loc: defs:
let
# Each element of `dupes` is a list. When there are duplicates,
# later lists will be duplicates of earlier lists, so just throw on
# the first set of duplicates found so that we don't have duplicate
# error msgs.
checked = filter (li:
checked = filter (
li:
if length li > 1 then
throw ''
The option `${
showOption loc
}' contains duplicate entries after merging:
The option `${showOption loc}' contains duplicate entries after merging:
${showDefs li}''
else
false) dupes;
dupes =
map (def: filter (def': def'.value == def.value) merged) merged;
merged = filter (x: x ? value) (concatLists (imap1 (n: def:
imap1 (m: el:
let
inherit (def) file;
loc' = loc
++ [ "[definition ${toString n}-entry ${toString m}]" ];
in (mergeDefinitions loc' listType.nestedTypes.elemType [{
inherit file;
value = el;
}]).optionalValue // {
inherit loc' file;
}) def.value) defs));
in deepSeq checked (map (x: x.value) merged);
false
) dupes;
dupes = map (def: filter (def': def'.value == def.value) merged) merged;
merged = filter (x: x ? value) (
concatLists (
imap1 (
n: def:
imap1 (
m: el:
let
inherit (def) file;
loc' = loc ++ [ "[definition ${toString n}-entry ${toString m}]" ];
in
(mergeDefinitions loc' listType.nestedTypes.elemType [
{
inherit file;
value = el;
}
]).optionalValue
// {
inherit loc' file;
}
) def.value
) defs
)
);
in
deepSeq checked (map (x: x.value) merged);
};
in {
StartCalendarInterval = let
CalendarIntervalEntry = types.submodule {
options = {
Minute = mkOption {
type = types.nullOr (types.ints.between 0 59);
default = null;
defaultText = wildcardText;
description = ''
The minute on which this job will be run.
'';
};
in
{
StartCalendarInterval =
let
CalendarIntervalEntry = types.submodule {
options = {
Minute = mkOption {
type = types.nullOr (types.ints.between 0 59);
default = null;
defaultText = wildcardText;
description = ''
The minute on which this job will be run.
'';
};
Hour = mkOption {
type = types.nullOr (types.ints.between 0 23);
default = null;
defaultText = wildcardText;
description = ''
The hour on which this job will be run.
'';
};
Hour = mkOption {
type = types.nullOr (types.ints.between 0 23);
default = null;
defaultText = wildcardText;
description = ''
The hour on which this job will be run.
'';
};
Day = mkOption {
type = types.nullOr (types.ints.between 1 31);
default = null;
defaultText = wildcardText;
description = ''
The day on which this job will be run.
'';
};
Day = mkOption {
type = types.nullOr (types.ints.between 1 31);
default = null;
defaultText = wildcardText;
description = ''
The day on which this job will be run.
'';
};
Weekday = mkOption {
type = types.nullOr (types.ints.between 0 7);
default = null;
defaultText = wildcardText;
description = ''
The weekday on which this job will be run (0 and 7 are Sunday).
'';
};
Weekday = mkOption {
type = types.nullOr (types.ints.between 0 7);
default = null;
defaultText = wildcardText;
description = ''
The weekday on which this job will be run (0 and 7 are Sunday).
'';
};
Month = mkOption {
type = types.nullOr (types.ints.between 1 12);
default = null;
defaultText = wildcardText;
description = ''
The month on which this job will be run.
'';
Month = mkOption {
type = types.nullOr (types.ints.between 1 12);
default = null;
defaultText = wildcardText;
description = ''
The month on which this job will be run.
'';
};
};
};
};
in types.either CalendarIntervalEntry
(types'.uniqueList (types.nonEmptyListOf CalendarIntervalEntry));
in
types.either CalendarIntervalEntry (types'.uniqueList (types.nonEmptyListOf CalendarIntervalEntry));
}