Skip to content

Commit

Permalink
Merge pull request #148 from phanirithvij/tmpfiles-settings
Browse files Browse the repository at this point in the history
systemd: support systemd.tmpfiles.settings
  • Loading branch information
r-vdp authored Nov 8, 2024
2 parents a46f563 + eb602df commit c80b30c
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 14 deletions.
5 changes: 5 additions & 0 deletions examples/example.nix
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,10 @@
)
);
systemd.tmpfiles.rules = [ "D /var/tmp/system-manager 0755 root root -" ];
systemd.tmpfiles.settings.sample = {
"/var/tmp/sample".d = {
mode = "0755";
};
};
};
}
194 changes: 187 additions & 7 deletions nix/modules/tmpfiles.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,139 @@
{ config, lib, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) types;
inherit (lib)
concatStrings
concatStringsSep
getLib
literalExpression
mapAttrsToList
mkOption
types
;
# copied from nixos/modules/system/boot/systemd/tmpfiles.nix
settingsOption = {
description = ''
Declare systemd-tmpfiles rules to create, delete, and clean up volatile
and temporary files and directories.
Even though the service is called `*tmp*files` you can also create
persistent files.
'';
example = {
"10-mypackage" = {
"/var/lib/my-service/statefolder".d = {
mode = "0755";
user = "root";
group = "root";
};
};
};
default = { };
type = types.attrsOf (
types.attrsOf (
types.attrsOf (
types.submodule (
{ name, ... }:
{
options = {
type = mkOption {
type = types.str;
default = name;
example = "d";
description = ''
The type of operation to perform on the file.
The type consists of a single letter and optionally one or more
modifier characters.
Please see the upstream documentation for the available types and
more details:
<https://www.freedesktop.org/software/systemd/man/tmpfiles.d>
'';
};
mode = mkOption {
type = types.str;
default = "-";
example = "0755";
description = ''
The file access mode to use when creating this file or directory.
'';
};
user = mkOption {
type = types.str;
default = "-";
example = "root";
description = ''
The user of the file.
This may either be a numeric ID or a user/group name.
If omitted or when set to `"-"`, the user and group of the user who
invokes systemd-tmpfiles is used.
'';
};
group = mkOption {
type = types.str;
default = "-";
example = "root";
description = ''
The group of the file.
This may either be a numeric ID or a user/group name.
If omitted or when set to `"-"`, the user and group of the user who
invokes systemd-tmpfiles is used.
'';
};
age = mkOption {
type = types.str;
default = "-";
example = "10d";
description = ''
Delete a file when it reaches a certain age.
If a file or directory is older than the current time minus the age
field, it is deleted.
If set to `"-"` no automatic clean-up is done.
'';
};
argument = mkOption {
type = types.str;
default = "";
example = "";
description = ''
An argument whose meaning depends on the type of operation.
Please see the upstream documentation for the meaning of this
parameter in different situations:
<https://www.freedesktop.org/software/systemd/man/tmpfiles.d>
'';
};
};
}
)
)
)
);
};

# generates a single entry for a tmpfiles.d rule
settingsEntryToRule = path: entry: ''
'${entry.type}' '${path}' '${entry.mode}' '${entry.user}' '${entry.group}' '${entry.age}' ${entry.argument}
'';

# generates a list of tmpfiles.d rules from the attrs (paths) under tmpfiles.settings.<name>
pathsToRules = mapAttrsToList (
path: types: concatStrings (mapAttrsToList (_type: settingsEntryToRule path) types)
);

mkRuleFileContent = paths: concatStrings (pathsToRules paths);

in
{
options = {
Expand All @@ -15,13 +148,60 @@ in
for the exact format.
'';
};

systemd.tmpfiles.settings = lib.mkOption settingsOption;

systemd.tmpfiles.packages = mkOption {
type = types.listOf types.package;
default = [ ];
example = literalExpression "[ pkgs.lvm2 ]";
apply = map getLib;
description = ''
List of packages containing {command}`systemd-tmpfiles` rules.
All files ending in .conf found in
{file}`«pkg»/lib/tmpfiles.d`
will be included.
If this folder does not exist or does not contain any files an error will be returned instead.
If a {file}`lib` output is available, rules are searched there and only there.
If there is no {file}`lib` output it will fall back to {file}`out`
and if that does not exist either, the default output will be used.
'';
};

};

config = {
environment.etc."tmpfiles.d/00-system-manager.conf".text = ''
# This file is created automatically and should not be modified.
# Please change the option ‘systemd.tmpfiles.rules’ instead.
${lib.concatStringsSep "\n" config.systemd.tmpfiles.rules}
'';
environment.etc = {
"tmpfiles.d".source = pkgs.symlinkJoin {
name = "tmpfiles.d";
paths = map (p: p + "/lib/tmpfiles.d") config.systemd.tmpfiles.packages;
postBuild = ''
for i in $(cat $pathsPath); do
(test -d "$i" && test $(ls "$i"/*.conf | wc -l) -ge 1) || (
echo "ERROR: The path '$i' from systemd.tmpfiles.packages contains no *.conf files."
exit 1
)
done
'';
};
};
systemd.tmpfiles.packages =
[
(pkgs.writeTextFile {
name = "system-manager-tmpfiles.d";
destination = "/lib/tmpfiles.d/00-system-manager.conf";
text = ''
# This file is created automatically and should not be modified.
# Please change the option ‘systemd.tmpfiles.rules’ instead.
${concatStringsSep "\n" config.systemd.tmpfiles.rules}
'';
})
]
++ (mapAttrsToList (
name: paths: pkgs.writeTextDir "lib/tmpfiles.d/${name}.conf" (mkRuleFileContent paths)
) config.systemd.tmpfiles.settings);
};
}
2 changes: 1 addition & 1 deletion src/activate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
match etc_files::activate(store_path, old_state.file_tree, ephemeral) {
Ok(etc_tree) => {
log::info!("Activating tmp files...");
match tmp_files::activate() {
match tmp_files::activate(&etc_tree) {
Ok(_) => {
log::debug!("Successfully created tmp files");
}
Expand Down
4 changes: 2 additions & 2 deletions src/activate/etc_files/etc_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ impl FileStatus {
#[serde(rename_all = "camelCase")]
pub struct FileTree {
status: FileStatus,
path: PathBuf,
pub(crate) path: PathBuf,
// TODO directories and files are now both represented as a string associated with a nested
// map. For files the nested map is simple empty.
// We could potentially optimise this.
nested: HashMap<String, FileTree>,
pub(crate) nested: HashMap<String, FileTree>,
}

impl AsRef<FileTree> for FileTree {
Expand Down
19 changes: 15 additions & 4 deletions src/activate/tmp_files.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
use crate::activate;
use crate::activate::etc_files::FileTree;

use super::ActivationResult;
use std::process;

type TmpFilesActivationResult = ActivationResult<()>;

pub fn activate() -> TmpFilesActivationResult {
pub fn activate(etc_tree: &FileTree) -> TmpFilesActivationResult {
let conf_files = etc_tree
.nested
.get("etc")
.and_then(|etc| etc.nested.get("tmpfiles.d"))
.map_or(vec![], |tmpfiles_d| {
tmpfiles_d
.nested
.iter()
.map(|(_, node)| node.path.to_string_lossy().to_string())
.collect::<Vec<_>>()
});
let mut cmd = process::Command::new("systemd-tmpfiles");
cmd.arg("--create")
.arg("--remove")
.arg("/etc/tmpfiles.d/00-system-manager.conf");
cmd.arg("--create").arg("--remove").args(conf_files);
log::debug!("running {:#?}", cmd);
let output = cmd
.stdout(process::Stdio::inherit())
.stderr(process::Stdio::inherit())
Expand Down
4 changes: 4 additions & 0 deletions test/nix/modules/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ forEachUbuntuImage "example" {
vm.fail("grep -F 'launch_the_rockets = false' /etc/foo.conf")
vm.succeed("test -d /var/tmp/system-manager")
vm.succeed("test -d /var/tmp/sample")
vm.succeed("test -f /etc/tmpfiles.d/sample.conf")
vm.succeed("test -f /etc/tmpfiles.d/00-system-manager.conf")
${system-manager.lib.activateProfileSnippet {
node = "vm";
Expand Down

0 comments on commit c80b30c

Please sign in to comment.