about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services/hardware/udev.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services/hardware/udev.nix')
-rw-r--r--nixpkgs/nixos/modules/services/hardware/udev.nix190
1 files changed, 148 insertions, 42 deletions
diff --git a/nixpkgs/nixos/modules/services/hardware/udev.nix b/nixpkgs/nixos/modules/services/hardware/udev.nix
index 61448af2d33b..1723cb508485 100644
--- a/nixpkgs/nixos/modules/services/hardware/udev.nix
+++ b/nixpkgs/nixos/modules/services/hardware/udev.nix
@@ -8,6 +8,24 @@ let
 
   cfg = config.services.udev;
 
+  initrdUdevRules = pkgs.runCommand "initrd-udev-rules" {} ''
+    mkdir -p $out/etc/udev/rules.d
+    for f in 60-cdrom_id 60-persistent-storage 75-net-description 80-drivers 80-net-setup-link; do
+      ln -s ${config.boot.initrd.systemd.package}/lib/udev/rules.d/$f.rules $out/etc/udev/rules.d
+    done
+  '';
+
+
+  # networkd link files are used early by udev to set up interfaces early.
+  # This must be done in stage 1 to avoid race conditions between udev and
+  # network daemons.
+  # TODO move this into the initrd-network module when it exists
+  initrdLinkUnits = pkgs.runCommand "initrd-link-units" {} ''
+    mkdir -p $out
+    ln -s ${udev}/lib/systemd/network/*.link $out/
+    ${lib.concatMapStringsSep "\n" (file: "ln -s ${file} $out/") (lib.mapAttrsToList (n: v: "${v.unit}/${n}") (lib.filterAttrs (n: _: hasSuffix ".link" n) config.systemd.network.units))}
+  '';
+
   extraUdevRules = pkgs.writeTextFile {
     name = "extra-udev-rules";
     text = cfg.extraRules;
@@ -23,17 +41,16 @@ let
   nixosRules = ''
     # Miscellaneous devices.
     KERNEL=="kvm",                  MODE="0666"
-    KERNEL=="kqemu",                MODE="0666"
 
     # Needed for gpm.
     SUBSYSTEM=="input", KERNEL=="mice", TAG+="systemd"
   '';
 
   # Perform substitutions in all udev rules files.
-  udevRules = pkgs.runCommand "udev-rules"
+  udevRulesFor = { name, udevPackages, udevPath, udev, systemd, binPackages, initrdBin ? null }: pkgs.runCommand name
     { preferLocalBuild = true;
       allowSubstitutes = false;
-      packages = unique (map toString cfg.packages);
+      packages = unique (map toString udevPackages);
     }
     ''
       mkdir -p $out
@@ -61,6 +78,9 @@ let
           --replace \"/bin/mount \"${pkgs.util-linux}/bin/mount \
           --replace /usr/bin/readlink ${pkgs.coreutils}/bin/readlink \
           --replace /usr/bin/basename ${pkgs.coreutils}/bin/basename
+      ${optionalString (initrdBin != null) ''
+        substituteInPlace $i --replace '/run/current-system/systemd' "${removeSuffix "/bin" initrdBin}"
+      ''}
       done
 
       echo -n "Checking that all programs called by relative paths in udev rules exist in ${udev}/lib/udev... "
@@ -85,8 +105,9 @@ let
       for i in $import_progs $run_progs; do
         # if the path refers to /run/current-system/systemd, replace with config.systemd.package
         if [[ $i == /run/current-system/systemd* ]]; then
-          i="${config.systemd.package}/''${i#/run/current-system/systemd/}"
+          i="${systemd}/''${i#/run/current-system/systemd/}"
         fi
+
         if [[ ! -x $i ]]; then
           echo "FAIL"
           echo "$i is called in udev rules but is not executable or does not exist"
@@ -103,7 +124,7 @@ let
         echo "Consider fixing the following udev rules:"
         echo "$filesToFixup" | while read localFile; do
           remoteFile="origin unknown"
-          for i in ${toString cfg.packages}; do
+          for i in ${toString binPackages}; do
             for j in "$i"/*/udev/rules.d/*; do
               [ -e "$out/$(basename "$j")" ] || continue
               [ "$(basename "$j")" = "$(basename "$localFile")" ] || continue
@@ -126,7 +147,7 @@ let
       ${optionalString (!config.boot.hardwareScan) ''
         ln -s /dev/null $out/80-drivers.rules
       ''}
-    ''; # */
+    '';
 
   hwdbBin = pkgs.runCommand "hwdb.bin"
     { preferLocalBuild = true;
@@ -150,6 +171,11 @@ let
       mv etc/udev/hwdb.bin $out
     '';
 
+  compressFirmware = if config.boot.kernelPackages.kernelAtLeast "5.3" then
+    pkgs.compressFirmwareXz
+  else
+    id;
+
   # Udev has a 512-character limit for ENV{PATH}, so create a symlink
   # tree to work around this.
   udevPath = pkgs.buildEnv {
@@ -170,7 +196,7 @@ in
     boot.hardwareScan = mkOption {
       type = types.bool;
       default = true;
-      description = ''
+      description = lib.mdDoc ''
         Whether to try to load kernel modules for all detected hardware.
         Usually this does a good job of providing you with the modules
         you need, but sometimes it can crash the system or cause other
@@ -183,11 +209,11 @@ in
       packages = mkOption {
         type = types.listOf types.path;
         default = [];
-        description = ''
-          List of packages containing <command>udev</command> rules.
+        description = lib.mdDoc ''
+          List of packages containing {command}`udev` rules.
           All files found in
-          <filename><replaceable>pkg</replaceable>/etc/udev/rules.d</filename> and
-          <filename><replaceable>pkg</replaceable>/lib/udev/rules.d</filename>
+          {file}`«pkg»/etc/udev/rules.d` and
+          {file}`«pkg»/lib/udev/rules.d`
           will be included.
         '';
         apply = map getBin;
@@ -202,29 +228,15 @@ in
         '';
       };
 
-      initrdRules = mkOption {
-        default = "";
-        example = ''
-          SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:1D:60:B9:6D:4F", KERNEL=="eth*", NAME="my_fast_network_card"
-        '';
-        type = types.lines;
-        description = ''
-          <command>udev</command> rules to include in the initrd
-          <emphasis>only</emphasis>. They'll be written into file
-          <filename>99-local.rules</filename>. Thus they are read and applied
-          after the essential initrd rules.
-        '';
-      };
-
       extraRules = mkOption {
         default = "";
         example = ''
           ENV{ID_VENDOR_ID}=="046d", ENV{ID_MODEL_ID}=="0825", ENV{PULSE_IGNORE}="1"
         '';
         type = types.lines;
-        description = ''
-          Additional <command>udev</command> rules. They'll be written
-          into file <filename>99-local.rules</filename>. Thus they are
+        description = lib.mdDoc ''
+          Additional {command}`udev` rules. They'll be written
+          into file {file}`99-local.rules`. Thus they are
           read and applied after all other rules.
         '';
       };
@@ -237,9 +249,9 @@ in
             KEYBOARD_KEY_700e2=leftctrl
         '';
         type = types.lines;
-        description = ''
-          Additional <command>hwdb</command> files. They'll be written
-          into file <filename>99-local.hwdb</filename>. Thus they are
+        description = lib.mdDoc ''
+          Additional {command}`hwdb` files. They'll be written
+          into file {file}`99-local.hwdb`. Thus they are
           read after all other files.
         '';
       };
@@ -249,7 +261,7 @@ in
     hardware.firmware = mkOption {
       type = types.listOf types.package;
       default = [];
-      description = ''
+      description = lib.mdDoc ''
         List of packages containing firmware files.  Such files
         will be loaded automatically if the kernel asks for them
         (i.e., when it has detected specific hardware that requires
@@ -260,7 +272,7 @@ in
       '';
       apply = list: pkgs.buildEnv {
         name = "firmware";
-        paths = list;
+        paths = map compressFirmware list;
         pathsToLink = [ "/lib/firmware" ];
         ignoreCollisions = true;
       };
@@ -269,20 +281,65 @@ in
     networking.usePredictableInterfaceNames = mkOption {
       default = true;
       type = types.bool;
-      description = ''
-        Whether to assign <link
-        xlink:href='http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames'>predictable
-        names to network interfaces</link>.  If enabled, interfaces
+      description = lib.mdDoc ''
+        Whether to assign [predictable names to network interfaces](http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames).
+        If enabled, interfaces
         are assigned names that contain topology information
-        (e.g. <literal>wlp3s0</literal>) and thus should be stable
+        (e.g. `wlp3s0`) and thus should be stable
         across reboots.  If disabled, names depend on the order in
         which interfaces are discovered by the kernel, which may
         change randomly across reboots; for instance, you may find
-        <literal>eth0</literal> and <literal>eth1</literal> flipping
+        `eth0` and `eth1` flipping
         unpredictably.
       '';
     };
 
+    boot.initrd.services.udev = {
+
+      packages = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        visible = false;
+        description = ''
+          <emphasis>This will only be used when systemd is used in stage 1.</emphasis>
+
+          List of packages containing <command>udev</command> rules that will be copied to stage 1.
+          All files found in
+          <filename>«pkg»/etc/udev/rules.d</filename> and
+          <filename>«pkg»/lib/udev/rules.d</filename>
+          will be included.
+        '';
+      };
+
+      binPackages = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        visible = false;
+        description = ''
+          <emphasis>This will only be used when systemd is used in stage 1.</emphasis>
+
+          Packages to search for binaries that are referenced by the udev rules in stage 1.
+          This list always contains /bin of the initrd.
+        '';
+        apply = map getBin;
+      };
+
+      rules = mkOption {
+        default = "";
+        example = ''
+          SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:1D:60:B9:6D:4F", KERNEL=="eth*", NAME="my_fast_network_card"
+        '';
+        type = types.lines;
+        description = lib.mdDoc ''
+          {command}`udev` rules to include in the initrd
+          *only*. They'll be written into file
+          {file}`99-local.rules`. Thus they are read and applied
+          after the essential initrd rules.
+        '';
+      };
+
+    };
+
   };
 
 
@@ -298,16 +355,61 @@ in
 
     boot.kernelParams = mkIf (!config.networking.usePredictableInterfaceNames) [ "net.ifnames=0" ];
 
-    boot.initrd.extraUdevRulesCommands = optionalString (cfg.initrdRules != "")
+    boot.initrd.extraUdevRulesCommands = optionalString (!config.boot.initrd.systemd.enable && config.boot.initrd.services.udev.rules != "")
       ''
         cat <<'EOF' > $out/99-local.rules
-        ${cfg.initrdRules}
+        ${config.boot.initrd.services.udev.rules}
         EOF
       '';
 
+    boot.initrd.systemd.additionalUpstreamUnits = [
+      # TODO: "initrd-udevadm-cleanup-db.service" is commented out because of https://github.com/systemd/systemd/issues/12953
+      "systemd-udevd-control.socket"
+      "systemd-udevd-kernel.socket"
+      "systemd-udevd.service"
+      "systemd-udev-settle.service"
+      "systemd-udev-trigger.service"
+    ];
+    boot.initrd.systemd.storePaths = [
+      "${config.boot.initrd.systemd.package}/lib/systemd/systemd-udevd"
+      "${config.boot.initrd.systemd.package}/lib/udev/ata_id"
+      "${config.boot.initrd.systemd.package}/lib/udev/cdrom_id"
+      "${config.boot.initrd.systemd.package}/lib/udev/scsi_id"
+      "${config.boot.initrd.systemd.package}/lib/udev/rules.d"
+    ] ++ map (x: "${x}/bin") config.boot.initrd.services.udev.binPackages;
+
+    # Generate the udev rules for the initrd
+    boot.initrd.systemd.contents = {
+      "/etc/udev/rules.d".source = udevRulesFor {
+        name = "initrd-udev-rules";
+        initrdBin = config.boot.initrd.systemd.contents."/bin".source;
+        udevPackages = config.boot.initrd.services.udev.packages;
+        udevPath = config.boot.initrd.systemd.contents."/bin".source;
+        udev = config.boot.initrd.systemd.package;
+        systemd = config.boot.initrd.systemd.package;
+        binPackages = config.boot.initrd.services.udev.binPackages ++ [ config.boot.initrd.systemd.contents."/bin".source ];
+      };
+      "/etc/systemd/network".source = initrdLinkUnits;
+    };
+    # Insert initrd rules
+    boot.initrd.services.udev.packages = [
+      initrdUdevRules
+      (mkIf (config.boot.initrd.services.udev.rules != "") (pkgs.writeTextFile {
+        name = "initrd-udev-rules";
+        destination = "/etc/udev/rules.d/99-local.rules";
+        text = config.boot.initrd.services.udev.rules;
+      }))
+    ];
+
     environment.etc =
       {
-        "udev/rules.d".source = udevRules;
+        "udev/rules.d".source = udevRulesFor {
+          name = "udev-rules";
+          udevPackages = cfg.packages;
+          systemd = config.systemd.package;
+          binPackages = cfg.packages;
+          inherit udevPath udev;
+        };
         "udev/hwdb.bin".source = hwdbBin;
       };
 
@@ -338,4 +440,8 @@ in
       };
 
   };
+
+  imports = [
+    (mkRenamedOptionModule [ "services" "udev" "initrdRules" ] [ "boot" "initrd" "services" "udev" "rules" ])
+  ];
 }