1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9let
10 cfg = config.services.lvm;
11in
12{
13 options.services.lvm = {
14 enable = mkEnableOption "lvm2" // {
15 default = true;
16 description = ''
17 Whether to enable lvm2.
18
19 :::{.note}
20 The lvm2 package contains device-mapper udev rules and without those tools like cryptsetup do not fully function!
21 :::
22 '';
23 };
24
25 package = mkOption {
26 type = types.package;
27 default = pkgs.lvm2;
28 internal = true;
29 defaultText = literalExpression "pkgs.lvm2";
30 description = ''
31 This option allows you to override the LVM package that's used on the system
32 (udev rules, tmpfiles, systemd services).
33 Defaults to pkgs.lvm2, pkgs.lvm2_dmeventd if dmeventd or pkgs.lvm2_vdo if vdo is enabled.
34 '';
35 };
36 dmeventd.enable = mkEnableOption "the LVM dmevent daemon";
37 boot.thin.enable = mkEnableOption "support for booting from ThinLVs";
38 boot.vdo.enable = mkEnableOption "support for booting from VDOLVs";
39 };
40
41 options.boot.initrd.services.lvm.enable = mkEnableOption "booting from LVM2 in the initrd" // {
42 description = ''
43 *This will only be used when systemd is used in stage 1.*
44
45 Whether to enable booting from LVM2 in the initrd.
46 '';
47 default = config.boot.initrd.systemd.enable && config.services.lvm.enable;
48 defaultText = lib.literalExpression "config.boot.initrd.systemd.enable && config.services.lvm.enable";
49 };
50
51 config = mkMerge [
52 ({
53 # minimal configuration file to make lvmconfig/lvm2-activation-generator happy
54 environment.etc."lvm/lvm.conf".text = "config {}";
55 })
56 (mkIf cfg.enable {
57 systemd.tmpfiles.packages = [ cfg.package.out ];
58 environment.systemPackages = [ cfg.package ];
59 systemd.packages = [ cfg.package ];
60
61 services.udev.packages = [ cfg.package.out ];
62 })
63 (mkIf config.boot.initrd.services.lvm.enable {
64 # We need lvm2 for the device-mapper rules
65 boot.initrd.services.udev.packages = [ cfg.package ];
66 # The device-mapper rules want to call tools from lvm2
67 boot.initrd.systemd.initrdBin = [ cfg.package ];
68 boot.initrd.services.udev.binPackages = [ cfg.package ];
69 })
70 (mkIf cfg.dmeventd.enable {
71 systemd.sockets."dm-event".wantedBy = [ "sockets.target" ];
72 systemd.services."lvm2-monitor".wantedBy = [ "sysinit.target" ];
73
74 environment.etc."lvm/lvm.conf".text = ''
75 dmeventd/executable = "${cfg.package}/bin/dmeventd"
76 '';
77 services.lvm.package = mkDefault pkgs.lvm2_dmeventd;
78 })
79 (mkIf cfg.boot.thin.enable {
80 boot.initrd = {
81 kernelModules = [
82 "dm-snapshot"
83 "dm-thin-pool"
84 ];
85
86 systemd.initrdBin = lib.mkIf config.boot.initrd.services.lvm.enable [
87 pkgs.thin-provisioning-tools
88 ];
89
90 extraUtilsCommands = mkIf (!config.boot.initrd.systemd.enable) ''
91 for BIN in ${pkgs.thin-provisioning-tools}/bin/*; do
92 copy_bin_and_libs $BIN
93 done
94 '';
95
96 extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) ''
97 ls ${pkgs.thin-provisioning-tools}/bin/ | grep -v pdata_tools | while read BIN; do
98 $out/bin/$(basename $BIN) --help > /dev/null
99 done
100 '';
101 };
102
103 environment.etc."lvm/lvm.conf".text =
104 concatMapStringsSep "\n"
105 (bin: "global/${bin}_executable = ${pkgs.thin-provisioning-tools}/bin/${bin}")
106 [
107 "thin_check"
108 "thin_dump"
109 "thin_repair"
110 "cache_check"
111 "cache_dump"
112 "cache_repair"
113 ];
114
115 environment.systemPackages = [ pkgs.thin-provisioning-tools ];
116 })
117 (mkIf cfg.boot.vdo.enable {
118 assertions = [
119 {
120 assertion = lib.versionAtLeast config.boot.kernelPackages.kernel.version "6.9";
121 message = "boot.vdo.enable requires at least kernel version 6.9";
122 }
123 ];
124
125 boot = {
126 initrd = {
127 kernelModules = [ "dm-vdo" ];
128
129 systemd.initrdBin = lib.mkIf config.boot.initrd.services.lvm.enable [ pkgs.vdo ];
130
131 extraUtilsCommands = mkIf (!config.boot.initrd.systemd.enable) ''
132 ls ${pkgs.vdo}/bin/ | while read BIN; do
133 copy_bin_and_libs ${pkgs.vdo}/bin/$BIN
134 done
135 substituteInPlace $out/bin/vdorecover --replace "${pkgs.bash}/bin/bash" "/bin/sh"
136 substituteInPlace $out/bin/adaptlvm --replace "${pkgs.bash}/bin/bash" "/bin/sh"
137 '';
138
139 extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) ''
140 ls ${pkgs.vdo}/bin/ | grep -vE '(adaptlvm|vdorecover)' | while read BIN; do
141 $out/bin/$(basename $BIN) --help > /dev/null
142 done
143 '';
144 };
145 };
146
147 services.lvm.package = mkOverride 999 pkgs.lvm2_vdo; # this overrides mkDefault
148
149 environment.systemPackages = [ pkgs.vdo ];
150 })
151 (mkIf (cfg.dmeventd.enable || cfg.boot.thin.enable) {
152 boot.initrd.systemd.contents."/etc/lvm/lvm.conf".text =
153 optionalString (config.boot.initrd.services.lvm.enable && cfg.boot.thin.enable) (
154 concatMapStringsSep "\n" (bin: "global/${bin}_executable = /bin/${bin}") [
155 "thin_check"
156 "thin_dump"
157 "thin_repair"
158 "cache_check"
159 "cache_dump"
160 "cache_repair"
161 ]
162 )
163 + "\n"
164 + optionalString cfg.dmeventd.enable ''
165 dmeventd/executable = /bin/false
166 activation/monitoring = 0
167 '';
168
169 boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) ''
170 mkdir -p /etc/lvm
171 cat << EOF >> /etc/lvm/lvm.conf
172 ${optionalString cfg.boot.thin.enable (
173 concatMapStringsSep "\n" (bin: "global/${bin}_executable = $(command -v ${bin})") [
174 "thin_check"
175 "thin_dump"
176 "thin_repair"
177 "cache_check"
178 "cache_dump"
179 "cache_repair"
180 ]
181 )}
182 ${optionalString cfg.dmeventd.enable ''
183 dmeventd/executable = "$(command -v false)"
184 activation/monitoring = 0
185 ''}
186 EOF
187 '';
188 })
189 ];
190
191}