1#! @perl@
2
3use strict;
4use Cwd 'abs_path';
5use File::Spec;
6use File::Path;
7use File::Basename;
8use File::Slurp;
9use File::stat;
10
11umask(0022);
12
13sub uniq {
14 my %seen;
15 my @res = ();
16 foreach my $s (@_) {
17 if (!defined $seen{$s}) {
18 $seen{$s} = 1;
19 push @res, $s;
20 }
21 }
22 return @res;
23}
24
25sub runCommand {
26 my ($cmd) = @_;
27 open FILE, "$cmd 2>&1 |" or die "Failed to execute: $cmd\n";
28 my @ret = <FILE>;
29 close FILE;
30 return ($?, @ret);
31}
32
33# Process the command line.
34my $outDir = "/etc/nixos";
35my $rootDir = ""; # = /
36my $force = 0;
37my $noFilesystems = 0;
38my $showHardwareConfig = 0;
39
40for (my $n = 0; $n < scalar @ARGV; $n++) {
41 my $arg = $ARGV[$n];
42 if ($arg eq "--help") {
43 exec "man nixos-generate-config" or die;
44 }
45 elsif ($arg eq "--dir") {
46 $n++;
47 $outDir = $ARGV[$n];
48 die "$0: ‘--dir’ requires an argument\n" unless defined $outDir;
49 }
50 elsif ($arg eq "--root") {
51 $n++;
52 $rootDir = $ARGV[$n];
53 die "$0: ‘--root’ requires an argument\n" unless defined $rootDir;
54 die "$0: no need to specify `/` with `--root`, it is the default\n" if $rootDir eq "/";
55 $rootDir =~ s/\/*$//; # remove trailing slashes
56 $rootDir = File::Spec->rel2abs($rootDir); # resolve absolute path
57 }
58 elsif ($arg eq "--force") {
59 $force = 1;
60 }
61 elsif ($arg eq "--no-filesystems") {
62 $noFilesystems = 1;
63 }
64 elsif ($arg eq "--show-hardware-config") {
65 $showHardwareConfig = 1;
66 }
67 else {
68 die "$0: unrecognized argument ‘$arg’\n";
69 }
70}
71
72
73my @attrs = ();
74my @kernelModules = ();
75my @initrdKernelModules = ();
76my @initrdAvailableKernelModules = ();
77my @modulePackages = ();
78my @imports;
79
80
81sub debug {
82 return unless defined $ENV{"DEBUG"};
83 print STDERR @_;
84}
85
86
87# nixpkgs.system
88push @attrs, "nixpkgs.hostPlatform = lib.mkDefault \"@system@\";";
89
90
91my $cpuinfo = read_file "/proc/cpuinfo";
92
93
94sub hasCPUFeature {
95 my $feature = shift;
96 return $cpuinfo =~ /^flags\s*:.* $feature( |$)/m;
97}
98
99
100sub cpuManufacturer {
101 my $id = shift;
102 return $cpuinfo =~ /^vendor_id\s*:.* $id$/m;
103}
104
105
106# Determine CPU governor to use
107if (-e "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors") {
108 my $governors = read_file("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors");
109 # ondemand governor is not available on sandy bridge or later Intel CPUs
110 my @desired_governors = ("ondemand", "powersave");
111 my $e;
112
113 foreach $e (@desired_governors) {
114 if (index($governors, $e) != -1) {
115 last if (push @attrs, "powerManagement.cpuFreqGovernor = lib.mkDefault \"$e\";");
116 }
117 }
118}
119
120
121# Virtualization support?
122push @kernelModules, "kvm-intel" if hasCPUFeature "vmx";
123push @kernelModules, "kvm-amd" if hasCPUFeature "svm";
124
125
126# Look at the PCI devices and add necessary modules. Note that most
127# modules are auto-detected so we don't need to list them here.
128# However, some are needed in the initrd to boot the system.
129
130my $videoDriver;
131
132sub pciCheck {
133 my $path = shift;
134 my $vendor = read_file "$path/vendor"; chomp $vendor;
135 my $device = read_file "$path/device"; chomp $device;
136 my $class = read_file "$path/class"; chomp $class;
137
138 my $module;
139 if (-e "$path/driver/module") {
140 $module = basename `readlink -f $path/driver/module`;
141 chomp $module;
142 }
143
144 debug "$path: $vendor $device $class";
145 debug " $module" if defined $module;
146 debug "\n";
147
148 if (defined $module) {
149 # See the bottom of http://pciids.sourceforge.net/pci.ids for
150 # device classes.
151 if (# Mass-storage controller. Definitely important.
152 $class =~ /^0x01/ ||
153
154 # Firewire controller. A disk might be attached.
155 $class =~ /^0x0c00/ ||
156
157 # USB controller. Needed if we want to use the
158 # keyboard when things go wrong in the initrd.
159 $class =~ /^0x0c03/
160 )
161 {
162 push @initrdAvailableKernelModules, $module;
163 }
164 }
165
166 # broadcom STA driver (wl.ko)
167 # list taken from http://www.broadcom.com/docs/linux_sta/README.txt
168 if ($vendor eq "0x14e4" &&
169 ($device eq "0x4311" || $device eq "0x4312" || $device eq "0x4313" ||
170 $device eq "0x4315" || $device eq "0x4327" || $device eq "0x4328" ||
171 $device eq "0x4329" || $device eq "0x432a" || $device eq "0x432b" ||
172 $device eq "0x432c" || $device eq "0x432d" || $device eq "0x4353" ||
173 $device eq "0x4357" || $device eq "0x4358" || $device eq "0x4359" ||
174 $device eq "0x4331" || $device eq "0x43a0" || $device eq "0x43b1"
175 ) )
176 {
177 push @modulePackages, "config.boot.kernelPackages.broadcom_sta";
178 push @kernelModules, "wl";
179 }
180
181 # broadcom FullMac driver
182 # list taken from
183 # https://wireless.wiki.kernel.org/en/users/Drivers/brcm80211#brcmfmac
184 if ($vendor eq "0x14e4" &&
185 ($device eq "0x43a3" || $device eq "0x43df" || $device eq "0x43ec" ||
186 $device eq "0x43d3" || $device eq "0x43d9" || $device eq "0x43e9" ||
187 $device eq "0x43ba" || $device eq "0x43bb" || $device eq "0x43bc" ||
188 $device eq "0xaa52" || $device eq "0x43ca" || $device eq "0x43cb" ||
189 $device eq "0x43cc" || $device eq "0x43c3" || $device eq "0x43c4" ||
190 $device eq "0x43c5"
191 ) )
192 {
193 # we need e.g. brcmfmac43602-pcie.bin
194 push @imports, "(modulesPath + \"/hardware/network/broadcom-43xx.nix\")";
195 }
196
197 # In case this is a virtio scsi device, we need to explicitly make this available.
198 if ($vendor eq "0x1af4" && ($device eq "0x1004" || $device eq "0x1048") ) {
199 push @initrdAvailableKernelModules, "virtio_scsi";
200 }
201
202 # Can't rely on $module here, since the module may not be loaded
203 # due to missing firmware. Ideally we would check modules.pcimap
204 # here.
205 push @attrs, "networking.enableIntel2200BGFirmware = true;" if
206 $vendor eq "0x8086" &&
207 ($device eq "0x1043" || $device eq "0x104f" || $device eq "0x4220" ||
208 $device eq "0x4221" || $device eq "0x4223" || $device eq "0x4224");
209
210 push @attrs, "networking.enableIntel3945ABGFirmware = true;" if
211 $vendor eq "0x8086" &&
212 ($device eq "0x4229" || $device eq "0x4230" ||
213 $device eq "0x4222" || $device eq "0x4227");
214
215 # Assume that all NVIDIA cards are supported by the NVIDIA driver.
216 # There may be exceptions (e.g. old cards).
217 # FIXME: do we want to enable an unfree driver here?
218 #$videoDriver = "nvidia" if $vendor eq "0x10de" && $class =~ /^0x03/;
219}
220
221foreach my $path (glob "/sys/bus/pci/devices/*") {
222 pciCheck $path;
223}
224
225# Idem for USB devices.
226
227sub usbCheck {
228 my $path = shift;
229 my $class = read_file "$path/bInterfaceClass"; chomp $class;
230 my $subclass = read_file "$path/bInterfaceSubClass"; chomp $subclass;
231 my $protocol = read_file "$path/bInterfaceProtocol"; chomp $protocol;
232
233 my $module;
234 if (-e "$path/driver/module") {
235 $module = basename `readlink -f $path/driver/module`;
236 chomp $module;
237 }
238
239 debug "$path: $class $subclass $protocol";
240 debug " $module" if defined $module;
241 debug "\n";
242
243 if (defined $module) {
244 if (# Mass-storage controller. Definitely important.
245 $class eq "08" ||
246
247 # Keyboard. Needed if we want to use the
248 # keyboard when things go wrong in the initrd.
249 ($class eq "03" && $protocol eq "01")
250 )
251 {
252 push @initrdAvailableKernelModules, $module;
253 }
254 }
255}
256
257foreach my $path (glob "/sys/bus/usb/devices/*") {
258 if (-e "$path/bInterfaceClass") {
259 usbCheck $path;
260 }
261}
262
263
264# Add the modules for all block and MMC devices.
265foreach my $path (glob "/sys/class/{block,mmc_host}/*") {
266 my $module;
267 if (-e "$path/device/driver/module") {
268 $module = basename `readlink -f $path/device/driver/module`;
269 chomp $module;
270 push @initrdAvailableKernelModules, $module;
271 }
272}
273
274# Add bcache module, if needed.
275my @bcacheDevices = glob("/dev/bcache*");
276if (scalar @bcacheDevices > 0) {
277 push @initrdAvailableKernelModules, "bcache";
278}
279
280# Prevent unbootable systems if LVM snapshots are present at boot time.
281if (`lsblk -o TYPE` =~ "lvm") {
282 push @initrdKernelModules, "dm-snapshot";
283}
284
285my $virt = `@detectvirt@`;
286chomp $virt;
287
288
289# Check if we're a VirtualBox guest. If so, enable the guest
290# additions.
291if ($virt eq "oracle") {
292 push @attrs, "virtualisation.virtualbox.guest.enable = true;"
293}
294
295# Check if we're a Parallels guest. If so, enable the guest additions.
296# It is blocked by https://github.com/systemd/systemd/pull/23859
297if ($virt eq "parallels") {
298 push @attrs, "hardware.parallels.enable = true;";
299 push @attrs, "nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ \"prl-tools\" ];";
300}
301
302# Likewise for QEMU.
303if ($virt eq "qemu" || $virt eq "kvm" || $virt eq "bochs") {
304 push @imports, "(modulesPath + \"/profiles/qemu-guest.nix\")";
305}
306
307# Also for Hyper-V.
308if ($virt eq "microsoft") {
309 push @attrs, "virtualisation.hypervGuest.enable = true;"
310}
311
312
313# Pull in NixOS configuration for containers.
314if ($virt eq "systemd-nspawn") {
315 push @attrs, "boot.isContainer = true;";
316}
317
318
319# Check if we're on bare metal, not in a VM/container.
320if ($virt eq "none") {
321 # Provide firmware for devices that are not detected by this script.
322 push @imports, "(modulesPath + \"/installer/scan/not-detected.nix\")";
323
324 # Update the microcode.
325 push @attrs, "hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;" if cpuManufacturer "AuthenticAMD";
326 push @attrs, "hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;" if cpuManufacturer "GenuineIntel";
327}
328
329# For a device name like /dev/sda1, find a more stable path like
330# /dev/disk/by-uuid/X or /dev/disk/by-label/Y.
331sub findStableDevPath {
332 my ($dev) = @_;
333 return $dev if substr($dev, 0, 1) ne "/";
334 return $dev unless -e $dev;
335
336 my $st = stat($dev) or return $dev;
337
338 foreach my $dev2 (glob("/dev/disk/by-uuid/*"), glob("/dev/mapper/*"), glob("/dev/disk/by-label/*")) {
339 my $st2 = stat($dev2) or next;
340 return $dev2 if $st->rdev == $st2->rdev;
341 }
342
343 return $dev;
344}
345
346push @attrs, "services.xserver.videoDrivers = [ \"$videoDriver\" ];" if $videoDriver;
347
348# Generate the swapDevices option from the currently activated swap
349# devices.
350my @swaps = read_file("/proc/swaps", err_mode => 'carp');
351my @swapDevices;
352if (@swaps) {
353 shift @swaps;
354 foreach my $swap (@swaps) {
355 my @fields = split ' ', $swap;
356 my $swapFilename = $fields[0];
357 my $swapType = $fields[1];
358 next unless -e $swapFilename;
359 my $dev = findStableDevPath $swapFilename;
360 if ($swapType =~ "partition") {
361 # zram devices are more likely created by configuration.nix, so
362 # ignore them here
363 next if ($swapFilename =~ /^\/dev\/zram/);
364 push @swapDevices, "{ device = \"$dev\"; }";
365 } elsif ($swapType =~ "file") {
366 # swap *files* are more likely specified in configuration.nix, so
367 # ignore them here.
368 } else {
369 die "Unsupported swap type: $swapType\n";
370 }
371 }
372}
373
374
375# Generate the fileSystems option from the currently mounted
376# filesystems.
377sub in {
378 my ($d1, $d2) = @_;
379 return $d1 eq $d2 || substr($d1, 0, length($d2) + 1) eq "$d2/";
380}
381
382my $fileSystems;
383my %fsByDev;
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 # Don't emit tmpfs entry for /tmp, because it most likely comes from the
471 # boot.tmp.useTmpfs option in configuration.nix (managed declaratively).
472 next if ($mountPoint eq "/tmp" && $fsType eq "tmpfs");
473
474 # Emit the filesystem.
475 $fileSystems .= <<EOF;
476 fileSystems.\"$mountPoint\" =
477 { device = \"${\(findStableDevPath $device)}\";
478 fsType = \"$fsType\";
479EOF
480
481 if (scalar @extraOptions > 0) {
482 $fileSystems .= <<EOF;
483 options = \[ ${\join " ", map { "\"" . $_ . "\"" } uniq(@extraOptions)} \];
484EOF
485 }
486
487 $fileSystems .= <<EOF;
488 };
489
490EOF
491
492 # If this filesystem is on a LUKS device, then add a
493 # boot.initrd.luks.devices entry.
494 if (-e $device) {
495 my $deviceName = basename(abs_path($device));
496 if (-e "/sys/class/block/$deviceName"
497 && read_file("/sys/class/block/$deviceName/dm/uuid", err_mode => 'quiet') =~ /^CRYPT-LUKS/)
498 {
499 my @slaves = glob("/sys/class/block/$deviceName/slaves/*");
500 if (scalar @slaves == 1) {
501 my $slave = "/dev/" . basename($slaves[0]);
502 if (-e $slave) {
503 my $dmName = read_file("/sys/class/block/$deviceName/dm/name");
504 chomp $dmName;
505 # Ensure to add an entry only once
506 my $luksDevice = " boot.initrd.luks.devices.\"$dmName\".device";
507 if ($fileSystems !~ /^\Q$luksDevice\E/m) {
508 $fileSystems .= "$luksDevice = \"${\(findStableDevPath $slave)}\";\n\n";
509 }
510 }
511 }
512 }
513 }
514}
515
516
517# Generate the hardware configuration file.
518
519sub toNixStringList {
520 my $res = "";
521 foreach my $s (@_) {
522 $res .= " \"$s\"";
523 }
524 return $res;
525}
526sub toNixList {
527 my $res = "";
528 foreach my $s (@_) {
529 $res .= " $s";
530 }
531 return $res;
532}
533
534sub multiLineList {
535 my $indent = shift;
536 return " [ ]" if !@_;
537 my $res = "\n${indent}[ ";
538 my $first = 1;
539 foreach my $s (@_) {
540 $res .= "$indent " if !$first;
541 $first = 0;
542 $res .= "$s\n";
543 }
544 $res .= "$indent]";
545 return $res;
546}
547
548my $initrdAvailableKernelModules = toNixStringList(uniq @initrdAvailableKernelModules);
549my $initrdKernelModules = toNixStringList(uniq @initrdKernelModules);
550my $kernelModules = toNixStringList(uniq @kernelModules);
551my $modulePackages = toNixList(uniq @modulePackages);
552
553my $fsAndSwap = "";
554if (!$noFilesystems) {
555 $fsAndSwap = "\n$fileSystems ";
556 $fsAndSwap .= "swapDevices =" . multiLineList(" ", @swapDevices) . ";\n";
557}
558
559my $networkingDhcpConfig = generateNetworkingDhcpConfig();
560
561my $hwConfig = <<EOF;
562# Do not modify this file! It was generated by ‘nixos-generate-config’
563# and may be overwritten by future invocations. Please make changes
564# to /etc/nixos/configuration.nix instead.
565{ config, lib, pkgs, modulesPath, ... }:
566
567{
568 imports =${\multiLineList(" ", @imports)};
569
570 boot.initrd.availableKernelModules = [$initrdAvailableKernelModules ];
571 boot.initrd.kernelModules = [$initrdKernelModules ];
572 boot.kernelModules = [$kernelModules ];
573 boot.extraModulePackages = [$modulePackages ];
574$fsAndSwap
575$networkingDhcpConfig
576${\join "", (map { " $_\n" } (uniq @attrs))}}
577EOF
578
579sub generateNetworkingDhcpConfig {
580 # FIXME disable networking.useDHCP by default when switching to networkd.
581 my $config = <<EOF;
582 # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
583 # (the default) this is the recommended approach. When using systemd-networkd it's
584 # still possible to use this option, but it's recommended to use it in conjunction
585 # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
586 networking.useDHCP = lib.mkDefault true;
587EOF
588
589 foreach my $path (glob "/sys/class/net/*") {
590 my $dev = basename($path);
591 if ($dev ne "lo") {
592 $config .= " # networking.interfaces.$dev.useDHCP = lib.mkDefault true;\n";
593 }
594 }
595
596 return $config;
597}
598
599sub generateXserverConfig {
600 my $xserverEnabled = "@xserverEnabled@";
601
602 my $config = "";
603 if ($xserverEnabled eq "1") {
604 $config = <<EOF;
605 # Enable the X11 windowing system.
606 services.xserver.enable = true;
607EOF
608 } else {
609 $config = <<EOF;
610 # Enable the X11 windowing system.
611 # services.xserver.enable = true;
612EOF
613 }
614}
615
616if ($showHardwareConfig) {
617 print STDOUT $hwConfig;
618} else {
619 if ($outDir eq "/etc/nixos") {
620 $outDir = "$rootDir$outDir";
621 } else {
622 $outDir = File::Spec->rel2abs($outDir);
623 $outDir =~ s/\/*$//; # remove trailing slashes
624 }
625
626 my $fn = "$outDir/hardware-configuration.nix";
627 print STDERR "writing $fn...\n";
628 mkpath($outDir, 0, 0755);
629 write_file($fn, $hwConfig);
630
631 # Generate a basic configuration.nix, unless one already exists.
632 $fn = "$outDir/configuration.nix";
633 if ($force || ! -e $fn) {
634 print STDERR "writing $fn...\n";
635
636 my $bootLoaderConfig = "";
637 if (-e "/sys/firmware/efi/efivars") {
638 $bootLoaderConfig = <<EOF;
639 # Use the systemd-boot EFI boot loader.
640 boot.loader.systemd-boot.enable = true;
641 boot.loader.efi.canTouchEfiVariables = true;
642EOF
643 } elsif (-e "/boot/extlinux") {
644 $bootLoaderConfig = <<EOF;
645 # Use the extlinux boot loader. (NixOS wants to enable GRUB by default)
646 boot.loader.grub.enable = false;
647 # Enables the generation of /boot/extlinux/extlinux.conf
648 boot.loader.generic-extlinux-compatible.enable = true;
649EOF
650 } elsif ($virt ne "systemd-nspawn") {
651 $bootLoaderConfig = <<EOF;
652 # Use the GRUB 2 boot loader.
653 boot.loader.grub.enable = true;
654 # boot.loader.grub.efiSupport = true;
655 # boot.loader.grub.efiInstallAsRemovable = true;
656 # boot.loader.efi.efiSysMountPoint = "/boot/efi";
657 # Define on which hard drive you want to install Grub.
658 # boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only
659EOF
660 }
661
662 my $networkingDhcpConfig = generateNetworkingDhcpConfig();
663
664 my $xserverConfig = generateXserverConfig();
665
666 (my $desktopConfiguration = <<EOF)=~s/^/ /gm;
667@desktopConfiguration@
668EOF
669
670 write_file($fn, <<EOF);
671@configuration@
672EOF
673 print STDERR "For more hardware-specific settings, see https://github.com/NixOS/nixos-hardware.\n"
674 } else {
675 print STDERR "warning: not overwriting existing $fn\n";
676 }
677}
678
679# workaround for a bug in substituteAll