at 16.09-beta 5.8 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3with utils; 4with lib; 5 6let 7 8 swapCfg = {config, options, ...}: { 9 10 options = { 11 12 device = mkOption { 13 example = "/dev/sda3"; 14 type = types.str; 15 description = "Path of the device."; 16 }; 17 18 label = mkOption { 19 example = "swap"; 20 type = types.str; 21 description = '' 22 Label of the device. Can be used instead of <varname>device</varname>. 23 ''; 24 }; 25 26 size = mkOption { 27 default = null; 28 example = 2048; 29 type = types.nullOr types.int; 30 description = '' 31 If this option is set, device is interpreted as the 32 path of a swapfile that will be created automatically 33 with the indicated size (in megabytes). 34 ''; 35 }; 36 37 priority = mkOption { 38 default = null; 39 example = 2048; 40 type = types.nullOr types.int; 41 description = '' 42 Specify the priority of the swap device. Priority is a value between 0 and 32767. 43 Higher numbers indicate higher priority. 44 null lets the kernel choose a priority, which will show up as a negative value. 45 ''; 46 }; 47 48 randomEncryption = mkOption { 49 default = false; 50 type = types.bool; 51 description = '' 52 Encrypt swap device with a random key. This way you won't have a persistent swap device. 53 54 WARNING: Don't try to hibernate when you have at least one swap partition with 55 this option enabled! We have no way to set the partition into which hibernation image 56 is saved, so if your image ends up on an encrypted one you would lose it! 57 58 WARNING #2: Do not use /dev/disk/by-uuid/ or /dev/disk/by-label/ as your swap device 59 when using randomEncryption as the UUIDs and labels will get erased on every boot when 60 the partition is encrypted. Best to use /dev/disk/by-partuuid/ 61 ''; 62 }; 63 64 deviceName = mkOption { 65 type = types.str; 66 internal = true; 67 }; 68 69 realDevice = mkOption { 70 type = types.path; 71 internal = true; 72 }; 73 74 }; 75 76 config = rec { 77 device = mkIf options.label.isDefined 78 "/dev/disk/by-label/${config.label}"; 79 deviceName = lib.replaceChars ["\\"] [""] (escapeSystemdPath config.device); 80 realDevice = if config.randomEncryption then "/dev/mapper/${deviceName}" else config.device; 81 }; 82 83 }; 84 85in 86 87{ 88 89 ###### interface 90 91 options = { 92 93 swapDevices = mkOption { 94 default = []; 95 example = [ 96 { device = "/dev/hda7"; } 97 { device = "/var/swapfile"; } 98 { label = "bigswap"; } 99 ]; 100 description = '' 101 The swap devices and swap files. These must have been 102 initialised using <command>mkswap</command>. Each element 103 should be an attribute set specifying either the path of the 104 swap device or file (<literal>device</literal>) or the label 105 of the swap device (<literal>label</literal>, see 106 <command>mkswap -L</command>). Using a label is 107 recommended. 108 ''; 109 110 type = types.listOf (types.submodule swapCfg); 111 }; 112 113 }; 114 115 config = mkIf ((length config.swapDevices) != 0) { 116 117 system.requiredKernelConfig = with config.lib.kernelConfig; [ 118 (isYes "SWAP") 119 ]; 120 121 # Create missing swapfiles. 122 # FIXME: support changing the size of existing swapfiles. 123 systemd.services = 124 let 125 126 createSwapDevice = sw: 127 assert sw.device != ""; 128 assert !(sw.randomEncryption && lib.hasPrefix "/dev/disk/by-uuid" sw.device); 129 assert !(sw.randomEncryption && lib.hasPrefix "/dev/disk/by-label" sw.device); 130 let realDevice' = escapeSystemdPath sw.realDevice; 131 in nameValuePair "mkswap-${sw.deviceName}" 132 { description = "Initialisation of swap device ${sw.device}"; 133 wantedBy = [ "${realDevice'}.swap" ]; 134 before = [ "${realDevice'}.swap" ]; 135 path = [ pkgs.utillinux ] ++ optional sw.randomEncryption pkgs.cryptsetup; 136 137 script = 138 '' 139 ${optionalString (sw.size != null) '' 140 currentSize=$(( $(stat -c "%s" "${sw.device}" 2>/dev/null || echo 0) / 1024 / 1024 )) 141 if [ "${toString sw.size}" != "$currentSize" ]; then 142 fallocate -l ${toString sw.size}M "${sw.device}" || 143 dd if=/dev/zero of="${sw.device}" bs=1M count=${toString sw.size} 144 if [ "${toString sw.size}" -lt "$currentSize" ]; then 145 truncate --size "${toString sw.size}M" "${sw.device}" 146 fi 147 chmod 0600 ${sw.device} 148 ${optionalString (!sw.randomEncryption) "mkswap ${sw.realDevice}"} 149 fi 150 ''} 151 ${optionalString sw.randomEncryption '' 152 echo "secretkey" | cryptsetup luksFormat --batch-mode ${sw.device} 153 echo "secretkey" | cryptsetup luksOpen ${sw.device} ${sw.deviceName} 154 cryptsetup luksErase --batch-mode ${sw.device} 155 mkswap ${sw.realDevice} 156 ''} 157 ''; 158 159 unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ]; 160 unitConfig.DefaultDependencies = false; # needed to prevent a cycle 161 serviceConfig.Type = "oneshot"; 162 serviceConfig.RemainAfterExit = sw.randomEncryption; 163 serviceConfig.ExecStop = optionalString sw.randomEncryption "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}"; 164 restartIfChanged = false; 165 }; 166 167 in listToAttrs (map createSwapDevice (filter (sw: sw.size != null || sw.randomEncryption) config.swapDevices)); 168 169 }; 170 171}