fs: add experimental support for symlink entries

This commit is contained in:
2023-01-04 02:51:07 +00:00
parent 175bc0709f
commit c94b8299a6

View File

@@ -24,6 +24,10 @@ let
type = types.nullOr (mountEntryFor name); type = types.nullOr (mountEntryFor name);
default = null; default = null;
}; };
symlink = mkOption {
type = types.nullOr (symlinkEntryFor name);
default = null;
};
unit = mkOption { unit = mkOption {
type = types.str; type = types.str;
description = "name of the systemd unit which ensures this entry"; description = "name of the systemd unit which ensures this entry";
@@ -42,9 +46,13 @@ let
# if defaulted, this module is responsible for finalizing the entry. # if defaulted, this module is responsible for finalizing the entry.
# the user could override this if, say, they finalize some aspect of the entry # the user could override this if, say, they finalize some aspect of the entry
# with a custom service. # with a custom service.
unit = lib.mkDefault (if config.mount != null then unit = lib.mkDefault (
if config.mount != null then
config.mount.unit config.mount.unit
else config.dir.unit); else if config.symlink != null then
config.symlink.unit
else config.dir.unit
);
}; };
}); });
@@ -92,7 +100,22 @@ let
}; };
}; };
# given a fsEntry definition, output the `config` attrs it generates. symlinkEntryFor = path: types.submodule {
options = {
target = mkOption {
type = types.str;
description = "fs path to link to";
default = null;
};
unit = mkOption {
type = types.str;
description = "name of the systemd unit which mounts this path";
default = mountNameFor path;
};
};
};
# given a dirEntry definition, evaluate its toplevel `config` output.
mkDirConfig = path: opt: { mkDirConfig = path: opt: {
systemd.services."${serviceNameFor path}" = { systemd.services."${serviceNameFor path}" = {
description = "prepare ${path}"; description = "prepare ${path}";
@@ -112,6 +135,7 @@ let
}; };
}; };
# given a mountEntry definition, evaluate its toplevel `config` output.
mkMountConfig = path: opt: (let mkMountConfig = path: opt: (let
underlying = cfg."${opt.mount.bind}"; underlying = cfg."${opt.mount.bind}";
in { in {
@@ -131,8 +155,29 @@ let
}; };
}); });
# given a symlinkEntry definition, evaluate its toplevel `config` output.
mkSymlinkConfig = path: opt: {
systemd.services."${serviceNameFor path}" = {
description = "prepare ${path}";
serviceConfig.Type = "oneshot";
script = ensure-symlink-script;
scriptArgs = "${path} ${opt.target}";
after = opt.dir.depends;
wants = opt.dir.depends;
# prevent systemd making this unit implicitly dependent on sysinit.target.
# see: <https://www.freedesktop.org/software/systemd/man/systemd.special.html>
unitConfig.DefaultDependencies = "no";
wantedBy = opt.dir.reverseDepends;
before = opt.dir.reverseDepends;
};
};
mkFsConfig = path: opt: mergeTopLevel [ mkFsConfig = path: opt: mergeTopLevel [
(mkDirConfig path opt) (mkDirConfig path opt)
(lib.mkIf (opt.symlink != null) (mkSymlinkConfig path opt))
(lib.mkIf (opt.mount != null) (mkMountConfig path opt)) (lib.mkIf (opt.mount != null) (mkMountConfig path opt))
]; ];
@@ -167,6 +212,14 @@ let
chown "$user:$group" "$path" chown "$user:$group" "$path"
''; '';
# systemd/shell script used to create a symlink
ensure-symlink-script = ''
from="$1"
to="$2"
ln -sf "$2" "$1"
'';
# return all ancestors of this path. # return all ancestors of this path.
# e.g. ancestorsOf "/foo/bar/baz" => [ "/" "/foo" "/foo/bar" ] # e.g. ancestorsOf "/foo/bar/baz" => [ "/" "/foo" "/foo/bar" ]
ancestorsOf = path: if path-lib.hasParent path then ancestorsOf = path: if path-lib.hasParent path then