mirror of
https://github.com/gensokyo-zone/infrastructure.git
synced 2026-02-10 04:49:19 -08:00
240 lines
7.8 KiB
Nix
240 lines
7.8 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
inputs,
|
|
pkgs,
|
|
...
|
|
}: let
|
|
inherit (inputs.self.lib.lib) unmerged;
|
|
inherit (lib.options) mkOption mkEnableOption;
|
|
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
|
|
inherit (lib.strings) match concatStringsSep escapeShellArg optionalString;
|
|
inherit (lib.attrsets) attrValues;
|
|
inherit (lib.lists) filter;
|
|
isGroupWritable = mode: match "[234567][0-7][76][0-7]" mode != null;
|
|
isOtherWritable = mode: match "[0-7][0-7][0-7][76]" mode != null;
|
|
cfg = config.services.tmpfiles;
|
|
files = filter (file: file.enable) (attrValues cfg.files);
|
|
systemdFiles = filter (file: file.systemd.enable) files;
|
|
setupFiles = filter (file: !file.systemd.enable) files;
|
|
bindFiles = filter (file: file.type == "bind") files;
|
|
fileModule = { config, name, ... }: {
|
|
options = with lib.types; {
|
|
enable = mkEnableOption "file" // {
|
|
default = true;
|
|
};
|
|
mkdirParent = mkEnableOption "mkdir";
|
|
bindReadOnly = mkEnableOption "mount -oro";
|
|
relativeSymlink = mkEnableOption "ln -sr";
|
|
noOverwrite = mkEnableOption "disable overwrite";
|
|
path = mkOption {
|
|
type = path;
|
|
default = name;
|
|
};
|
|
type = mkOption {
|
|
type = enum [ "directory" "symlink" "link" "copy" "bind" ];
|
|
default = if config.src != null then "symlink" else "directory";
|
|
};
|
|
mode = mkOption {
|
|
type = str;
|
|
default = "0755";
|
|
};
|
|
owner = mkOption {
|
|
type = str;
|
|
default = cfg.user;
|
|
};
|
|
group = mkOption {
|
|
type = str;
|
|
default = "root";
|
|
};
|
|
src = mkOption {
|
|
type = nullOr path;
|
|
default = null;
|
|
};
|
|
acls = mkOption {
|
|
type = listOf str;
|
|
};
|
|
systemd = {
|
|
enable = mkEnableOption "systemd-tmpfiles";
|
|
rules = mkOption {
|
|
type = listOf str;
|
|
};
|
|
mountSettings = mkOption {
|
|
type = unmerged.type;
|
|
};
|
|
};
|
|
setup = {
|
|
script = mkOption {
|
|
type = lines;
|
|
};
|
|
};
|
|
};
|
|
config = let
|
|
acls = concatStringsSep "," config.acls;
|
|
enableAcls = config.type == "directory" && config.acls != [ ];
|
|
systemdAclRule = "a+ ${config.path} - - - - ${acls}";
|
|
systemdRule = {
|
|
directory = [
|
|
"d ${config.path} ${config.mode} ${config.owner} ${config.group}"
|
|
];
|
|
symlink = [
|
|
"L+ ${config.path} - - - - ${config.src}"
|
|
];
|
|
copy = [
|
|
"C ${config.path} - - - - ${config.src}"
|
|
"z ${config.path} ${config.mode} ${config.owner} ${config.group} - ${config.src}"
|
|
];
|
|
link = throw "unsupported link for systemd tmpfiles";
|
|
bind = throw "unsupported bind for systemd tmpfiles";
|
|
};
|
|
chown = "chown ${escapeShellArg config.owner}:${escapeShellArg config.group} ${escapeShellArg config.path}";
|
|
chmod = "chmod ${escapeShellArg config.mode} ${escapeShellArg config.path}";
|
|
parentFlag = optionalString config.mkdirParent "p";
|
|
relativeFlag = optionalString config.relativeSymlink "r";
|
|
scriptCatch = " || EXITCODE=$?";
|
|
scriptFail = "EXITCODE=1";
|
|
setupScript = {
|
|
directory = ''
|
|
if [[ -d ${escapeShellArg config.path} ]]; then
|
|
${chmod} &&
|
|
${chown}${scriptCatch}
|
|
elif [[ ! -e ${escapeShellArg config.path} ]]; then
|
|
mkdir -${parentFlag}m ${escapeShellArg config.mode} ${escapeShellArg config.path} &&
|
|
${chown}${scriptCatch}
|
|
else
|
|
echo ${escapeShellArg config.path} exists but is not a directory >&2
|
|
${scriptFail}
|
|
fi
|
|
'';
|
|
symlink = ''
|
|
if [[ -e ${escapeShellArg config.path} && ! -L ${escapeShellArg config.path} ]]; then
|
|
echo ${escapeShellArg config.path} exists but is not a symlink >&2
|
|
${scriptFail}
|
|
else
|
|
if [[ ! -L ${escapeShellArg config.path} || -z ${escapeShellArg config.noOverwrite} ]]; then
|
|
ln -s${relativeFlag}fT ${escapeShellArg config.src} ${escapeShellArg config.path} &&
|
|
${chown} -h${scriptCatch}
|
|
fi
|
|
fi
|
|
'';
|
|
link = ''
|
|
if [[ -L ${escapeShellArg config.path} ]]; then
|
|
rm -f ${escapeShellArg config.path}
|
|
fi
|
|
ln -fT ${escapeShellArg config.src} ${escapeShellArg config.path}${scriptCatch}
|
|
'';
|
|
copy = ''
|
|
if [[ -d ${escapeShellArg config.src} ]]; then
|
|
echo TODO: copy directory to ${escapeShellArg config.path} >&2
|
|
${scriptFail}
|
|
else
|
|
if [[ -L ${escapeShellArg config.path} ]] || [[ -e ${escapeShellArg config.path} && ! -f ${escapeShellArg config.path} ]]; then
|
|
echo ${escapeShellArg config.path} exists but is not a file >&2
|
|
${scriptFail}
|
|
else
|
|
if [[ ! -e ${escapeShellArg config.path} || -z ${escapeShellArg config.noOverwrite} ]]; then
|
|
cp -TPf ${escapeShellArg config.src} ${escapeShellArg config.path}${scriptCatch}
|
|
fi
|
|
${chmod} &&
|
|
${chown}${scriptCatch}
|
|
fi
|
|
fi
|
|
'';
|
|
bind = ''
|
|
if [[ ! -e ${escapeShellArg config.src} ]]; then
|
|
echo ${escapeShellArg config.src} does not exist >&2
|
|
${scriptFail}
|
|
elif [[ -d $(readlink -f ${escapeShellArg config.src}) ]]; then
|
|
mkdir -p ${escapeShellArg config.path}${scriptCatch}
|
|
else
|
|
if [[ ! -e ${escapeShellArg config.path} ]]; then
|
|
touch ${escapeShellArg config.path}${scriptCatch}
|
|
fi
|
|
fi
|
|
'';
|
|
};
|
|
aclScript = ''
|
|
setfacl -b -m ${escapeShellArg acls} ${escapeShellArg config.path}${scriptCatch}
|
|
'';
|
|
in {
|
|
acls = mkOptionDefault [
|
|
(mkIf (isGroupWritable config.mode) "default:group::rwx")
|
|
(mkIf (isOtherWritable config.mode) "default:other::rwx")
|
|
];
|
|
setup.script = mkMerge [
|
|
setupScript.${config.type}
|
|
(mkIf enableAcls aclScript)
|
|
];
|
|
systemd = {
|
|
rules = mkMerge [
|
|
systemdRule.${config.type}
|
|
(mkIf enableAcls [ systemdAclRule ])
|
|
];
|
|
mountSettings = mkIf (config.type == "bind") {
|
|
enable = mkDefault config.enable;
|
|
type = mkDefault "none";
|
|
options = mkMerge [
|
|
"bind"
|
|
(mkIf config.bindReadOnly "ro")
|
|
];
|
|
what = mkDefault config.src;
|
|
where = mkDefault config.path;
|
|
wantedBy = [
|
|
"tmpfiles.service"
|
|
];
|
|
after = mkDefault [
|
|
"tmpfiles.service"
|
|
];
|
|
};
|
|
};
|
|
};
|
|
};
|
|
in {
|
|
options.services.tmpfiles = with lib.types; {
|
|
enable = mkEnableOption "extended tmpfiles" // {
|
|
default = cfg.files != { };
|
|
};
|
|
user = mkOption {
|
|
type = str;
|
|
default = if config.proxmoxLXC.privileged or true then "root" else "admin";
|
|
};
|
|
files = mkOption {
|
|
type = attrsOf (submodule fileModule);
|
|
default = { };
|
|
};
|
|
};
|
|
config = {
|
|
systemd = mkIf cfg.enable {
|
|
tmpfiles.rules = mkMerge (
|
|
map (file: file.systemd.rules) systemdFiles
|
|
);
|
|
services.tmpfiles = {
|
|
path = [ pkgs.coreutils pkgs.acl ];
|
|
script = mkMerge (
|
|
[ ''
|
|
EXITCODE=0
|
|
'' ]
|
|
++ map (file: file.setup.script) setupFiles
|
|
++ [ ''
|
|
exit $EXITCODE
|
|
'' ]
|
|
);
|
|
wantedBy = [
|
|
"sysinit.target"
|
|
];
|
|
after = [
|
|
"local-fs.target"
|
|
];
|
|
before = [
|
|
"systemd-tmpfiles-setup.service"
|
|
"systemd-tmpfiles-resetup.service"
|
|
];
|
|
serviceConfig = {
|
|
User = mkOptionDefault cfg.user;
|
|
RemainAfterExit = mkOptionDefault true;
|
|
};
|
|
};
|
|
mounts = map (file: unmerged.merge file.systemd.mountSettings) bindFiles;
|
|
};
|
|
};
|
|
}
|