losslesscut-bin: 3.61.1 -> 3.64.0 (#373227)
[NixPkgs.git] / nixos / modules / services / networking / sunshine.nix
blob2b723b74ce7b2b12942832181362a006658b0f1b
2   config,
3   lib,
4   pkgs,
5   utils,
6   ...
7 }:
8 let
9   inherit (lib)
10     mkEnableOption
11     mkPackageOption
12     mkOption
13     literalExpression
14     mkIf
15     mkDefault
16     types
17     optionals
18     getExe
19     ;
20   inherit (utils) escapeSystemdExecArgs;
21   cfg = config.services.sunshine;
23   # ports used are offset from a single base port, see https://docs.lizardbyte.dev/projects/sunshine/en/latest/about/advanced_usage.html#port
24   generatePorts = port: offsets: map (offset: port + offset) offsets;
25   defaultPort = 47989;
27   appsFormat = pkgs.formats.json { };
28   settingsFormat = pkgs.formats.keyValue { };
30   appsFile = appsFormat.generate "apps.json" cfg.applications;
31   configFile = settingsFormat.generate "sunshine.conf" cfg.settings;
34   options.services.sunshine = with types; {
35     enable = mkEnableOption "Sunshine, a self-hosted game stream host for Moonlight";
36     package = mkPackageOption pkgs "sunshine" { };
37     openFirewall = mkOption {
38       type = bool;
39       default = false;
40       description = ''
41         Whether to automatically open ports in the firewall.
42       '';
43     };
44     capSysAdmin = mkOption {
45       type = bool;
46       default = false;
47       description = ''
48         Whether to give the Sunshine binary CAP_SYS_ADMIN, required for DRM/KMS screen capture.
49       '';
50     };
51     autoStart = mkOption {
52       type = bool;
53       default = true;
54       description = ''
55         Whether sunshine should be started automatically.
56       '';
57     };
58     settings = mkOption {
59       default = { };
60       description = ''
61         Settings to be rendered into the configuration file. If this is set, no configuration is possible from the web UI.
63         See https://docs.lizardbyte.dev/projects/sunshine/en/latest/about/advanced_usage.html#configuration for syntax.
64       '';
65       example = literalExpression ''
66         {
67           sunshine_name = "nixos";
68         }
69       '';
70       type = submodule (settings: {
71         freeformType = settingsFormat.type;
72         options.port = mkOption {
73           type = port;
74           default = defaultPort;
75           description = ''
76             Base port -- others used are offset from this one, see https://docs.lizardbyte.dev/projects/sunshine/en/latest/about/advanced_usage.html#port for details.
77           '';
78         };
79       });
80     };
81     applications = mkOption {
82       default = { };
83       description = ''
84         Configuration for applications to be exposed to Moonlight. If this is set, no configuration is possible from the web UI, and must be by the `settings` option.
85       '';
86       example = literalExpression ''
87         {
88           env = {
89             PATH = "$(PATH):$(HOME)/.local/bin";
90           };
91           apps = [
92             {
93               name = "1440p Desktop";
94               prep-cmd = [
95                 {
96                   do = "''${pkgs.kdePackages.libkscreen}/bin/kscreen-doctor output.DP-4.mode.2560x1440@144";
97                   undo = "''${pkgs.kdePackages.libkscreen}/bin/kscreen-doctor output.DP-4.mode.3440x1440@144";
98                 }
99               ];
100               exclude-global-prep-cmd = "false";
101               auto-detach = "true";
102             }
103           ];
104         }
105       '';
106       type = submodule {
107         options = {
108           env = mkOption {
109             default = { };
110             description = ''
111               Environment variables to be set for the applications.
112             '';
113             type = attrsOf str;
114           };
115           apps = mkOption {
116             default = [ ];
117             description = ''
118               Applications to be exposed to Moonlight.
119             '';
120             type = listOf attrs;
121           };
122         };
123       };
124     };
125   };
127   config = mkIf cfg.enable {
128     services.sunshine.settings.file_apps = mkIf (cfg.applications.apps != [ ]) "${appsFile}";
130     environment.systemPackages = [
131       cfg.package
132     ];
134     networking.firewall = mkIf cfg.openFirewall {
135       allowedTCPPorts = generatePorts cfg.settings.port [
136         (-5)
137         0
138         1
139         21
140       ];
141       allowedUDPPorts = generatePorts cfg.settings.port [
142         9
143         10
144         11
145         13
146         21
147       ];
148     };
150     boot.kernelModules = [ "uinput" ];
152     services.udev.packages = [ cfg.package ];
154     services.avahi = {
155       enable = mkDefault true;
156       publish = {
157         enable = mkDefault true;
158         userServices = mkDefault true;
159       };
160     };
162     security.wrappers.sunshine = mkIf cfg.capSysAdmin {
163       owner = "root";
164       group = "root";
165       capabilities = "cap_sys_admin+p";
166       source = getExe cfg.package;
167     };
169     systemd.user.services.sunshine = {
170       description = "Self-hosted game stream host for Moonlight";
172       wantedBy = mkIf cfg.autoStart [ "graphical-session.target" ];
173       partOf = [ "graphical-session.target" ];
174       wants = [ "graphical-session.target" ];
175       after = [ "graphical-session.target" ];
177       startLimitIntervalSec = 500;
178       startLimitBurst = 5;
180       environment.PATH = lib.mkForce null; # don't use default PATH, needed for tray icon menu links to work
182       serviceConfig = {
183         # only add configFile if an application or a setting other than the default port is set to allow configuration from web UI
184         ExecStart = escapeSystemdExecArgs (
185           [
186             (if cfg.capSysAdmin then "${config.security.wrapperDir}/sunshine" else "${getExe cfg.package}")
187           ]
188           ++ optionals (
189             cfg.applications.apps != [ ]
190             || (builtins.length (builtins.attrNames cfg.settings) > 1 || cfg.settings.port != defaultPort)
191           ) [ "${configFile}" ]
192         );
193         Restart = "on-failure";
194         RestartSec = "5s";
195       };
196     };
197   };