grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / virtualisation / xen-dom0.nix
blob2cc82c910110928255a97595fc7c892f85209acc
1 # Xen hypervisor (Dom0) support.
4   config,
5   lib,
6   pkgs,
7   ...
8 }:
10 let
11   cfg = config.virtualisation.xen;
13   xenBootBuilder = pkgs.writeShellApplication {
14     name = "xenBootBuilder";
15     runtimeInputs =
16       (with pkgs; [
17         binutils
18         coreutils
19         findutils
20         gawk
21         gnugrep
22         gnused
23         jq
24       ])
25       ++ lib.lists.optionals (cfg.efi.bootBuilderVerbosity == "info") (
26         with pkgs;
27         [
28           bat
29           diffutils
30         ]
31       );
32     runtimeEnv = {
33       efiMountPoint = config.boot.loader.efi.efiSysMountPoint;
34     };
36     # We disable SC2016 because we don't want to expand the regexes in the sed commands.
37     excludeShellChecks = [ "SC2016" ];
39     text = builtins.readFile ./xen-boot-builder.sh;
40   };
44   imports = with lib.modules; [
45     (mkRemovedOptionModule
46       [
47         "virtualisation"
48         "xen"
49         "bridge"
50         "name"
51       ]
52       "The Xen Network Bridge options are currently unavailable. Please set up your own bridge manually."
53     )
54     (mkRemovedOptionModule
55       [
56         "virtualisation"
57         "xen"
58         "bridge"
59         "address"
60       ]
61       "The Xen Network Bridge options are currently unavailable. Please set up your own bridge manually."
62     )
63     (mkRemovedOptionModule
64       [
65         "virtualisation"
66         "xen"
67         "bridge"
68         "prefixLength"
69       ]
70       "The Xen Network Bridge options are currently unavailable. Please set up your own bridge manually."
71     )
72     (mkRemovedOptionModule
73       [
74         "virtualisation"
75         "xen"
76         "bridge"
77         "forwardDns"
78       ]
79       "The Xen Network Bridge options are currently unavailable. Please set up your own bridge manually."
80     )
81     (mkRenamedOptionModule
82       [
83         "virtualisation"
84         "xen"
85         "qemu-package"
86       ]
87       [
88         "virtualisation"
89         "xen"
90         "qemu"
91         "package"
92       ]
93     )
94     (mkRenamedOptionModule
95       [
96         "virtualisation"
97         "xen"
98         "package-qemu"
99       ]
100       [
101         "virtualisation"
102         "xen"
103         "qemu"
104         "package"
105       ]
106     )
107     (mkRenamedOptionModule
108       [
109         "virtualisation"
110         "xen"
111         "stored"
112       ]
113       [
114         "virtualisation"
115         "xen"
116         "store"
117         "path"
118       ]
119     )
120   ];
122   ## Interface ##
124   options.virtualisation.xen = {
126     enable = lib.options.mkEnableOption "the Xen Hypervisor, a virtualisation technology defined as a *type-1 hypervisor*, which allows multiple virtual machines, known as *domains*, to run concurrently on the physical machine. NixOS runs as the privileged *Domain 0*. This option requires a reboot into a Xen kernel to take effect";
128     debug = lib.options.mkEnableOption "Xen debug features for Domain 0. This option enables some hidden debugging tests and features, and should not be used in production";
130     trace = lib.options.mkOption {
131       type = lib.types.bool;
132       default = cfg.debug;
133       defaultText = lib.options.literalExpression "false";
134       example = true;
135       description = "Whether to enable Xen debug tracing and logging for Domain 0.";
136     };
138     package = lib.options.mkOption {
139       type = lib.types.package;
140       default = pkgs.xen;
141       defaultText = lib.options.literalExpression "pkgs.xen";
142       example = lib.options.literalExpression "pkgs.xen-slim";
143       description = ''
144         The package used for Xen Hypervisor.
145       '';
146       relatedPackages = [
147         "xen"
148         "xen-slim"
149       ];
150     };
152     qemu = {
153       package = lib.options.mkOption {
154         type = lib.types.package;
155         default = pkgs.xen;
156         defaultText = lib.options.literalExpression "pkgs.xen";
157         example = lib.options.literalExpression "pkgs.qemu_xen";
158         description = ''
159           The package with QEMU binaries that runs in Domain 0
160           and virtualises the unprivileged domains.
161         '';
162         relatedPackages = [
163           "xen"
164           {
165             name = "qemu_xen";
166             comment = "For use with `pkgs.xen-slim`.";
167           }
168         ];
169       };
170       pidFile = lib.options.mkOption {
171         type = lib.types.path;
172         default = "/run/xen/qemu-dom0.pid";
173         example = "/var/run/xen/qemu-dom0.pid";
174         description = "Path to the QEMU PID file.";
175       };
176     };
178     bootParams = lib.options.mkOption {
179       default = [ ];
180       example = ''
181         [
182           "iommu=force:true,qinval:true,debug:true"
183           "noreboot=true"
184           "vga=ask"
185         ]
186       '';
187       type = lib.types.listOf lib.types.str;
188       description = ''
189         Xen Command Line parameters passed to Domain 0 at boot time.
190         Note: these are different from `boot.kernelParams`. See
191         the [Xen documentation](https://xenbits.xenproject.org/docs/unstable/misc/xen-command-line.html) for more information.
192       '';
193     };
195     efi = {
196       bootBuilderVerbosity = lib.options.mkOption {
197         type = lib.types.enum [
198           "default"
199           "info"
200           "debug"
201           "quiet"
202         ];
203         default = "default";
204         example = "info";
205         description = ''
206           The EFI boot entry builder script should be called with exactly one of the following arguments in order to specify its verbosity:
208           - `quiet` supresses all messages.
210           - `default` adds a simple "Installing Xen Hypervisor boot entries...done." message to the script.
212           - `info` is the same as `default`, but it also prints a diff with information on which generations were altered.
213             - This option adds two extra dependencies to the script: `diffutils` and `bat`.
215           - `debug` prints information messages for every single step of the script.
217           This option does not alter the actual functionality of the script, just the number of messages printed when rebuilding the system.
218         '';
219       };
221       path = lib.options.mkOption {
222         type = lib.types.path;
223         default = "${cfg.package.boot}/${cfg.package.efi}";
224         defaultText = lib.options.literalExpression "\${config.virtualisation.xen.package.boot}/\${config.virtualisation.xen.package.efi}";
225         example = lib.options.literalExpression "\${config.virtualisation.xen.package}/boot/efi/efi/nixos/xen-\${config.virtualisation.xen.package.version}.efi";
226         description = ''
227           Path to xen.efi. `pkgs.xen` is patched to install the xen.efi file
228           on `$boot/boot/xen.efi`, but an unpatched Xen build may install it
229           somewhere else, such as `$out/boot/efi/efi/nixos/xen.efi`. Unless
230           you're building your own Xen derivation, you should leave this
231           option as the default value.
232         '';
233       };
234     };
236     dom0Resources = {
237       maxVCPUs = lib.options.mkOption {
238         default = 0;
239         example = 4;
240         type = lib.types.ints.unsigned;
241         description = ''
242           Amount of virtual CPU cores allocated to Domain 0 on boot.
243           If set to 0, all cores are assigned to Domain 0, and
244           unprivileged domains will compete with Domain 0 for CPU time.
245         '';
246       };
248       memory = lib.options.mkOption {
249         default = 0;
250         example = 512;
251         type = lib.types.ints.unsigned;
252         description = ''
253           Amount of memory (in MiB) allocated to Domain 0 on boot.
254           If set to 0, all memory is assigned to Domain 0, and
255           unprivileged domains will compete with Domain 0 for free RAM.
256         '';
257       };
259       maxMemory = lib.options.mkOption {
260         default = cfg.dom0Resources.memory;
261         defaultText = lib.options.literalExpression "config.virtualisation.xen.dom0Resources.memory";
262         example = 1024;
263         type = lib.types.ints.unsigned;
264         description = ''
265           Maximum amount of memory (in MiB) that Domain 0 can
266           dynamically allocate to itself. Does nothing if set
267           to the same amount as virtualisation.xen.memory, or
268           if that option is set to 0.
269         '';
270       };
271     };
273     domains = {
274       extraConfig = lib.options.mkOption {
275         type = lib.types.lines;
276         default = "";
277         example = ''
278           XENDOMAINS_SAVE=/persist/xen/save
279           XENDOMAINS_RESTORE=false
280           XENDOMAINS_CREATE_USLEEP=10000000
281         '';
282         description = ''
283           Options defined here will override the defaults for xendomains.
284           The default options can be seen in the file included from
285           /etc/default/xendomains.
286         '';
287       };
288     };
290     store = {
291       path = lib.options.mkOption {
292         type = lib.types.path;
293         default = "${cfg.package}/bin/oxenstored";
294         defaultText = lib.options.literalExpression "\${config.virtualisation.xen.package}/bin/oxenstored";
295         example = lib.options.literalExpression "\${config.virtualisation.xen.package}/bin/xenstored";
296         description = ''
297           Path to the Xen Store Daemon. This option is useful to
298           switch between the legacy C-based Xen Store Daemon, and
299           the newer OCaml-based Xen Store Daemon, `oxenstored`.
300         '';
301       };
302       type = lib.options.mkOption {
303         type = lib.types.enum [
304           "c"
305           "ocaml"
306         ];
307         default = if (lib.strings.hasSuffix "oxenstored" cfg.store.path) then "ocaml" else "c";
308         internal = true;
309         readOnly = true;
310         description = "Helper internal option that determines the type of the Xen Store Daemon based on cfg.store.path.";
311       };
312       settings = lib.options.mkOption {
313         default = { };
314         example = {
315           enableMerge = false;
316           quota.maxWatchEvents = 2048;
317           quota.enable = true;
318           conflict.maxHistorySeconds = 0.12;
319           conflict.burstLimit = 15.0;
320           xenstored.log.file = "/dev/null";
321           xenstored.log.level = "info";
322         };
323         description = ''
324           The OCaml-based Xen Store Daemon configuration. This
325           option does nothing with the C-based `xenstored`.
326         '';
327         type = lib.types.submodule {
328           options = {
329             pidFile = lib.options.mkOption {
330               default = "/run/xen/xenstored.pid";
331               example = "/var/run/xen/xenstored.pid";
332               type = lib.types.path;
333               description = "Path to the Xen Store Daemon PID file.";
334             };
335             testEAGAIN = lib.options.mkOption {
336               default = cfg.debug;
337               defaultText = lib.options.literalExpression "config.virtualisation.xen.debug";
338               example = true;
339               type = lib.types.bool;
340               visible = false;
341               description = "Randomly fail a transaction with EAGAIN. This option is used for debugging purposes only.";
342             };
343             enableMerge = lib.options.mkOption {
344               default = true;
345               example = false;
346               type = lib.types.bool;
347               description = "Whether to enable transaction merge support.";
348             };
349             conflict = {
350               burstLimit = lib.options.mkOption {
351                 default = 5.0;
352                 example = 15.0;
353                 type = lib.types.addCheck (
354                   lib.types.float
355                   // {
356                     name = "nonnegativeFloat";
357                     description = "nonnegative floating point number, meaning >=0";
358                     descriptionClass = "nonRestrictiveClause";
359                   }
360                 ) (n: n >= 0);
361                 description = ''
362                   Limits applied to domains whose writes cause other domains' transaction
363                   commits to fail. Must include decimal point.
365                   The burst limit is the number of conflicts a domain can cause to
366                   fail in a short period; this value is used for both the initial and
367                   the maximum value of each domain's conflict-credit, which falls by
368                   one point for each conflict caused, and when it reaches zero the
369                   domain's requests are ignored.
370                 '';
371               };
372               maxHistorySeconds = lib.options.mkOption {
373                 default = 5.0e-2;
374                 example = 1.0;
375                 type = lib.types.addCheck (
376                   lib.types.float // { description = "nonnegative floating point number, meaning >=0"; }
377                 ) (n: n >= 0);
378                 description = ''
379                   Limits applied to domains whose writes cause other domains' transaction
380                   commits to fail. Must include decimal point.
382                   The conflict-credit is replenished over time:
383                   one point is issued after each conflict.maxHistorySeconds, so this
384                   is the minimum pause-time during which a domain will be ignored.
385                 '';
386               };
387               rateLimitIsAggregate = lib.options.mkOption {
388                 default = true;
389                 example = false;
390                 type = lib.types.bool;
391                 description = ''
392                   If the conflict.rateLimitIsAggregate option is `true`, then after each
393                   tick one point of conflict-credit is given to just one domain: the
394                   one at the front of the queue. If `false`, then after each tick each
395                   domain gets a point of conflict-credit.
397                   In environments where it is known that every transaction will
398                   involve a set of nodes that is writable by at most one other domain,
399                   then it is safe to set this aggregate limit flag to `false` for better
400                   performance. (This can be determined by considering the layout of
401                   the xenstore tree and permissions, together with the content of the
402                   transactions that require protection.)
404                   A transaction which involves a set of nodes which can be modified by
405                   multiple other domains can suffer conflicts caused by any of those
406                   domains, so the flag must be set to `true`.
407                 '';
408               };
409             };
410             perms = {
411               enable = lib.options.mkOption {
412                 default = true;
413                 example = false;
414                 type = lib.types.bool;
415                 description = "Whether to enable the node permission system.";
416               };
417               enableWatch = lib.options.mkOption {
418                 default = true;
419                 example = false;
420                 type = lib.types.bool;
421                 description = ''
422                   Whether to enable the watch permission system.
424                   When this is set to `true`, unprivileged guests can only get watch events
425                   for xenstore entries that they would've been able to read.
427                   When this is set to `false`, unprivileged guests may get watch events
428                   for xenstore entries that they cannot read. The watch event contains
429                   only the entry name, not the value.
430                   This restores behaviour prior to [XSA-115](https://xenbits.xenproject.org/xsa/advisory-115.html).
431                 '';
432               };
433             };
434             quota = {
435               enable = lib.options.mkOption {
436                 default = true;
437                 example = false;
438                 type = lib.types.bool;
439                 description = "Whether to enable the quota system.";
440               };
441               maxEntity = lib.options.mkOption {
442                 default = 1000;
443                 example = 1024;
444                 type = lib.types.ints.positive;
445                 description = "Entity limit for transactions.";
446               };
447               maxSize = lib.options.mkOption {
448                 default = 2048;
449                 example = 4096;
450                 type = lib.types.ints.positive;
451                 description = "Size limit for transactions.";
452               };
453               maxWatch = lib.options.mkOption {
454                 default = 100;
455                 example = 256;
456                 type = lib.types.ints.positive;
457                 description = "Maximum number of watches by the Xenstore Watchdog.";
458               };
459               transaction = lib.options.mkOption {
460                 default = 10;
461                 example = 50;
462                 type = lib.types.ints.positive;
463                 description = "Maximum number of transactions.";
464               };
465               maxRequests = lib.options.mkOption {
466                 default = 1024;
467                 example = 1024;
468                 type = lib.types.ints.positive;
469                 description = "Maximum number of requests per transaction.";
470               };
471               maxPath = lib.options.mkOption {
472                 default = 1024;
473                 example = 1024;
474                 type = lib.types.ints.positive;
475                 description = "Path limit for the quota system.";
476               };
477               maxOutstanding = lib.options.mkOption {
478                 default = 1024;
479                 example = 1024;
480                 type = lib.types.ints.positive;
481                 description = "Maximum outstanding requests, i.e. in-flight requests / domain.";
482               };
483               maxWatchEvents = lib.options.mkOption {
484                 default = 1024;
485                 example = 2048;
486                 type = lib.types.ints.positive;
487                 description = "Maximum number of outstanding watch events per watch.";
488               };
489             };
490             persistent = lib.options.mkOption {
491               default = false;
492               example = true;
493               type = lib.types.bool;
494               description = "Whether to activate the filed base backend.";
495             };
496             xenstored = {
497               log = {
498                 file = lib.options.mkOption {
499                   default = "/var/log/xen/xenstored.log";
500                   example = "/dev/null";
501                   type = lib.types.path;
502                   description = "Path to the Xen Store log file.";
503                 };
504                 level = lib.options.mkOption {
505                   default = if cfg.trace then "debug" else null;
506                   defaultText = lib.options.literalExpression "if (config.virtualisation.xen.trace == true) then \"debug\" else null";
507                   example = "error";
508                   type = lib.types.nullOr (
509                     lib.types.enum [
510                       "debug"
511                       "info"
512                       "warn"
513                       "error"
514                     ]
515                   );
516                   description = "Logging level for the Xen Store.";
517                 };
518                 # The hidden options below have no upstream documentation whatsoever.
519                 # The nb* options appear to alter the log rotation behaviour, and
520                 # the specialOps option appears to affect the Xenbus logging logic.
521                 nbFiles = lib.options.mkOption {
522                   default = 10;
523                   example = 16;
524                   type = lib.types.int;
525                   visible = false;
526                   description = "Set `xenstored-log-nb-files`.";
527                 };
528               };
529               accessLog = {
530                 file = lib.options.mkOption {
531                   default = "/var/log/xen/xenstored-access.log";
532                   example = "/var/log/security/xenstored-access.log";
533                   type = lib.types.path;
534                   description = "Path to the Xen Store access log file.";
535                 };
536                 nbLines = lib.options.mkOption {
537                   default = 13215;
538                   example = 16384;
539                   type = lib.types.int;
540                   visible = false;
541                   description = "Set `access-log-nb-lines`.";
542                 };
543                 nbChars = lib.options.mkOption {
544                   default = 180;
545                   example = 256;
546                   type = lib.types.int;
547                   visible = false;
548                   description = "Set `acesss-log-nb-chars`.";
549                 };
550                 specialOps = lib.options.mkOption {
551                   default = false;
552                   example = true;
553                   type = lib.types.bool;
554                   visible = false;
555                   description = "Set `access-log-special-ops`.";
556                 };
557               };
558               xenfs = {
559                 kva = lib.options.mkOption {
560                   default = "/proc/xen/xsd_kva";
561                   example = cfg.store.settings.xenstored.xenfs.kva;
562                   type = lib.types.path;
563                   visible = false;
564                   description = ''
565                     Path to the Xen Store Daemon KVA location inside the XenFS pseudo-filesystem.
566                     While it is possible to alter this value, some drivers may be hardcoded to follow the default paths.
567                   '';
568                 };
569                 port = lib.options.mkOption {
570                   default = "/proc/xen/xsd_port";
571                   example = cfg.store.settings.xenstored.xenfs.port;
572                   type = lib.types.path;
573                   visible = false;
574                   description = ''
575                     Path to the Xen Store Daemon userspace port inside the XenFS pseudo-filesystem.
576                     While it is possible to alter this value, some drivers may be hardcoded to follow the default paths.
577                   '';
578                 };
579               };
580             };
581             ringScanInterval = lib.options.mkOption {
582               default = 20;
583               example = 30;
584               type = lib.types.addCheck (
585                 lib.types.int
586                 // {
587                   name = "nonzeroInt";
588                   description = "nonzero signed integer, meaning !=0";
589                   descriptionClass = "nonRestrictiveClause";
590                 }
591               ) (n: n != 0);
592               description = ''
593                 Perodic scanning for all the rings as a safenet for lazy clients.
594                 Define the interval in seconds; set to a negative integer to disable.
595               '';
596             };
597           };
598         };
599       };
600     };
601   };
603   ## Implementation ##
605   config = lib.modules.mkIf cfg.enable {
606     assertions = [
607       {
608         assertion = pkgs.stdenv.hostPlatform.isx86_64;
609         message = "Xen is currently not supported on ${pkgs.stdenv.hostPlatform.system}.";
610       }
611       {
612         assertion =
613           config.boot.loader.systemd-boot.enable
614           || (config.boot ? lanzaboote) && config.boot.lanzaboote.enable;
615         message = "Xen only supports booting on systemd-boot or Lanzaboote.";
616       }
617       {
618         assertion = config.boot.initrd.systemd.enable;
619         message = "Xen does not support the legacy script-based Stage 1 initrd.";
620       }
621       {
622         assertion = cfg.dom0Resources.maxMemory >= cfg.dom0Resources.memory;
623         message = ''
624           You have allocated more memory to dom0 than virtualisation.xen.dom0Resources.maxMemory
625           allows for. Please increase the maximum memory limit, or decrease the default memory allocation.
626         '';
627       }
628       {
629         assertion = cfg.debug -> cfg.trace;
630         message = "Xen's debugging features are enabled, but logging is disabled. This is most likely not what you want.";
631       }
632       {
633         assertion = cfg.store.settings.quota.maxWatchEvents >= cfg.store.settings.quota.maxOutstanding;
634         message = ''
635           Upstream Xen recommends that maxWatchEvents be equal to or greater than maxOutstanding,
636           in order to mitigate denial of service attacks from malicious frontends.
637         '';
638       }
639     ];
641     virtualisation.xen.bootParams =
642       lib.lists.optionals cfg.trace [
643         "loglvl=all"
644         "guest_loglvl=all"
645       ]
646       ++
647         lib.lists.optional (cfg.dom0Resources.memory != 0)
648           "dom0_mem=${toString cfg.dom0Resources.memory}M${
649             lib.strings.optionalString (
650               cfg.dom0Resources.memory != cfg.dom0Resources.maxMemory
651             ) ",max:${toString cfg.dom0Resources.maxMemory}M"
652           }"
653       ++ lib.lists.optional (
654         cfg.dom0Resources.maxVCPUs != 0
655       ) "dom0_max_vcpus=${toString cfg.dom0Resources.maxVCPUs}";
657     boot = {
658       kernelModules = [
659         "xen-evtchn"
660         "xen-gntdev"
661         "xen-gntalloc"
662         "xen-blkback"
663         "xen-netback"
664         "xen-pciback"
665         "evtchn"
666         "gntdev"
667         "netbk"
668         "blkbk"
669         "xen-scsibk"
670         "usbbk"
671         "pciback"
672         "xen-acpi-processor"
673         "blktap2"
674         "tun"
675         "netxen_nic"
676         "xen_wdt"
677         "xen-acpi-processor"
678         "xen-privcmd"
679         "xen-scsiback"
680         "xenfs"
681       ];
683       # The xenfs module is needed to mount /proc/xen.
684       initrd.kernelModules = [ "xenfs" ];
686       # Increase the number of loopback devices from the default (8),
687       # which is way too small because every VM virtual disk requires a
688       # loopback device.
689       extraModprobeConfig = ''
690         options loop max_loop=64
691       '';
693       # Xen Bootspec extension. This extension allows NixOS bootloaders to
694       # fetch the `xen.efi` path and access the `cfg.bootParams` option.
695       bootspec.extensions = {
696         "org.xenproject.bootspec.v1" = {
697           xen = cfg.efi.path;
698           xenParams = cfg.bootParams;
699         };
700       };
702       # See the `xenBootBuilder` script in the main `let...in` statement of this file.
703       loader.systemd-boot.extraInstallCommands = ''
704         ${lib.meta.getExe xenBootBuilder} ${cfg.efi.bootBuilderVerbosity}
705       '';
706     };
708     # Domain 0 requires a pvops-enabled kernel.
709     # All NixOS kernels come with this enabled by default; this is merely a sanity check.
710     system.requiredKernelConfig = with config.lib.kernelConfig; [
711       (isYes "XEN")
712       (isYes "X86_IO_APIC")
713       (isYes "ACPI")
714       (isYes "XEN_DOM0")
715       (isYes "PCI_XEN")
716       (isYes "XEN_DEV_EVTCHN")
717       (isYes "XENFS")
718       (isYes "XEN_COMPAT_XENFS")
719       (isYes "XEN_SYS_HYPERVISOR")
720       (isYes "XEN_GNTDEV")
721       (isYes "XEN_BACKEND")
722       (isModule "XEN_NETDEV_BACKEND")
723       (isModule "XEN_BLKDEV_BACKEND")
724       (isModule "XEN_PCIDEV_BACKEND")
725       (isYes "XEN_BALLOON")
726       (isYes "XEN_SCRUB_PAGES")
727     ];
729     environment = {
730       systemPackages = [
731         cfg.package
732         cfg.qemu.package
733       ];
734       etc =
735         # Set up Xen Domain 0 configuration files.
736         {
737           "xen/xl.conf".source = "${cfg.package}/etc/xen/xl.conf"; # TODO: Add options to configure xl.conf declaratively. It's worth considering making a new "xl value" type, as it could be reused to produce xl.cfg (domain definition) files.
738           "xen/scripts-xen" = {
739             source = "${cfg.package}/etc/xen/scripts/*";
740             target = "xen/scripts";
741           };
742           "default/xencommons".text = ''
743             source ${cfg.package}/etc/default/xencommons
745             XENSTORED="${cfg.store.path}"
746             QEMU_XEN="${cfg.qemu.package}/${cfg.qemu.package.qemu-system-i386}"
747             ${lib.strings.optionalString cfg.trace ''
748               XENSTORED_TRACE=yes
749               XENCONSOLED_TRACE=all
750             ''}
751           '';
752           "default/xendomains".text = ''
753             source ${cfg.package}/etc/default/xendomains
755             ${cfg.domains.extraConfig}
756           '';
757         }
758         # The OCaml-based Xen Store Daemon requires /etc/xen/oxenstored.conf to start.
759         // lib.attrsets.optionalAttrs (cfg.store.type == "ocaml") {
760           "xen/oxenstored.conf".text = ''
761             pid-file = ${cfg.store.settings.pidFile}
762             test-eagain = ${lib.trivial.boolToString cfg.store.settings.testEAGAIN}
763             merge-activate = ${toString cfg.store.settings.enableMerge}
764             conflict-burst-limit = ${toString cfg.store.settings.conflict.burstLimit}
765             conflict-max-history-seconds = ${toString cfg.store.settings.conflict.maxHistorySeconds}
766             conflict-rate-limit-is-aggregate = ${toString cfg.store.settings.conflict.rateLimitIsAggregate}
767             perms-activate = ${toString cfg.store.settings.perms.enable}
768             perms-watch-activate = ${toString cfg.store.settings.perms.enableWatch}
769             quota-activate = ${toString cfg.store.settings.quota.enable}
770             quota-maxentity = ${toString cfg.store.settings.quota.maxEntity}
771             quota-maxsize = ${toString cfg.store.settings.quota.maxSize}
772             quota-maxwatch = ${toString cfg.store.settings.quota.maxWatch}
773             quota-transaction = ${toString cfg.store.settings.quota.transaction}
774             quota-maxrequests = ${toString cfg.store.settings.quota.maxRequests}
775             quota-path-max = ${toString cfg.store.settings.quota.maxPath}
776             quota-maxoutstanding = ${toString cfg.store.settings.quota.maxOutstanding}
777             quota-maxwatchevents = ${toString cfg.store.settings.quota.maxWatchEvents}
778             persistent = ${lib.trivial.boolToString cfg.store.settings.persistent}
779             xenstored-log-file = ${cfg.store.settings.xenstored.log.file}
780             xenstored-log-level = ${
781               if isNull cfg.store.settings.xenstored.log.level then
782                 "null"
783               else
784                 cfg.store.settings.xenstored.log.level
785             }
786             xenstored-log-nb-files = ${toString cfg.store.settings.xenstored.log.nbFiles}
787             access-log-file = ${cfg.store.settings.xenstored.accessLog.file}
788             access-log-nb-lines = ${toString cfg.store.settings.xenstored.accessLog.nbLines}
789             acesss-log-nb-chars = ${toString cfg.store.settings.xenstored.accessLog.nbChars}
790             access-log-special-ops = ${lib.trivial.boolToString cfg.store.settings.xenstored.accessLog.specialOps}
791             ring-scan-interval = ${toString cfg.store.settings.ringScanInterval}
792             xenstored-kva = ${cfg.store.settings.xenstored.xenfs.kva}
793             xenstored-port = ${cfg.store.settings.xenstored.xenfs.port}
794           '';
795         };
796     };
798     # Xen provides udev rules.
799     services.udev.packages = [ cfg.package ];
801     systemd = {
802       # Xen provides systemd units.
803       packages = [ cfg.package ];
805       mounts = [
806         {
807           description = "Mount /proc/xen files";
808           what = "xenfs";
809           where = "/proc/xen";
810           type = "xenfs";
811           unitConfig = {
812             ConditionPathExists = "/proc/xen";
813             RefuseManualStop = "true";
814           };
815         }
816       ];
818       services = {
820         # While this service is installed by the `xen` package, it shouldn't be used in dom0.
821         xendriverdomain.enable = false;
823         xenstored = {
824           wantedBy = [ "multi-user.target" ];
825           preStart = ''
826             export XENSTORED_ROOTDIR="/var/lib/xenstored"
827             rm -f "$XENSTORED_ROOTDIR"/tdb* &>/dev/null
828             mkdir -p /var/{run,log,lib}/xen
829           '';
830         };
832         xen-init-dom0 = {
833           restartIfChanged = false;
834           wantedBy = [ "multi-user.target" ];
835         };
837         xen-qemu-dom0-disk-backend = {
838           wantedBy = [ "multi-user.target" ];
839           serviceConfig = {
840             PIDFile = cfg.qemu.pidFile;
841             ExecStart = ''
842               ${cfg.qemu.package}/${cfg.qemu.package.qemu-system-i386} \
843               -xen-domid 0 -xen-attach -name dom0 -nographic -M xenpv \
844               -daemonize -monitor /dev/null -serial /dev/null -parallel \
845               /dev/null -nodefaults -no-user-config -pidfile \
846               ${cfg.qemu.pidFile}
847             '';
848           };
849         };
851         xenconsoled.wantedBy = [ "multi-user.target" ];
853         xen-watchdog = {
854           wantedBy = [ "multi-user.target" ];
855           serviceConfig = {
856             RestartSec = "1";
857             Restart = "on-failure";
858           };
859         };
861         xendomains = {
862           restartIfChanged = false;
863           path = [
864             cfg.package
865             cfg.qemu.package
866           ];
867           preStart = "mkdir -p /var/lock/subsys -m 755";
868           wantedBy = [ "multi-user.target" ];
869         };
870       };
871     };
872   };
873   meta.maintainers = with lib.maintainers; [ sigmasquadron ];