Home Explore Blog CI



nixpkgs

3rd chunk of `nixos/doc/manual/development/writing-modules.chapter.md`
7bee89892c3c7be1e065778d5873cf0562d9f6c16e7492720000000100000c82
default systemd performs substitution on `%<char>` specifiers in these
directives, expands environment variables from `$FOO` and `${FOO}`, splits
arguments on whitespace, and splits commands on `;`. All of these must be escaped
to avoid unexpected substitution or splitting when interpolating into an `Exec*`
directive, e.g. when using an `extraArgs` option to pass additional arguments to
the service. The functions `utils.escapeSystemdExecArg` and
`utils.escapeSystemdExecArgs` are provided for this, see [Example: Escaping in
Exec directives](#exec-escaping-example) for an example. When using these
functions system environment substitution should *not* be disabled explicitly.

::: {#locate-example .example}
### NixOS Module for the "locate" Service
```nix
{ config, lib, pkgs, ... }:

let
  inherit (lib) concatStringsSep mkIf mkOption optionalString types;
  cfg = config.services.locate;
in {
  options.services.locate = {
    enable = mkOption {
      type = types.bool;
      default = false;
      description = ''
        If enabled, NixOS will periodically update the database of
        files used by the locate command.
      '';
    };

    interval = mkOption {
      type = types.str;
      default = "02:15";
      example = "hourly";
      description = ''
        Update the locate database at this interval. Updates by
        default at 2:15 AM every day.

        The format is described in
        systemd.time(7).
      '';
    };

    # Other options omitted for documentation
  };

  config = {
    systemd.services.update-locatedb =
      { description = "Update Locate Database";
        path  = [ pkgs.su ];
        script =
          ''
            mkdir -p $(dirname ${toString cfg.output})
            chmod 0755 $(dirname ${toString cfg.output})
            exec updatedb \
              --localuser=${cfg.localuser} \
              ${optionalString (!cfg.includeStore) "--prunepaths='/nix/store'"} \
              --output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags}
          '';
      };

    systemd.timers.update-locatedb = mkIf cfg.enable
      { description = "Update timer for locate database";
        partOf      = [ "update-locatedb.service" ];
        wantedBy    = [ "timers.target" ];
        timerConfig.OnCalendar = cfg.interval;
      };
  };
}
```
:::

::: {#exec-escaping-example .example}
### Escaping in Exec directives
```nix
{ config, pkgs, utils, ... }:

let
  cfg = config.services.echo;
  echoAll = pkgs.writeScript "echo-all" ''
    #! ${pkgs.runtimeShell}
    for s in "$@"; do
      printf '%s\n' "$s"
    done
  '';
  args = [ "a%Nything" "lang=\${LANG}" ";" "/bin/sh -c date" ];
in {
  systemd.services.echo =
    { description = "Echo to the journal";
      wantedBy = [ "multi-user.target" ];
      serviceConfig.Type = "oneshot";
      serviceConfig.ExecStart = ''
        ${echoAll} ${utils.escapeSystemdExecArgs args}
      '';
    };
}
```
:::

```{=include=} sections
option-declarations.section.md
option-types.section.md
option-def.section.md
assertions.section.md
meta-attributes.section.md
importing-modules.section.md
replace-modules.section.md
freeform-modules.section.md
settings-options.section.md
```

Title: NixOS Module Examples: Locate Service and Exec Directive Escaping
Summary
The text provides a NixOS module example for the 'locate' service, showing how to periodically update the locate database using systemd services and timers. It also emphasizes the importance of escaping special characters in systemd `Exec*` directives using `utils.escapeSystemdExecArg` and `utils.escapeSystemdExecArgs` to avoid unintended substitution or splitting. An example demonstrates how to use these functions to properly escape arguments passed to a service.