at 15.09-beta 8.6 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 inherit (pkgs) stdenv writeText procps; 8 9 udev = config.systemd.package; 10 11 cfg = config.services.udev; 12 13 extraUdevRules = pkgs.writeTextFile { 14 name = "extra-udev-rules"; 15 text = cfg.extraRules; 16 destination = "/etc/udev/rules.d/10-local.rules"; 17 }; 18 19 nixosRules = '' 20 # Miscellaneous devices. 21 KERNEL=="kvm", MODE="0666" 22 KERNEL=="kqemu", MODE="0666" 23 24 # Needed for gpm. 25 SUBSYSTEM=="input", KERNEL=="mice", TAG+="systemd" 26 ''; 27 28 # Perform substitutions in all udev rules files. 29 udevRules = stdenv.mkDerivation { 30 name = "udev-rules"; 31 32 preferLocalBuild = true; 33 allowSubstitutes = false; 34 35 buildCommand = '' 36 mkdir -p $out 37 shopt -s nullglob 38 set +o pipefail 39 40 # Set a reasonable $PATH for programs called by udev rules. 41 echo 'ENV{PATH}="${udevPath}/bin:${udevPath}/sbin"' > $out/00-path.rules 42 43 # Add the udev rules from other packages. 44 for i in ${toString cfg.packages}; do 45 echo "Adding rules for package $i" 46 for j in $i/{etc,lib}/udev/rules.d/*; do 47 echo "Copying $j to $out/$(basename $j)" 48 cat $j > $out/$(basename $j) 49 done 50 done 51 52 # Fix some paths in the standard udev rules. Hacky. 53 for i in $out/*.rules; do 54 substituteInPlace $i \ 55 --replace \"/sbin/modprobe \"${config.system.sbin.modprobe}/sbin/modprobe \ 56 --replace \"/sbin/mdadm \"${pkgs.mdadm}/sbin/mdadm \ 57 --replace \"/sbin/blkid \"${pkgs.utillinux}/sbin/blkid \ 58 --replace \"/bin/mount \"${pkgs.utillinux}/bin/mount 59 done 60 61 echo -n "Checking that all programs called by relative paths in udev rules exist in ${udev}/lib/udev... " 62 import_progs=$(grep 'IMPORT{program}="[^/$]' $out/* | 63 sed -e 's/.*IMPORT{program}="\([^ "]*\)[ "].*/\1/' | uniq) 64 run_progs=$(grep -v '^[[:space:]]*#' $out/* | grep 'RUN+="[^/$]' | 65 sed -e 's/.*RUN+="\([^ "]*\)[ "].*/\1/' | uniq) 66 for i in $import_progs $run_progs; do 67 if [[ ! -x ${pkgs.udev}/lib/udev/$i && ! $i =~ socket:.* ]]; then 68 echo "FAIL" 69 echo "$i is called in udev rules but not installed by udev" 70 exit 1 71 fi 72 done 73 echo "OK" 74 75 echo -n "Checking that all programs called by absolute paths in udev rules exist... " 76 import_progs=$(grep 'IMPORT{program}="\/' $out/* | 77 sed -e 's/.*IMPORT{program}="\([^ "]*\)[ "].*/\1/' | uniq) 78 run_progs=$(grep -v '^[[:space:]]*#' $out/* | grep 'RUN+="/' | 79 sed -e 's/.*RUN+="\([^ "]*\)[ "].*/\1/' | uniq) 80 for i in $import_progs $run_progs; do 81 if [[ ! -x $i ]]; then 82 echo "FAIL" 83 echo "$i is called in udev rules but not installed by udev" 84 exit 1 85 fi 86 done 87 echo "OK" 88 89 echo "Consider fixing the following udev rules:" 90 for i in ${toString cfg.packages}; do 91 grep -l '\(RUN+\|IMPORT{program}\)="\(/usr\)\?/s\?bin' $i/*/udev/rules.d/* || true 92 done 93 94 ${optionalString config.networking.usePredictableInterfaceNames '' 95 cp ${./80-net-setup-link.rules} $out/80-net-setup-link.rules 96 ''} 97 98 # If auto-configuration is disabled, then remove 99 # udev's 80-drivers.rules file, which contains rules for 100 # automatically calling modprobe. 101 ${optionalString (!config.boot.hardwareScan) '' 102 ln -s /dev/null $out/80-drivers.rules 103 ''} 104 ''; # */ 105 }; 106 107 # Udev has a 512-character limit for ENV{PATH}, so create a symlink 108 # tree to work around this. 109 udevPath = pkgs.buildEnv { 110 name = "udev-path"; 111 paths = cfg.path; 112 pathsToLink = [ "/bin" "/sbin" ]; 113 ignoreCollisions = true; 114 }; 115 116in 117 118{ 119 120 ###### interface 121 122 options = { 123 124 boot.hardwareScan = mkOption { 125 type = types.bool; 126 default = true; 127 description = '' 128 Whether to try to load kernel modules for all detected hardware. 129 Usually this does a good job of providing you with the modules 130 you need, but sometimes it can crash the system or cause other 131 nasty effects. 132 ''; 133 }; 134 135 services.udev = { 136 137 packages = mkOption { 138 type = types.listOf types.path; 139 default = []; 140 description = '' 141 List of packages containing <command>udev</command> rules. 142 All files found in 143 <filename><replaceable>pkg</replaceable>/etc/udev/rules.d</filename> and 144 <filename><replaceable>pkg</replaceable>/lib/udev/rules.d</filename> 145 will be included. 146 ''; 147 }; 148 149 path = mkOption { 150 type = types.listOf types.path; 151 default = []; 152 description = '' 153 Packages added to the <envar>PATH</envar> environment variable when 154 executing programs from Udev rules. 155 ''; 156 }; 157 158 extraRules = mkOption { 159 default = ""; 160 example = '' 161 KERNEL=="eth*", ATTR{address}=="00:1D:60:B9:6D:4F", NAME="my_fast_network_card" 162 ''; 163 type = types.lines; 164 description = '' 165 Additional <command>udev</command> rules. They'll be written 166 into file <filename>10-local.rules</filename>. Thus they are 167 read before all other rules. 168 ''; 169 }; 170 171 }; 172 173 hardware.firmware = mkOption { 174 type = types.listOf types.package; 175 default = []; 176 description = '' 177 List of packages containing firmware files. Such files 178 will be loaded automatically if the kernel asks for them 179 (i.e., when it has detected specific hardware that requires 180 firmware to function). If multiple packages contain firmware 181 files with the same name, the first package in the list takes 182 precedence. Note that you must rebuild your system if you add 183 files to any of these directories. For quick testing, 184 put firmware files in <filename>/root/test-firmware</filename> 185 and add that directory to the list. 186 ''; 187 apply = list: pkgs.buildEnv { 188 name = "firmware"; 189 paths = list; 190 pathsToLink = [ "/lib/firmware" ]; 191 ignoreCollisions = true; 192 }; 193 }; 194 195 networking.usePredictableInterfaceNames = mkOption { 196 default = true; 197 type = types.bool; 198 description = '' 199 Whether to assign <link 200 xlink:href='http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames'>predictable 201 names to network interfaces</link>. If enabled, interfaces 202 are assigned names that contain topology information 203 (e.g. <literal>wlp3s0</literal>) and thus should be stable 204 across reboots. If disabled, names depend on the order in 205 which interfaces are discovered by the kernel, which may 206 change randomly across reboots; for instance, you may find 207 <literal>eth0</literal> and <literal>eth1</literal> flipping 208 unpredictably. 209 ''; 210 }; 211 212 }; 213 214 215 ###### implementation 216 217 config = mkIf (!config.boot.isContainer) { 218 219 services.udev.extraRules = nixosRules; 220 221 services.udev.packages = [ extraUdevRules ]; 222 223 services.udev.path = [ pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.utillinux udev ]; 224 225 environment.etc = 226 [ { source = udevRules; 227 target = "udev/rules.d"; 228 } 229 ]; 230 231 system.requiredKernelConfig = with config.lib.kernelConfig; [ 232 (isEnabled "UNIX") 233 (isYes "INOTIFY_USER") 234 (isYes "NET") 235 ]; 236 237 boot.extraModprobeConfig = "options firmware_class path=${config.hardware.firmware}/lib/firmware"; 238 239 system.activationScripts.udevd = 240 '' 241 # The deprecated hotplug uevent helper is not used anymore 242 if [ -e /proc/sys/kernel/hotplug ]; then 243 echo "" > /proc/sys/kernel/hotplug 244 fi 245 246 # Regenerate the hardware database /var/lib/udev/hwdb.bin 247 # whenever systemd changes. 248 if [ ! -e /var/lib/udev/prev-systemd -o "$(readlink /var/lib/udev/prev-systemd)" != ${config.systemd.package} ]; then 249 echo "regenerating udev hardware database..." 250 ${config.systemd.package}/bin/udevadm hwdb --update && ln -sfn ${config.systemd.package} /var/lib/udev/prev-systemd 251 fi 252 253 # Allow the kernel to find our firmware. 254 if [ -e /sys/module/firmware_class/parameters/path ]; then 255 echo -n "${config.hardware.firmware}/lib/firmware" > /sys/module/firmware_class/parameters/path 256 fi 257 ''; 258 259 systemd.services.systemd-udevd = 260 { environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules"; 261 }; 262 263 }; 264}