mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-11-17 21:25:19 +01:00
251 lines
8 KiB
Nix
251 lines
8 KiB
Nix
{
|
|
config,
|
|
pkgs,
|
|
lib,
|
|
...
|
|
}:
|
|
let
|
|
inherit (lib)
|
|
mkIf
|
|
mkEnableOption
|
|
mkPackageOption
|
|
mkOption
|
|
literalExpression
|
|
hasAttr
|
|
toList
|
|
length
|
|
head
|
|
tail
|
|
concatStringsSep
|
|
optionalString
|
|
optionalAttrs
|
|
isDerivation
|
|
recursiveUpdate
|
|
getExe
|
|
types
|
|
maintainers
|
|
;
|
|
cfg = config.services.wivrn;
|
|
configFormat = pkgs.formats.json { };
|
|
|
|
# For the application option to work with systemd PATH, we find the store binary path of
|
|
# the package, concat all of the following strings, and then update the application attribute.
|
|
|
|
# Since the json config attribute type "configFormat.type" doesn't allow specifying types for
|
|
# individual attributes, we have to type check manually.
|
|
|
|
# The application option should be a list with package as the first element, though a single package is also valid.
|
|
# Note that this module depends on the package containing the meta.mainProgram attribute.
|
|
|
|
# Check if an application is provided
|
|
applicationAttrExists = hasAttr "application" cfg.config.json;
|
|
applicationList = toList cfg.config.json.application;
|
|
applicationListNotEmpty = length applicationList != 0;
|
|
applicationCheck = applicationAttrExists && applicationListNotEmpty;
|
|
|
|
# Manage packages and their exe paths
|
|
applicationAttr = head applicationList;
|
|
applicationPackage = mkIf applicationCheck applicationAttr;
|
|
applicationPackageExe = getExe applicationAttr;
|
|
serverPackageExe = (
|
|
if cfg.highPriority then "${config.security.wrapperDir}/wivrn-server" else getExe cfg.package
|
|
);
|
|
|
|
# Manage strings
|
|
applicationStrings = tail applicationList;
|
|
applicationConcat = concatStringsSep " " ([ applicationPackageExe ] ++ applicationStrings);
|
|
|
|
# Manage config file
|
|
applicationUpdate = recursiveUpdate cfg.config.json (
|
|
optionalAttrs applicationCheck { application = applicationConcat; }
|
|
);
|
|
configFile = configFormat.generate "config.json" applicationUpdate;
|
|
enabledConfig = optionalString cfg.config.enable "-f ${configFile}";
|
|
|
|
# Manage server executables and flags
|
|
serverExec = concatStringsSep " " (
|
|
[
|
|
serverPackageExe
|
|
"--systemd"
|
|
enabledConfig
|
|
]
|
|
++ cfg.extraServerFlags
|
|
);
|
|
in
|
|
{
|
|
options = {
|
|
services.wivrn = {
|
|
enable = mkEnableOption "WiVRn, an OpenXR streaming application";
|
|
|
|
package = mkPackageOption pkgs "wivrn" { };
|
|
|
|
openFirewall = mkEnableOption "the default ports in the firewall for the WiVRn server";
|
|
|
|
defaultRuntime = mkEnableOption ''
|
|
WiVRn as the default OpenXR runtime on the system.
|
|
The config can be found at `/etc/xdg/openxr/1/active_runtime.json`.
|
|
|
|
Note that applications can bypass this option by setting an active
|
|
runtime in a writable XDG_CONFIG_DIRS location like `~/.config`
|
|
'';
|
|
|
|
autoStart = mkEnableOption "starting the service by default";
|
|
|
|
highPriority = mkEnableOption "high priority capability for asynchronous reprojection";
|
|
|
|
monadoEnvironment = mkOption {
|
|
type = types.attrs;
|
|
description = "Environment variables to be passed to the Monado environment.";
|
|
default = { };
|
|
};
|
|
|
|
extraServerFlags = mkOption {
|
|
type = types.listOf types.str;
|
|
description = "Flags to add to the wivrn service.";
|
|
default = [ ];
|
|
example = literalExpression ''[ "--no-publish-service" ]'';
|
|
};
|
|
|
|
steam = {
|
|
importOXRRuntimes = mkEnableOption ''
|
|
Sets `PRESSURE_VESSEL_IMPORT_OPENXR_1_RUNTIMES` system-wide to allow Steam to automatically discover the WiVRn server.
|
|
|
|
Note that you may have to logout for this variable to be visible
|
|
'';
|
|
|
|
package = mkPackageOption pkgs "steam" { };
|
|
};
|
|
|
|
config = {
|
|
enable = mkEnableOption "configuration for WiVRn";
|
|
json = mkOption {
|
|
type = configFormat.type;
|
|
description = ''
|
|
Configuration for WiVRn. The attributes are serialized to JSON in config.json. The server will fallback to default values for any missing attributes.
|
|
|
|
Like upstream, the application option is a list including the application and it's flags. In the case of the NixOS module however, the first element of the list must be a package. The module will assert otherwise.
|
|
The application can be set to a single package because it gets passed to lib.toList, though this will not allow for flags to be passed.
|
|
|
|
See https://github.com/WiVRn/WiVRn/blob/master/docs/configuration.md
|
|
'';
|
|
default = { };
|
|
example = literalExpression ''
|
|
{
|
|
scale = 0.5;
|
|
bitrate = 100000000;
|
|
encoders = [
|
|
{
|
|
encoder = "nvenc";
|
|
codec = "h264";
|
|
width = 1.0;
|
|
height = 1.0;
|
|
offset_x = 0.0;
|
|
offset_y = 0.0;
|
|
}
|
|
];
|
|
application = [ pkgs.wlx-overlay-s ];
|
|
}
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
assertions = [
|
|
{
|
|
assertion = !applicationCheck || isDerivation applicationAttr;
|
|
message = "The application in WiVRn configuration is not a package. Please ensure that the application is a package or that a package is the first element in the list.";
|
|
}
|
|
];
|
|
|
|
security.wrappers."wivrn-server" = mkIf cfg.highPriority {
|
|
setuid = false;
|
|
owner = "root";
|
|
group = "root";
|
|
capabilities = "cap_sys_nice+eip";
|
|
source = getExe cfg.package;
|
|
};
|
|
|
|
systemd.user = {
|
|
services = {
|
|
wivrn = {
|
|
description = "WiVRn XR runtime service";
|
|
environment = recursiveUpdate {
|
|
# Default options
|
|
# https://gitlab.freedesktop.org/monado/monado/-/blob/598080453545c6bf313829e5780ffb7dde9b79dc/src/xrt/targets/service/monado.in.service#L12
|
|
XRT_COMPOSITOR_LOG = "debug";
|
|
XRT_PRINT_OPTIONS = "on";
|
|
IPC_EXIT_ON_DISCONNECT = "off";
|
|
PRESSURE_VESSEL_IMPORT_OPENXR_1_RUNTIMES = mkIf cfg.steam.importOXRRuntimes "1";
|
|
} cfg.monadoEnvironment;
|
|
serviceConfig = (
|
|
if cfg.highPriority then
|
|
{
|
|
ExecStart = serverExec;
|
|
}
|
|
# Hardening options break high-priority
|
|
else
|
|
{
|
|
ExecStart = serverExec;
|
|
# Hardening options
|
|
CapabilityBoundingSet = [ "CAP_SYS_NICE" ];
|
|
AmbientCapabilities = [ "CAP_SYS_NICE" ];
|
|
LockPersonality = true;
|
|
NoNewPrivileges = true;
|
|
PrivateTmp = true;
|
|
ProtectClock = true;
|
|
ProtectControlGroups = true;
|
|
ProtectKernelLogs = true;
|
|
ProtectKernelModules = true;
|
|
ProtectKernelTunables = true;
|
|
ProtectProc = "invisible";
|
|
ProtectSystem = "strict";
|
|
RemoveIPC = true;
|
|
RestrictNamespaces = true;
|
|
RestrictSUIDSGID = true;
|
|
}
|
|
);
|
|
path = [ cfg.steam.package ];
|
|
wantedBy = mkIf cfg.autoStart [ "default.target" ];
|
|
restartTriggers = [
|
|
cfg.package
|
|
cfg.steam.package
|
|
];
|
|
};
|
|
};
|
|
};
|
|
|
|
services = {
|
|
udev.packages = with pkgs; [ android-udev-rules ];
|
|
avahi = {
|
|
enable = true;
|
|
publish = {
|
|
enable = true;
|
|
userServices = true;
|
|
};
|
|
};
|
|
};
|
|
|
|
networking.firewall = mkIf cfg.openFirewall {
|
|
allowedTCPPorts = [ 9757 ];
|
|
allowedUDPPorts = [ 9757 ];
|
|
};
|
|
|
|
environment = {
|
|
systemPackages = [
|
|
cfg.package
|
|
applicationPackage
|
|
];
|
|
sessionVariables = mkIf cfg.steam.importOXRRuntimes {
|
|
PRESSURE_VESSEL_IMPORT_OPENXR_1_RUNTIMES = "1";
|
|
};
|
|
pathsToLink = [ "/share/openxr" ];
|
|
etc."xdg/openxr/1/active_runtime.json" = mkIf cfg.defaultRuntime {
|
|
source = "${cfg.package}/share/openxr/1/openxr_wivrn.json";
|
|
};
|
|
};
|
|
};
|
|
meta.maintainers = with maintainers; [ passivelemon ];
|
|
}
|