-
-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
84886ff
commit 97bdae4
Showing
3 changed files
with
229 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
{ | ||
config, | ||
pkgs, | ||
lib, | ||
... | ||
}: | ||
let | ||
inherit (lib) | ||
mkIf | ||
mkEnableOption | ||
mkPackageOption | ||
mkOption | ||
optionalString | ||
optionalAttrs | ||
isDerivation | ||
recursiveUpdate | ||
getExe | ||
literalExpression | ||
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. | ||
# Application can either be a package or a list that has a package as the first element. | ||
applicationExists = builtins.hasAttr "application" cfg.config.json; | ||
applicationListNotEmpty = ( | ||
if builtins.isList cfg.config.json.application then | ||
(builtins.length cfg.config.json.application) != 0 | ||
else | ||
true | ||
); | ||
applicationCheck = applicationExists && applicationListNotEmpty; | ||
|
||
applicationBinary = ( | ||
if builtins.isList cfg.config.json.application then | ||
builtins.head cfg.config.json.application | ||
else | ||
cfg.config.json.application | ||
); | ||
applicationStrings = builtins.tail cfg.config.json.application; | ||
|
||
applicationPath = mkIf applicationCheck applicationBinary; | ||
|
||
applicationConcat = ( | ||
if builtins.isList cfg.config.json.application then | ||
builtins.concatStringsSep " " ([ (getExe applicationBinary) ] ++ applicationStrings) | ||
else | ||
(getExe applicationBinary) | ||
); | ||
applicationUpdate = recursiveUpdate cfg.config.json ( | ||
optionalAttrs applicationCheck { application = applicationConcat; } | ||
); | ||
configFile = configFormat.generate "config.json" applicationUpdate; | ||
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 Monado 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"; | ||
|
||
monadoEnvironment = mkOption { | ||
type = types.attrs; | ||
description = "Environment variables to be passed to the Monado environment."; | ||
default = { | ||
XRT_COMPOSITOR_LOG = "debug"; | ||
XRT_PRINT_OPTIONS = "on"; | ||
IPC_EXIT_ON_DISCONNECT = "off"; | ||
}; | ||
}; | ||
|
||
extraPackages = mkOption { | ||
type = types.listOf types.package; | ||
description = "Packages to add to the wivrn-application service $PATH."; | ||
default = [ ]; | ||
example = literalExpression "[ pkgs.bash pkgs.procps ]"; | ||
}; | ||
|
||
config = { | ||
enable = mkEnableOption "configuration for WiVRn"; | ||
json = mkOption { | ||
type = configFormat.type; | ||
description = '' | ||
Configuration for WiVRn. The attributes are serialized to JSON in config.json. | ||
Note that the application option must be either a package or a | ||
list with package as the first element. | ||
See https://github.com/Meumeu/WiVRn/blob/master/docs/configuration.md | ||
''; | ||
default = { }; | ||
example = literalExpression '' | ||
{ | ||
scale = 0.8; | ||
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 ]; | ||
tcp_only = true; | ||
} | ||
''; | ||
}; | ||
}; | ||
}; | ||
}; | ||
|
||
config = mkIf cfg.enable { | ||
assertions = [ | ||
{ | ||
assertion = !applicationCheck || isDerivation applicationBinary; | ||
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."; | ||
} | ||
]; | ||
|
||
systemd.user = { | ||
services = { | ||
# The WiVRn server runs in a hardened service and starts the applications in a different service | ||
wivrn = { | ||
description = "WiVRn XR runtime service"; | ||
environment = { | ||
# 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"; | ||
} // cfg.monadoEnvironment; | ||
serviceConfig = { | ||
ExecStart = ( | ||
(getExe cfg.package) + " --systemd" + optionalString cfg.config.enable " -f ${configFile}" | ||
); | ||
# 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; | ||
}; | ||
wantedBy = mkIf cfg.autoStart [ "default.target" ]; | ||
restartTriggers = [ | ||
cfg.package | ||
configFile | ||
]; | ||
}; | ||
wivrn-application = mkIf applicationCheck { | ||
description = "WiVRn application service"; | ||
requires = [ "wivrn.service" ]; | ||
serviceConfig = { | ||
ExecStart = ( | ||
(getExe cfg.package) + " --application" + optionalString cfg.config.enable " -f ${configFile}" | ||
); | ||
Restart = "on-failure"; | ||
RestartSec = 0; | ||
PrivateTmp = true; | ||
}; | ||
# We need to add the application to PATH so WiVRn can find it | ||
path = [ applicationPath ] ++ cfg.extraPackages; | ||
}; | ||
}; | ||
}; | ||
|
||
services = { | ||
# WiVRn can be used with some wired headsets so we include xr-hardware | ||
udev.packages = with pkgs; [ | ||
android-udev-rules | ||
xr-hardware | ||
]; | ||
avahi = { | ||
enable = true; | ||
publish = { | ||
enable = true; | ||
userServices = true; | ||
}; | ||
}; | ||
}; | ||
|
||
networking.firewall = mkIf cfg.openFirewall { | ||
allowedTCPPorts = [ 9757 ]; | ||
allowedUDPPorts = [ 9757 ]; | ||
}; | ||
|
||
environment = { | ||
systemPackages = [ | ||
cfg.package | ||
applicationPath | ||
]; | ||
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 ]; | ||
} |