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