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