1#! @perl@
2
3use strict;
4use Cwd 'abs_path';
5use File::Spec;
6use File::Path;
7use File::Basename;
8use File::Slurp;
9use File::stat;
10use Config::IniFiles;
11
12umask(0022);
13
14sub uniq {
15 my %seen;
16 my @res = ();
17 foreach my $s (@_) {
18 if (!defined $seen{$s}) {
19 $seen{$s} = 1;
20 push @res, $s;
21 }
22 }
23 return @res;
24}
25
26sub runCommand {
27 my ($cmd) = @_;
28 open FILE, "$cmd 2>&1 |" or die "Failed to execute: $cmd\n";
29 my @ret = <FILE>;
30 close FILE;
31 return ($?, @ret);
32}
33
34# Process the command line.
35my $outDir = "/etc/nixos";
36my $rootDir = ""; # = /
37my $force = 0;
38my $noFilesystems = 0;
39my $flake = 0;
40my $showHardwareConfig = 0;
41my $kernel = "lts";
42
43if (-e "/etc/nixos-generate-config.conf") {
44 my $cfg = new Config::IniFiles -file => "/etc/nixos-generate-config.conf";
45 $outDir = $cfg->val("Defaults", "Directory") // $outDir;
46 if (defined $cfg->val("Defaults", "RootDirectory")) {
47 $rootDir = $cfg->val("Defaults", "RootDirectory");
48 $rootDir =~ s/\/*$//; # remove trailing slashes
49 $rootDir = File::Spec->rel2abs($rootDir); # resolve absolute path
50 }
51 $kernel = $cfg->val("Defaults", "Kernel") // $kernel;
52 $flake = $cfg->val("Defaults", "Flake") // $flake;
53}
54
55for (my $n = 0; $n < scalar @ARGV; $n++) {
56 my $arg = $ARGV[$n];
57 if ($arg eq "--help") {
58 exec "man nixos-generate-config" or die;
59 }
60 elsif ($arg eq "--dir") {
61 $n++;
62 $outDir = $ARGV[$n];
63 die "$0: ‘--dir’ requires an argument\n" unless defined $outDir;
64 }
65 elsif ($arg eq "--root") {
66 $n++;
67 $rootDir = $ARGV[$n];
68 die "$0: ‘--root’ requires an argument\n" unless defined $rootDir;
69 die "$0: no need to specify `/` with `--root`, it is the default\n" if $rootDir eq "/";
70 $rootDir =~ s/\/*$//; # remove trailing slashes
71 $rootDir = File::Spec->rel2abs($rootDir); # resolve absolute path
72 }
73 elsif ($arg eq "--force") {
74 $force = 1;
75 }
76 elsif ($arg eq "--no-filesystems") {
77 $noFilesystems = 1;
78 }
79 elsif ($arg eq "--show-hardware-config") {
80 $showHardwareConfig = 1;
81 }
82 elsif ($arg eq "--flake") {
83 $flake = 1;
84 }
85 elsif ($arg eq "--kernel") {
86 $n++;
87 $kernel = $ARGV[$n];
88 die "$0: ‘--kernel’ requires an argument\n" unless defined $kernel;
89 }
90 else {
91 die "$0: unrecognized argument ‘$arg’\n";
92 }
93}
94
95die "$0: invalid kernel: '$kernel'" unless $kernel eq "lts" || $kernel eq "latest";
96
97my @attrs = ();
98my @kernelModules = ();
99my @initrdKernelModules = ();
100my @initrdAvailableKernelModules = ();
101my @modulePackages = ();
102my @imports;
103
104
105sub debug {
106 return unless defined $ENV{"DEBUG"};
107 print STDERR @_;
108}
109
110
111# nixpkgs.system
112push @attrs, "nixpkgs.hostPlatform = lib.mkDefault \"@hostPlatformSystem@\";";
113
114
115my $cpuinfo = read_file "/proc/cpuinfo";
116
117
118sub hasCPUFeature {
119 my $feature = shift;
120 return $cpuinfo =~ /^flags\s*:.* $feature( |$)/m;
121}
122
123
124sub cpuManufacturer {
125 my $id = shift;
126 return $cpuinfo =~ /^vendor_id\s*:.* $id$/m;
127}
128
129# Virtualization support?
130push @kernelModules, "kvm-intel" if hasCPUFeature "vmx";
131push @kernelModules, "kvm-amd" if hasCPUFeature "svm";
132
133
134# Look at the PCI devices and add necessary modules. Note that most
135# modules are auto-detected so we don't need to list them here.
136# However, some are needed in the initrd to boot the system.
137
138my $videoDriver;
139
140sub pciCheck {
141 my $path = shift;
142 my $vendor = read_file "$path/vendor"; chomp $vendor;
143 my $device = read_file "$path/device"; chomp $device;
144 my $class = read_file "$path/class"; chomp $class;
145
146 my $module;
147 if (-e "$path/driver/module") {
148 $module = basename `readlink -f $path/driver/module`;
149 chomp $module;
150 }
151
152 debug "$path: $vendor $device $class";
153 debug " $module" if defined $module;
154 debug "\n";
155
156 if (defined $module) {
157 # See the bottom of https://pciids.sourceforge.net/pci.ids for
158 # device classes.
159 if (# Mass-storage controller. Definitely important.
160 $class =~ /^0x01/ ||
161
162 # Firewire controller. A disk might be attached.
163 $class =~ /^0x0c00/ ||
164
165 # USB controller. Needed if we want to use the
166 # keyboard when things go wrong in the initrd.
167 $class =~ /^0x0c03/
168 )
169 {
170 push @initrdAvailableKernelModules, $module;
171 }
172 }
173
174 # broadcom FullMac driver
175 # list taken from
176 # https://wireless.wiki.kernel.org/en/users/Drivers/brcm80211#brcmfmac
177 if ($vendor eq "0x14e4" &&
178 ($device eq "0x43a3" || $device eq "0x43df" || $device eq "0x43ec" ||
179 $device eq "0x43d3" || $device eq "0x43d9" || $device eq "0x43e9" ||
180 $device eq "0x43ba" || $device eq "0x43bb" || $device eq "0x43bc" ||
181 $device eq "0xaa52" || $device eq "0x43ca" || $device eq "0x43cb" ||
182 $device eq "0x43cc" || $device eq "0x43c3" || $device eq "0x43c4" ||
183 $device eq "0x43c5"
184 ) )
185 {
186 # we need e.g. brcmfmac43602-pcie.bin
187 push @imports, "(modulesPath + \"/hardware/network/broadcom-43xx.nix\")";
188 }
189
190 # In case this is a virtio scsi device, we need to explicitly make this available.
191 if ($vendor eq "0x1af4" && ($device eq "0x1004" || $device eq "0x1048") ) {
192 push @initrdAvailableKernelModules, "virtio_scsi";
193 }
194
195 # Can't rely on $module here, since the module may not be loaded
196 # due to missing firmware. Ideally we would check modules.pcimap
197 # here.
198 push @attrs, "networking.enableIntel2200BGFirmware = true;" if
199 $vendor eq "0x8086" &&
200 ($device eq "0x1043" || $device eq "0x104f" || $device eq "0x4220" ||
201 $device eq "0x4221" || $device eq "0x4223" || $device eq "0x4224");
202
203 push @attrs, "networking.enableIntel3945ABGFirmware = true;" if
204 $vendor eq "0x8086" &&
205 ($device eq "0x4229" || $device eq "0x4230" ||
206 $device eq "0x4222" || $device eq "0x4227");
207
208 # Assume that all NVIDIA cards are supported by the NVIDIA driver.
209 # There may be exceptions (e.g. old cards).
210 # FIXME: do we want to enable an unfree driver here?
211 #$videoDriver = "nvidia" if $vendor eq "0x10de" && $class =~ /^0x03/;
212}
213
214foreach my $path (glob "/sys/bus/pci/devices/*") {
215 pciCheck $path;
216}
217
218# Idem for USB devices.
219
220sub usbCheck {
221 my $path = shift;
222 my $class = read_file "$path/bInterfaceClass"; chomp $class;
223 my $subclass = read_file "$path/bInterfaceSubClass"; chomp $subclass;
224 my $protocol = read_file "$path/bInterfaceProtocol"; chomp $protocol;
225
226 my $module;
227 if (-e "$path/driver/module") {
228 $module = basename `readlink -f $path/driver/module`;
229 chomp $module;
230 }
231
232 debug "$path: $class $subclass $protocol";
233 debug " $module" if defined $module;
234 debug "\n";
235
236 if (defined $module) {
237 if (# Mass-storage controller. Definitely important.
238 $class eq "08" ||
239
240 # Keyboard. Needed if we want to use the
241 # keyboard when things go wrong in the initrd.
242 ($class eq "03" && $protocol eq "01")
243 )
244 {
245 push @initrdAvailableKernelModules, $module;
246 }
247 }
248}
249
250foreach my $path (glob "/sys/bus/usb/devices/*") {
251 if (-e "$path/bInterfaceClass") {
252 usbCheck $path;
253 }
254}
255
256
257# Add the modules for all block and MMC devices.
258foreach my $path (glob "/sys/class/{block,mmc_host}/*") {
259 my $module;
260 if (-e "$path/device/driver/module") {
261 $module = basename `readlink -f $path/device/driver/module`;
262 chomp $module;
263 push @initrdAvailableKernelModules, $module;
264 }
265}
266
267# Add bcache module, if needed.
268my @bcacheDevices = glob("/dev/bcache*");
269@bcacheDevices = grep(!m#dev/bcachefs.*#, @bcacheDevices);
270if (scalar @bcacheDevices > 0) {
271 push @initrdAvailableKernelModules, "bcache";
272}
273
274# Prevent unbootable systems if LVM snapshots are present at boot time.
275if (`lsblk -o TYPE` =~ "lvm") {
276 push @initrdKernelModules, "dm-snapshot";
277}
278
279my $virt = `@detectvirt@`;
280chomp $virt;
281
282
283# Check if we're a VirtualBox guest. If so, enable the guest
284# additions.
285if ($virt eq "oracle") {
286 push @attrs, "virtualisation.virtualbox.guest.enable = true;"
287}
288
289# Check if we're a Parallels guest. If so, enable the guest additions.
290# It is blocked by https://github.com/systemd/systemd/pull/23859
291if ($virt eq "parallels") {
292 push @attrs, "hardware.parallels.enable = true;";
293 push @attrs, "nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ \"prl-tools\" ];";
294}
295
296# Likewise for QEMU.
297if ($virt eq "qemu" || $virt eq "kvm" || $virt eq "bochs") {
298 push @imports, "(modulesPath + \"/profiles/qemu-guest.nix\")";
299}
300
301# Also for Hyper-V.
302if ($virt eq "microsoft") {
303 push @attrs, "virtualisation.hypervGuest.enable = true;"
304}
305
306
307# Pull in NixOS configuration for containers.
308if ($virt eq "systemd-nspawn") {
309 push @attrs, "boot.isContainer = true;";
310}
311
312
313# Check if we're on bare metal, not in a VM/container.
314if ($virt eq "none") {
315 # Provide firmware for devices that are not detected by this script.
316 push @imports, "(modulesPath + \"/installer/scan/not-detected.nix\")";
317
318 # Update the microcode.
319 push @attrs, "hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;" if cpuManufacturer "AuthenticAMD";
320 push @attrs, "hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;" if cpuManufacturer "GenuineIntel";
321}
322
323# For a device name like /dev/sda1, find a more stable path like
324# /dev/disk/by-uuid/X or /dev/disk/by-label/Y.
325sub findStableDevPath {
326 my ($dev) = @_;
327 return $dev if substr($dev, 0, 1) ne "/";
328 return $dev unless -e $dev;
329
330 my $st = stat($dev) or return $dev;
331
332 foreach my $dev2 (
333 glob("/dev/stratis/*/*"),
334 glob("/dev/mapper/*"),
335 glob("/dev/disk/by-uuid/*"),
336 glob("/dev/disk/by-label/*"),
337 ) {
338 my $st2 = stat($dev2) or next;
339 return $dev2 if $st->rdev == $st2->rdev;
340 }
341
342 return $dev;
343}
344
345push @attrs, "services.xserver.videoDrivers = [ \"$videoDriver\" ];" if $videoDriver;
346
347# Generate the swapDevices option from the currently activated swap
348# devices.
349my @swaps = read_file("/proc/swaps", err_mode => 'carp');
350my @swapDevices;
351if (@swaps) {
352 shift @swaps;
353 foreach my $swap (@swaps) {
354 my @fields = split ' ', $swap;
355 my $swapFilename = $fields[0];
356 my $swapType = $fields[1];
357 next unless -e $swapFilename;
358 my $dev = findStableDevPath $swapFilename;
359 if ($swapType =~ "partition") {
360 # zram devices are more likely created by configuration.nix, so
361 # ignore them here
362 next if ($swapFilename =~ /^\/dev\/zram/);
363 push @swapDevices, "{ device = \"$dev\"; }";
364 } elsif ($swapType =~ "file") {
365 # swap *files* are more likely specified in configuration.nix, so
366 # ignore them here.
367 } else {
368 die "Unsupported swap type: $swapType\n";
369 }
370 }
371}
372
373
374# Generate the fileSystems option from the currently mounted
375# filesystems.
376sub in {
377 my ($d1, $d2) = @_;
378 return $d1 eq $d2 || substr($d1, 0, length($d2) + 1) eq "$d2/";
379}
380
381my $fileSystems;
382my %fsByDev;
383my $useSwraid = 0;
384foreach my $fs (read_file("/proc/self/mountinfo")) {
385 chomp $fs;
386 my @fields = split / /, $fs;
387 my $mountPoint = $fields[4];
388 $mountPoint =~ s/\\040/ /g; # account for mount points with spaces in the name (\040 is the escape character)
389 $mountPoint =~ s/\\011/\t/g; # account for mount points with tabs in the name (\011 is the escape character)
390 next unless -d $mountPoint;
391 my @mountOptions = split /,/, $fields[5];
392
393 next if !in($mountPoint, $rootDir);
394 $mountPoint = substr($mountPoint, length($rootDir)); # strip the root directory (e.g. /mnt)
395 $mountPoint = "/" if $mountPoint eq "";
396
397 # Skip special filesystems.
398 next if in($mountPoint, "/proc") || in($mountPoint, "/dev") || in($mountPoint, "/sys") || in($mountPoint, "/run") || $mountPoint eq "/var/lib/nfs/rpc_pipefs";
399
400 # Skip the optional fields.
401 my $n = 6; $n++ while $fields[$n] ne "-"; $n++;
402 my $fsType = $fields[$n];
403 my $device = $fields[$n + 1];
404 my @superOptions = split /,/, $fields[$n + 2];
405 $device =~ s/\\040/ /g; # account for devices with spaces in the name (\040 is the escape character)
406 $device =~ s/\\011/\t/g; # account for mount points with tabs in the name (\011 is the escape character)
407
408 # Skip the read-only bind-mount on /nix/store.
409 next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions) && (grep { $_ eq "ro" } @mountOptions);
410
411 # Maybe this is a bind-mount of a filesystem we saw earlier?
412 if (defined $fsByDev{$fields[2]}) {
413 # Make sure this isn't a btrfs subvolume.
414 my $msg = `@btrfs@ subvol show $rootDir$mountPoint`;
415 if ($? != 0 || $msg =~ /ERROR:/s) {
416 my $path = $fields[3]; $path = "" if $path eq "/";
417 my $base = $fsByDev{$fields[2]};
418 $base = "" if $base eq "/";
419 $fileSystems .= <<EOF;
420 fileSystems.\"$mountPoint\" =
421 { device = \"$base$path\";
422 fsType = \"none\";
423 options = \[ \"bind\" \];
424 };
425
426EOF
427 next;
428 }
429 }
430 $fsByDev{$fields[2]} = $mountPoint;
431
432 # We don't know how to handle FUSE filesystems.
433 if ($fsType eq "fuseblk" || $fsType eq "fuse") {
434 print STDERR "warning: don't know how to emit ‘fileSystem’ option for FUSE filesystem ‘$mountPoint’\n";
435 next;
436 }
437
438 # Is this a mount of a loopback device?
439 my @extraOptions;
440 if ($device =~ /\/dev\/loop(\d+)/) {
441 my $loopnr = $1;
442 my $backer = read_file "/sys/block/loop$loopnr/loop/backing_file";
443 if (defined $backer) {
444 chomp $backer;
445 $device = $backer;
446 push @extraOptions, "loop";
447 }
448 }
449
450 # Is this a btrfs filesystem?
451 if ($fsType eq "btrfs") {
452 my ($status, @info) = runCommand("@btrfs@ subvol show $rootDir$mountPoint");
453 if ($status != 0 || join("", @info) =~ /ERROR:/) {
454 die "Failed to retrieve subvolume info for $mountPoint\n";
455 }
456 my @ids = join("\n", @info) =~ m/^(?!\/\n).*Subvolume ID:[ \t\n]*([0-9]+)/s;
457 if ($#ids > 0) {
458 die "Btrfs subvol name for $mountPoint listed multiple times in mount\n"
459 } elsif ($#ids == 0) {
460 my @paths = join("", @info) =~ m/^([^\n]*)/;
461 if ($#paths > 0) {
462 die "Btrfs returned multiple paths for a single subvolume id, mountpoint $mountPoint\n";
463 } elsif ($#paths != 0) {
464 die "Btrfs did not return a path for the subvolume at $mountPoint\n";
465 }
466 push @extraOptions, "subvol=$paths[0]";
467 }
468 }
469
470 # Preserve umask (fmask, dmask) settings for vfat filesystems.
471 # (The default is to mount these world-readable, but that's a security risk
472 # for the EFI System Partition.)
473 if ($fsType eq "vfat") {
474 for (@superOptions) {
475 if ($_ =~ /fmask|dmask/) {
476 push @extraOptions, $_;
477 }
478 }
479 }
480
481 # is this a stratis fs?
482 my $stableDevPath = findStableDevPath $device;
483 my $stratisPool;
484 if ($stableDevPath =~ qr#/dev/stratis/(.*)/.*#) {
485 my $poolName = $1;
486 my ($header, @lines) = split "\n", qx/stratis pool list/;
487 my $uuidIndex = index $header, 'UUID';
488 my ($line) = grep /^$poolName /, @lines;
489 $stratisPool = substr $line, $uuidIndex - 32, 36;
490 }
491
492 # Don't emit tmpfs entry for /tmp, because it most likely comes from the
493 # boot.tmp.useTmpfs option in configuration.nix (managed declaratively).
494 next if ($mountPoint eq "/tmp" && $fsType eq "tmpfs");
495
496 # This should work for single and multi-device systems.
497 # still needs subvolume support
498 if ($fsType eq "bcachefs") {
499 my ($status, @info) = runCommand("bcachefs fs usage $rootDir$mountPoint");
500 my $UUID = $info[0];
501
502 if ($status == 0 && $UUID =~ /^Filesystem:[ \t\n]*([0-9a-z-]+)/) {
503 $stableDevPath = "UUID=$1";
504 } else {
505 print STDERR "warning: can't find bcachefs mount UUID falling back to device-path";
506 }
507 }
508
509 # Emit the filesystem.
510 $fileSystems .= <<EOF;
511 fileSystems.\"$mountPoint\" =
512 { device = \"$stableDevPath\";
513 fsType = \"$fsType\";
514EOF
515
516 if (scalar @extraOptions > 0) {
517 $fileSystems .= <<EOF;
518 options = \[ ${\join " ", map { "\"" . $_ . "\"" } uniq(@extraOptions)} \];
519EOF
520 }
521
522 if ($stratisPool) {
523 $fileSystems .= <<EOF;
524 stratis.poolUuid = "$stratisPool";
525EOF
526 }
527
528 $fileSystems .= <<EOF;
529 };
530
531EOF
532
533 # If this filesystem is on a LUKS device, then add a
534 # boot.initrd.luks.devices entry.
535 if (-e $device) {
536 my $deviceName = basename(abs_path($device));
537 my $dmUuid = read_file("/sys/class/block/$deviceName/dm/uuid", err_mode => 'quiet');
538 if ($dmUuid =~ /^CRYPT-LUKS/)
539 {
540 my @slaves = glob("/sys/class/block/$deviceName/slaves/*");
541 if (scalar @slaves == 1) {
542 my $slave = "/dev/" . basename($slaves[0]);
543 if (-e $slave) {
544 my $dmName = read_file("/sys/class/block/$deviceName/dm/name");
545 chomp $dmName;
546 # Ensure to add an entry only once
547 my $luksDevice = " boot.initrd.luks.devices.\"$dmName\".device";
548 if ($fileSystems !~ /^\Q$luksDevice\E/m) {
549 $fileSystems .= "$luksDevice = \"${\(findStableDevPath $slave)}\";\n\n";
550 }
551 }
552 }
553 }
554 if (-e "/sys/class/block/$deviceName/md/uuid") {
555 $useSwraid = 1;
556 }
557 }
558}
559if ($useSwraid) {
560 push @attrs, "boot.swraid.enable = true;\n\n";
561}
562
563
564# Generate the hardware configuration file.
565
566sub toNixStringList {
567 my $res = "";
568 foreach my $s (@_) {
569 $res .= " \"$s\"";
570 }
571 return $res;
572}
573sub toNixList {
574 my $res = "";
575 foreach my $s (@_) {
576 $res .= " $s";
577 }
578 return $res;
579}
580
581sub multiLineList {
582 my $indent = shift;
583 return " [ ]" if !@_;
584 my $res = "\n${indent}[ ";
585 my $first = 1;
586 foreach my $s (@_) {
587 $res .= "$indent " if !$first;
588 $first = 0;
589 $res .= "$s\n";
590 }
591 $res .= "$indent]";
592 return $res;
593}
594
595my $initrdAvailableKernelModules = toNixStringList(uniq @initrdAvailableKernelModules);
596my $initrdKernelModules = toNixStringList(uniq @initrdKernelModules);
597my $kernelModules = toNixStringList(uniq @kernelModules);
598my $modulePackages = toNixList(uniq @modulePackages);
599
600my $fsAndSwap = "";
601if (!$noFilesystems) {
602 $fsAndSwap = "\n$fileSystems ";
603 $fsAndSwap .= "swapDevices =" . multiLineList(" ", @swapDevices) . ";\n";
604}
605
606my $hwConfig = <<EOF;
607# Do not modify this file! It was generated by ‘nixos-generate-config’
608# and may be overwritten by future invocations. Please make changes
609# to /etc/nixos/configuration.nix instead.
610{ config, lib, pkgs, modulesPath, ... }:
611
612{
613 imports =${\multiLineList(" ", @imports)};
614
615 boot.initrd.availableKernelModules = [$initrdAvailableKernelModules ];
616 boot.initrd.kernelModules = [$initrdKernelModules ];
617 boot.kernelModules = [$kernelModules ];
618 boot.extraModulePackages = [$modulePackages ];
619$fsAndSwap
620${\join "", (map { " $_\n" } (uniq @attrs))}}
621EOF
622
623sub generateXserverConfig {
624 my $xserverEnabled = "@xserverEnabled@";
625
626 my $config = "";
627 if ($xserverEnabled eq "1") {
628 $config = <<EOF;
629 # Enable the X11 windowing system.
630 services.xserver.enable = true;
631EOF
632 } else {
633 $config = <<EOF;
634 # Enable the X11 windowing system.
635 # services.xserver.enable = true;
636EOF
637 }
638}
639
640if ($showHardwareConfig) {
641 print STDOUT $hwConfig;
642} else {
643 if ($outDir eq "/etc/nixos") {
644 $outDir = "$rootDir$outDir";
645 } else {
646 $outDir = File::Spec->rel2abs($outDir);
647 $outDir =~ s/\/*$//; # remove trailing slashes
648 }
649
650 my $fn = "$outDir/hardware-configuration.nix";
651 print STDERR "writing $fn...\n";
652 mkpath($outDir, 0, 0755);
653 write_file($fn, $hwConfig);
654
655 $fn = "$outDir/flake.nix";
656 if ($flake) {
657 if ($force || ! -e $fn) {
658 print STDERR "writing $fn...\n";
659 mkpath($outDir, 0, 0755);
660 write_file($fn, <<EOF);
661@flake@
662EOF
663 } else {
664 print STDERR "warning: not overwriting existing $fn\n";
665 }
666 }
667
668 # Generate a basic configuration.nix, unless one already exists.
669 $fn = "$outDir/configuration.nix";
670 if ($force || ! -e $fn) {
671 print STDERR "writing $fn...\n";
672
673 my $bootLoaderConfig = "";
674 if (-e "/sys/firmware/efi/efivars") {
675 $bootLoaderConfig = <<EOF;
676 # Use the systemd-boot EFI boot loader.
677 boot.loader.systemd-boot.enable = true;
678 boot.loader.efi.canTouchEfiVariables = true;
679EOF
680 } elsif (-e "/boot/extlinux") {
681 $bootLoaderConfig = <<EOF;
682 # Use the extlinux boot loader. (NixOS wants to enable GRUB by default)
683 boot.loader.grub.enable = false;
684 # Enables the generation of /boot/extlinux/extlinux.conf
685 boot.loader.generic-extlinux-compatible.enable = true;
686EOF
687 } elsif ($virt ne "systemd-nspawn") {
688 $bootLoaderConfig = <<EOF;
689 # Use the GRUB 2 boot loader.
690 boot.loader.grub.enable = true;
691 # boot.loader.grub.efiSupport = true;
692 # boot.loader.grub.efiInstallAsRemovable = true;
693 # boot.loader.efi.efiSysMountPoint = "/boot/efi";
694 # Define on which hard drive you want to install Grub.
695 # boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only
696EOF
697 }
698
699 if ($kernel eq "latest") {
700 $bootLoaderConfig .= <<EOF;
701
702 # Use latest kernel.
703 boot.kernelPackages = pkgs.linuxPackages_latest;
704EOF
705 }
706
707 my $xserverConfig = generateXserverConfig();
708
709 (my $desktopConfiguration = <<EOF)=~s/^/ /gm;
710@desktopConfiguration@
711EOF
712
713 write_file($fn, <<EOF);
714@configuration@
715EOF
716 print STDERR "For more hardware-specific settings, see https://github.com/NixOS/nixos-hardware.\n"
717 } else {
718 print STDERR "warning: not overwriting existing $fn\n";
719 }
720}
721
722# workaround for a bug in substituteAll