systemd: Add functions.escapeSystemdExecArg[s]

This makes it available outside the NixOS `utils` context.
This commit is contained in:
Robert Hensing
2025-04-25 10:44:49 +02:00
parent 2f8fd401b9
commit b915f0c5c0
4 changed files with 90 additions and 25 deletions

View File

@@ -119,31 +119,7 @@ let
)
);
# Quotes an argument for use in Exec* service lines.
# systemd accepts "-quoted strings with escape sequences, toJSON produces
# a subset of these.
# Additionally we escape % to disallow expansion of % specifiers. Any lone ;
# in the input will be turned it ";" and thus lose its special meaning.
# Every $ is escaped to $$, this makes it unnecessary to disable environment
# substitution for the directive.
escapeSystemdExecArg =
arg:
let
s =
if isPath arg then
"${arg}"
else if isString arg then
arg
else if isInt arg || isFloat arg || isDerivation arg then
toString arg
else
throw "escapeSystemdExecArg only allows strings, paths, numbers and derivations";
in
replaceStrings [ "%" "$" ] [ "%%" "$$" ] (toJSON s);
# Quotes a list of arguments into a single string for use in a Exec*
# line.
escapeSystemdExecArgs = concatMapStringsSep " " escapeSystemdExecArg;
inherit (config.systemd.package.functions) escapeSystemdExecArg escapeSystemdExecArgs;
# Returns a system path for a given shell package
toShellPath =

View File

@@ -897,6 +897,11 @@ stdenv.mkDerivation (finalAttrs: {
# needed - and therefore `interfaceVersion` should be incremented.
interfaceVersion = 2;
functions = import ./functions/default.nix {
inherit lib;
systemd = finalAttrs.finalPackage;
};
inherit
withBootloader
withCryptsetup
@@ -1022,6 +1027,12 @@ stdenv.mkDerivation (finalAttrs: {
pkgsCross.${systemString}.systemd;
pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage;
functions = import ./functions/test.nix {
inherit lib;
systemd = finalAttrs.finalPackage;
ok = buildPackages.emptyFile;
};
};
};

View File

@@ -0,0 +1,55 @@
/**
Build the `systemd.functions` library (where `systemd` is the package)
*/
{ lib, systemd }:
let
inherit (lib)
concatMapStringsSep
isDerivation
isFloat
isInt
isPath
isString
replaceStrings
;
inherit (builtins)
toJSON
;
/**
Quotes an argument for use in `Exec*` service lines.
Additionally we escape `%` to disallow expansion of `%` specifiers. Any lone `;`
in the input will be turned into `";"` and thus lose its special meaning.
Every `$` is escaped to `$$`, this makes it unnecessary to disable environment
substitution for the directive.
*/
escapeSystemdExecArg =
arg:
let
s =
if isPath arg then
"${arg}"
else if isString arg then
arg
else if isInt arg || isFloat arg || isDerivation arg then
toString arg
else
throw "escapeSystemdExecArg only allows strings, paths, numbers and derivations";
in
# systemd accepts "-quoted strings with escape sequences, toJSON produces
# a subset of these.
replaceStrings [ "%" "$" ] [ "%%" "$$" ] (toJSON s);
/**
Quotes a list of arguments into a single string for use in a Exec* line.
*/
escapeSystemdExecArgs = concatMapStringsSep " " escapeSystemdExecArg;
in
# Instead of requiring v2, we can make this library conditional on the version as needed.
assert systemd.interfaceVersion == 2;
{
inherit
escapeSystemdExecArg
escapeSystemdExecArgs
;
}

View File

@@ -0,0 +1,23 @@
{
lib,
systemd,
ok,
}:
# This function is also tested in nixosTests.systemd-escaping
assert systemd.functions.escapeSystemdExecArg "hi" == ''"hi"'';
assert systemd.functions.escapeSystemdExecArg "hi there" == ''"hi there"'';
assert systemd.functions.escapeSystemdExecArg ''"hi there"'' == ''"\"hi there\""'';
assert systemd.functions.escapeSystemdExecArg ''"%$'' == ''"\"%%$$"'';
assert
systemd.functions.escapeSystemdExecArgs [
"hi"
"there"
] == ''"hi" "there"'';
assert
systemd.functions.escapeSystemdExecArgs [
"hi"
"%"
"there"
] == ''"hi" "%%" "there"'';
ok