From b51a6c3531c669c744778eb8c986bd7e9b19dcee Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 20 Jul 2025 03:00:05 +0200 Subject: [PATCH] nixos/system/service: Use copy of escapeSystemdExecArgs This unblocks modular services while providing opportunity to improve this when a solution is agreed on. --- .../system/service/systemd/service.nix | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/nixos/modules/system/service/systemd/service.nix b/nixos/modules/system/service/systemd/service.nix index 83f7a58e0deb..9ba9bb7bf3f2 100644 --- a/nixos/modules/system/service/systemd/service.nix +++ b/nixos/modules/system/service/systemd/service.nix @@ -5,7 +5,51 @@ ... }: let - inherit (lib) mkOption types; + inherit (lib) + concatMapStringsSep + isDerivation + isInt + isFloat + isPath + isString + mkOption + replaceStrings + types + ; + inherit (builtins) toJSON; + + # Local copy of systemd exec argument escaping function. + # TODO: This could perhaps be deduplicated, but it is unclear where it should go. + # Preferably, we don't create a hard dependency on NixOS here, so that this + # module can be reused in a non-NixOS context, such as mutaable services + # in /run/systemd/system. + + # 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; + in { imports = [ @@ -69,7 +113,7 @@ in Restart = lib.mkDefault "always"; RestartSec = lib.mkDefault "5"; ExecStart = [ - (systemdPackage.functions.escapeSystemdExecArgs config.process.argv) + (escapeSystemdExecArgs config.process.argv) ]; }; };