All checks were successful
Test / ShareFS (push) Successful in 41s
Test / Sandbox (race detector) (push) Successful in 45s
Test / Create distribution (push) Successful in 42s
Test / Sandbox (push) Successful in 47s
Test / Hpkg (push) Successful in 50s
Test / Hakurei (push) Successful in 56s
Test / Hakurei (race detector) (push) Successful in 56s
Test / Flake checks (push) Successful in 1m35s
Turns out this did not work because in the vm test harness, virtualisation.fileSystems completely and silently overrides fileSystems, causing its contents to not even be evaluated anymore. This is not documented as far as I can tell, and is not obvious by any stretch of the imagination. The current hack is cargo culted from nix-community/impermanence and hopefully lasts until this project fully replaces nix. Signed-off-by: Ophestra <cat@gensokyo.uk>
411 lines
14 KiB
Nix
411 lines
14 KiB
Nix
packages:
|
|
{
|
|
lib,
|
|
pkgs,
|
|
config,
|
|
...
|
|
}:
|
|
|
|
let
|
|
inherit (lib)
|
|
lists
|
|
attrsets
|
|
mkMerge
|
|
mkIf
|
|
mapAttrs
|
|
foldlAttrs
|
|
optional
|
|
optionals
|
|
;
|
|
|
|
cfg = config.environment.hakurei;
|
|
|
|
# userid*userOffset + appStart + appid
|
|
getsubuid = userid: appid: userid * 100000 + 10000 + appid;
|
|
getsubname = userid: appid: "u${toString userid}_a${toString appid}";
|
|
getsubhome = userid: appid: "${cfg.stateDir}/u${toString userid}/a${toString appid}";
|
|
|
|
mountpoints = {
|
|
${cfg.sharefs.name} = mkIf (cfg.sharefs.source != null) {
|
|
depends = [ cfg.sharefs.source ];
|
|
device = "sharefs";
|
|
fsType = "fuse.sharefs";
|
|
noCheck = true;
|
|
options = [
|
|
"rw"
|
|
"noexec"
|
|
"nosuid"
|
|
"nodev"
|
|
"noatime"
|
|
"allow_other"
|
|
"mkdir"
|
|
"source=${cfg.sharefs.source}"
|
|
"setuid=${toString config.users.users.${cfg.sharefs.user}.uid}"
|
|
"setgid=${toString config.users.groups.${cfg.sharefs.group}.gid}"
|
|
];
|
|
};
|
|
};
|
|
in
|
|
|
|
{
|
|
imports = [ (import ./options.nix packages) ];
|
|
|
|
options = {
|
|
# Forward declare a dummy option for VM filesystems since the real one won't exist
|
|
# unless the VM module is actually imported.
|
|
virtualisation.fileSystems = lib.mkOption { };
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
assertions = [
|
|
(
|
|
let
|
|
conflictingApps = foldlAttrs (
|
|
acc: id: app:
|
|
(
|
|
acc
|
|
++ foldlAttrs (
|
|
acc': id': app':
|
|
if id == id' || app.shareUid && app'.shareUid || app.identity != app'.identity then acc' else acc' ++ [ id ]
|
|
) [ ] cfg.apps
|
|
)
|
|
) [ ] cfg.apps;
|
|
in
|
|
{
|
|
assertion = (lists.length conflictingApps) == 0;
|
|
message = "the following hakurei apps have conflicting identities: " + (builtins.concatStringsSep ", " conflictingApps);
|
|
}
|
|
)
|
|
];
|
|
|
|
security.wrappers.hsu = {
|
|
source = "${cfg.hsuPackage}/bin/hsu";
|
|
setuid = true;
|
|
owner = "root";
|
|
group = "root";
|
|
};
|
|
|
|
environment.etc.hsurc = {
|
|
mode = "0400";
|
|
text = foldlAttrs (
|
|
acc: username: fid:
|
|
"${toString config.users.users.${username}.uid} ${toString fid}\n" + acc
|
|
) "" cfg.users;
|
|
};
|
|
|
|
environment.systemPackages = optional (cfg.sharefs.source != null) cfg.sharefs.package;
|
|
fileSystems = mountpoints;
|
|
virtualisation.fileSystems = mountpoints;
|
|
|
|
home-manager =
|
|
let
|
|
privPackages = mapAttrs (_: userid: {
|
|
home.packages = foldlAttrs (
|
|
acc: id: app:
|
|
[
|
|
(
|
|
let
|
|
extendDBusDefault = id: ext: {
|
|
filter = true;
|
|
|
|
talk = [ "org.freedesktop.Notifications" ] ++ ext.talk;
|
|
own = [
|
|
"${id}.*"
|
|
"org.mpris.MediaPlayer2.${id}.*"
|
|
]
|
|
++ ext.own;
|
|
|
|
inherit (ext) call broadcast;
|
|
};
|
|
dbusConfig =
|
|
let
|
|
default = {
|
|
talk = [ ];
|
|
own = [ ];
|
|
call = { };
|
|
broadcast = { };
|
|
};
|
|
in
|
|
{
|
|
session_bus = if app.dbus.session != null then (app.dbus.session (extendDBusDefault id)) else (extendDBusDefault id default);
|
|
system_bus = app.dbus.system;
|
|
};
|
|
command = if app.command == null then app.name else app.command;
|
|
script = if app.script == null then ("exec " + command + " $@") else app.script;
|
|
isGraphical = if app.gpu != null then app.gpu else app.enablements.wayland || app.enablements.x11;
|
|
|
|
conf = {
|
|
inherit id;
|
|
inherit (app) identity groups enablements;
|
|
inherit (dbusConfig) session_bus system_bus;
|
|
direct_wayland = app.insecureWayland;
|
|
|
|
container = {
|
|
inherit (app)
|
|
wait_delay
|
|
devel
|
|
userns
|
|
device
|
|
tty
|
|
multiarch
|
|
env
|
|
;
|
|
map_real_uid = app.mapRealUid;
|
|
host_net = app.hostNet;
|
|
host_abstract = app.hostAbstract;
|
|
share_runtime = app.shareRuntime;
|
|
share_tmpdir = app.shareTmpdir;
|
|
|
|
filesystem =
|
|
let
|
|
bind = src: {
|
|
type = "bind";
|
|
inherit src;
|
|
};
|
|
optBind = src: {
|
|
type = "bind";
|
|
inherit src;
|
|
optional = true;
|
|
};
|
|
optDevBind = src: {
|
|
type = "bind";
|
|
inherit src;
|
|
dev = true;
|
|
optional = true;
|
|
};
|
|
in
|
|
[
|
|
(bind "/bin")
|
|
(bind "/usr/bin")
|
|
(bind "/nix/store")
|
|
(optBind "/sys/block")
|
|
(optBind "/sys/bus")
|
|
(optBind "/sys/class")
|
|
(optBind "/sys/dev")
|
|
(optBind "/sys/devices")
|
|
]
|
|
++ optionals app.nix [
|
|
(bind "/nix/var")
|
|
]
|
|
++ optionals isGraphical [
|
|
(optDevBind "/dev/dri")
|
|
(optDevBind "/dev/nvidiactl")
|
|
(optDevBind "/dev/nvidia-modeset")
|
|
(optDevBind "/dev/nvidia-uvm")
|
|
(optDevBind "/dev/nvidia-uvm-tools")
|
|
(optDevBind "/dev/nvidia0")
|
|
]
|
|
++ optionals app.useCommonPaths cfg.commonPaths
|
|
++ app.extraPaths
|
|
++ [
|
|
{
|
|
type = "bind";
|
|
dst = "/etc/";
|
|
src = "/etc/";
|
|
special = true;
|
|
}
|
|
{
|
|
type = "link";
|
|
dst = "/run/current-system";
|
|
linkname = "/run/current-system";
|
|
dereference = true;
|
|
}
|
|
]
|
|
++ optionals (isGraphical && config.hardware.graphics.enable) (
|
|
[
|
|
{
|
|
type = "link";
|
|
dst = "/run/opengl-driver";
|
|
linkname = config.systemd.tmpfiles.settings.graphics-driver."/run/opengl-driver"."L+".argument;
|
|
}
|
|
]
|
|
++ optionals (app.multiarch && config.hardware.graphics.enable32Bit) [
|
|
{
|
|
type = "link";
|
|
dst = "/run/opengl-driver-32";
|
|
linkname = config.systemd.tmpfiles.settings.graphics-driver."/run/opengl-driver-32"."L+".argument;
|
|
}
|
|
]
|
|
)
|
|
++ [
|
|
{
|
|
type = "bind";
|
|
src = getsubhome userid app.identity;
|
|
write = true;
|
|
ensure = true;
|
|
}
|
|
];
|
|
|
|
username = getsubname userid app.identity;
|
|
inherit (cfg) shell;
|
|
home = getsubhome userid app.identity;
|
|
|
|
path =
|
|
if app.path == null then
|
|
pkgs.writeScript "${app.name}-start" ''
|
|
#!${pkgs.zsh}${pkgs.zsh.shellPath}
|
|
${script}
|
|
''
|
|
else
|
|
app.path;
|
|
args = if app.args == null then [ "${app.name}-start" ] else app.args;
|
|
};
|
|
};
|
|
|
|
checkedConfig =
|
|
name: value:
|
|
let
|
|
file = pkgs.writeText name (builtins.toJSON value);
|
|
in
|
|
pkgs.runCommand "checked-${name}" { nativeBuildInputs = [ cfg.package ]; } ''
|
|
ln -vs ${file} "$out"
|
|
hakurei show --no-store ${file}
|
|
'';
|
|
in
|
|
pkgs.writeShellScriptBin app.name ''
|
|
exec hakurei${if app.verbose then " -v" else ""} app ${checkedConfig "hakurei-app-${app.name}.json" conf} $@
|
|
''
|
|
)
|
|
]
|
|
++ (
|
|
let
|
|
pkg = if app.share != null then app.share else pkgs.${app.name};
|
|
copy = source: "[ -d '${source}' ] && cp -Lrv '${source}' $out/share || true";
|
|
in
|
|
optional (app.enablements.wayland || app.enablements.x11) (
|
|
pkgs.runCommand "${app.name}-share" { } ''
|
|
mkdir -p $out/share
|
|
${copy "${pkg}/share/applications"}
|
|
${copy "${pkg}/share/pixmaps"}
|
|
${copy "${pkg}/share/icons"}
|
|
${copy "${pkg}/share/man"}
|
|
|
|
if test -d "$out/share/applications"; then
|
|
substituteInPlace $out/share/applications/* \
|
|
--replace-warn '${pkg}/bin/' "" \
|
|
--replace-warn '${pkg}/libexec/' ""
|
|
fi
|
|
''
|
|
)
|
|
)
|
|
++ acc
|
|
) [ cfg.package ] cfg.apps;
|
|
}) cfg.users;
|
|
in
|
|
{
|
|
useUserPackages = false; # prevent users.users entries from being added
|
|
|
|
users =
|
|
mkMerge
|
|
(foldlAttrs
|
|
(
|
|
acc: _: fid:
|
|
foldlAttrs
|
|
(
|
|
acc: _: app:
|
|
(
|
|
let
|
|
key = getsubname fid app.identity;
|
|
in
|
|
{
|
|
usernames = acc.usernames // {
|
|
${key} = true;
|
|
};
|
|
merge = acc.merge ++ [
|
|
{
|
|
${key} = mkMerge (
|
|
[
|
|
app.extraConfig
|
|
{ home.packages = app.packages; }
|
|
]
|
|
++ lib.optional (!attrsets.hasAttrByPath [ key ] acc.usernames) cfg.extraHomeConfig
|
|
);
|
|
}
|
|
];
|
|
}
|
|
)
|
|
)
|
|
{
|
|
inherit (acc) usernames;
|
|
merge = acc.merge ++ [ { ${getsubname fid 0} = cfg.extraHomeConfig; } ];
|
|
}
|
|
cfg.apps
|
|
)
|
|
{
|
|
usernames = { };
|
|
merge = [ privPackages ];
|
|
}
|
|
cfg.users
|
|
).merge;
|
|
};
|
|
|
|
users =
|
|
let
|
|
getuser = userid: appid: {
|
|
isSystemUser = true;
|
|
createHome = true;
|
|
description = "Hakurei subordinate user ${toString appid} (u${toString userid})";
|
|
group = getsubname userid appid;
|
|
home = getsubhome userid appid;
|
|
uid = getsubuid userid appid;
|
|
};
|
|
getgroup = userid: appid: { gid = getsubuid userid appid; };
|
|
in
|
|
{
|
|
users = mkMerge (
|
|
foldlAttrs
|
|
(
|
|
acc: _: fid:
|
|
acc
|
|
++ foldlAttrs (
|
|
acc': _: app:
|
|
acc' ++ [ { ${getsubname fid app.identity} = getuser fid app.identity; } ]
|
|
) [ { ${getsubname fid 0} = getuser fid 0; } ] cfg.apps
|
|
)
|
|
(
|
|
if (cfg.sharefs.source != null) then
|
|
[
|
|
{
|
|
${cfg.sharefs.user} = {
|
|
uid = lib.mkDefault 1023;
|
|
inherit (cfg.sharefs) group;
|
|
isSystemUser = true;
|
|
home = cfg.sharefs.source;
|
|
};
|
|
|
|
}
|
|
]
|
|
else
|
|
[ ]
|
|
)
|
|
cfg.users
|
|
);
|
|
|
|
groups = mkMerge (
|
|
foldlAttrs
|
|
(
|
|
acc: _: fid:
|
|
acc
|
|
++ foldlAttrs (
|
|
acc': _: app:
|
|
acc' ++ [ { ${getsubname fid app.identity} = getgroup fid app.identity; } ]
|
|
) [ { ${getsubname fid 0} = getgroup fid 0; } ] cfg.apps
|
|
)
|
|
(
|
|
if (cfg.sharefs.source != null) then
|
|
[
|
|
{
|
|
${cfg.sharefs.group} = {
|
|
gid = lib.mkDefault 1023;
|
|
};
|
|
}
|
|
]
|
|
else
|
|
[ ]
|
|
)
|
|
cfg.users
|
|
);
|
|
};
|
|
};
|
|
}
|