1{ config, lib, options, pkgs, ... }:
2
3with lib;
4
5let
6
7 plymouth = pkgs.plymouth.override {
8 systemd = config.boot.initrd.systemd.package;
9 };
10
11 cfg = config.boot.plymouth;
12 opt = options.boot.plymouth;
13
14 nixosBreezePlymouth = pkgs.plasma5Packages.breeze-plymouth.override {
15 logoFile = cfg.logo;
16 logoName = "nixos";
17 osName = "NixOS";
18 osVersion = config.system.nixos.release;
19 };
20
21 plymouthLogos = pkgs.runCommand "plymouth-logos" { inherit (cfg) logo; } ''
22 mkdir -p $out
23
24 # For themes that are compiled with PLYMOUTH_LOGO_FILE
25 mkdir -p $out/etc/plymouth
26 ln -s $logo $out/etc/plymouth/logo.png
27
28 # Logo for bgrt theme
29 # Note this is technically an abuse of watermark for the bgrt theme
30 # See: https://gitlab.freedesktop.org/plymouth/plymouth/-/issues/95#note_813768
31 mkdir -p $out/share/plymouth/themes/spinner
32 ln -s $logo $out/share/plymouth/themes/spinner/watermark.png
33
34 # Logo for spinfinity theme
35 # See: https://gitlab.freedesktop.org/plymouth/plymouth/-/issues/106
36 mkdir -p $out/share/plymouth/themes/spinfinity
37 ln -s $logo $out/share/plymouth/themes/spinfinity/header-image.png
38 '';
39
40 themesEnv = pkgs.buildEnv {
41 name = "plymouth-themes";
42 paths = [
43 plymouth
44 plymouthLogos
45 ] ++ cfg.themePackages;
46 };
47
48 configFile = pkgs.writeText "plymouthd.conf" ''
49 [Daemon]
50 ShowDelay=0
51 DeviceTimeout=8
52 Theme=${cfg.theme}
53 ${cfg.extraConfig}
54 '';
55
56in
57
58{
59
60 options = {
61
62 boot.plymouth = {
63
64 enable = mkEnableOption "Plymouth boot splash screen";
65
66 font = mkOption {
67 default = "${pkgs.dejavu_fonts.minimal}/share/fonts/truetype/DejaVuSans.ttf";
68 defaultText = literalExpression ''"''${pkgs.dejavu_fonts.minimal}/share/fonts/truetype/DejaVuSans.ttf"'';
69 type = types.path;
70 description = ''
71 Font file made available for displaying text on the splash screen.
72 '';
73 };
74
75 themePackages = mkOption {
76 default = lib.optional (cfg.theme == "breeze") nixosBreezePlymouth;
77 defaultText = literalMD ''
78 A NixOS branded variant of the breeze theme when
79 `config.${opt.theme} == "breeze"`, otherwise
80 `[ ]`.
81 '';
82 type = types.listOf types.package;
83 description = ''
84 Extra theme packages for plymouth.
85 '';
86 };
87
88 theme = mkOption {
89 default = "bgrt";
90 type = types.str;
91 description = ''
92 Splash screen theme.
93 '';
94 };
95
96 logo = mkOption {
97 type = types.path;
98 # Dimensions are 48x48 to match GDM logo
99 default = "${pkgs.nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png";
100 defaultText = literalExpression ''"''${pkgs.nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png"'';
101 example = literalExpression ''
102 pkgs.fetchurl {
103 url = "https://nixos.org/logo/nixos-hires.png";
104 sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
105 }
106 '';
107 description = ''
108 Logo which is displayed on the splash screen.
109 Currently supports PNG file format only.
110 '';
111 };
112
113 extraConfig = mkOption {
114 type = types.lines;
115 default = "";
116 description = ''
117 Literal string to append to `configFile`
118 and the config file generated by the plymouth module.
119 '';
120 };
121
122 };
123
124 };
125
126 config = mkIf cfg.enable {
127
128 boot.kernelParams = [ "splash" ];
129
130 # To be discoverable by systemd.
131 environment.systemPackages = [ plymouth ];
132
133 environment.etc."plymouth/plymouthd.conf".source = configFile;
134 environment.etc."plymouth/plymouthd.defaults".source = "${plymouth}/share/plymouth/plymouthd.defaults";
135 environment.etc."plymouth/logo.png".source = cfg.logo;
136 environment.etc."plymouth/themes".source = "${themesEnv}/share/plymouth/themes";
137 # XXX: Needed because we supply a different set of plugins in initrd.
138 environment.etc."plymouth/plugins".source = "${plymouth}/lib/plymouth";
139
140 systemd.tmpfiles.rules = [
141 "d /run/plymouth 0755 root root 0 -"
142 "L+ /run/plymouth/plymouthd.defaults - - - - /etc/plymouth/plymouthd.defaults"
143 "L+ /run/plymouth/themes - - - - /etc/plymouth/themes"
144 "L+ /run/plymouth/plugins - - - - /etc/plymouth/plugins"
145 ];
146
147 systemd.packages = [ plymouth ];
148
149 systemd.services.plymouth-kexec.wantedBy = [ "kexec.target" ];
150 systemd.services.plymouth-halt.wantedBy = [ "halt.target" ];
151 systemd.services.plymouth-quit-wait.wantedBy = [ "multi-user.target" ];
152 systemd.services.plymouth-quit.wantedBy = [ "multi-user.target" ];
153 systemd.services.plymouth-poweroff.wantedBy = [ "poweroff.target" ];
154 systemd.services.plymouth-reboot.wantedBy = [ "reboot.target" ];
155 systemd.services.plymouth-read-write.wantedBy = [ "sysinit.target" ];
156 systemd.services.systemd-ask-password-plymouth.wantedBy = [ "multi-user.target" ];
157 systemd.paths.systemd-ask-password-plymouth.wantedBy = [ "multi-user.target" ];
158
159 # Prevent Plymouth taking over the screen during system updates.
160 systemd.services.plymouth-start.restartIfChanged = false;
161
162 boot.initrd.systemd = {
163 extraBin.plymouth = "${plymouth}/bin/plymouth"; # for the recovery shell
164 storePaths = [
165 "${lib.getBin config.boot.initrd.systemd.package}/bin/systemd-tty-ask-password-agent"
166 "${plymouth}/bin/plymouthd"
167 "${plymouth}/sbin/plymouthd"
168 ];
169 packages = [ plymouth ]; # systemd units
170 contents = {
171 # Files
172 "/etc/plymouth/plymouthd.conf".source = configFile;
173 "/etc/plymouth/logo.png".source = cfg.logo;
174 "/etc/plymouth/plymouthd.defaults".source = "${plymouth}/share/plymouth/plymouthd.defaults";
175 # Directories
176 "/etc/plymouth/plugins".source = pkgs.runCommand "plymouth-initrd-plugins" {} ''
177 # Check if the actual requested theme is here
178 if [[ ! -d ${themesEnv}/share/plymouth/themes/${cfg.theme} ]]; then
179 echo "The requested theme: ${cfg.theme} is not provided by any of the packages in boot.plymouth.themePackages"
180 exit 1
181 fi
182
183 moduleName="$(sed -n 's,ModuleName *= *,,p' ${themesEnv}/share/plymouth/themes/${cfg.theme}/${cfg.theme}.plymouth)"
184
185 mkdir -p $out/renderers
186 # module might come from a theme
187 cp ${themesEnv}/lib/plymouth/*.so $out
188 cp ${plymouth}/lib/plymouth/renderers/*.so $out/renderers
189 # useless in the initrd, and adds several megabytes to the closure
190 rm $out/renderers/x11.so
191 '';
192 "/etc/plymouth/themes".source = pkgs.runCommand "plymouth-initrd-themes" {} ''
193 # Check if the actual requested theme is here
194 if [[ ! -d ${themesEnv}/share/plymouth/themes/${cfg.theme} ]]; then
195 echo "The requested theme: ${cfg.theme} is not provided by any of the packages in boot.plymouth.themePackages"
196 exit 1
197 fi
198
199 mkdir -p $out/${cfg.theme}
200 cp -r ${themesEnv}/share/plymouth/themes/${cfg.theme}/* $out/${cfg.theme}
201 # Copy more themes if the theme depends on others
202 for theme in $(grep -hRo '/share/plymouth/themes/.*$' $out | xargs -n1 basename); do
203 if [[ -d "${themesEnv}/share/plymouth/themes/$theme" ]]; then
204 if [[ ! -d "$out/$theme" ]]; then
205 echo "Adding dependent theme: $theme"
206 mkdir -p "$out/$theme"
207 cp -r "${themesEnv}/share/plymouth/themes/$theme"/* "$out/$theme"
208 fi
209 else
210 echo "Missing theme dependency: $theme"
211 fi
212 done
213 # Fixup references
214 for theme in $out/*/*.plymouth; do
215 sed -i "s,${builtins.storeDir}/.*/share/plymouth/themes,$out," "$theme"
216 done
217 '';
218
219 # Fonts
220 "/etc/plymouth/fonts".source = pkgs.runCommand "plymouth-initrd-fonts" {} ''
221 mkdir -p $out
222 cp ${cfg.font} $out
223 '';
224 "/etc/fonts/fonts.conf".text = ''
225 <?xml version="1.0"?>
226 <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
227 <fontconfig>
228 <dir>/etc/plymouth/fonts</dir>
229 </fontconfig>
230 '';
231 };
232 # Properly enable units. These are the units that arch copies
233 services = {
234 plymouth-halt.wantedBy = [ "halt.target" ];
235 plymouth-kexec.wantedBy = [ "kexec.target" ];
236 plymouth-poweroff.wantedBy = [ "poweroff.target" ];
237 plymouth-quit-wait.wantedBy = [ "multi-user.target" ];
238 plymouth-quit.wantedBy = [ "multi-user.target" ];
239 plymouth-read-write.wantedBy = [ "sysinit.target" ];
240 plymouth-reboot.wantedBy = [ "reboot.target" ];
241 plymouth-start.wantedBy = [ "initrd-switch-root.target" "sysinit.target" ];
242 plymouth-switch-root-initramfs.wantedBy = [ "halt.target" "kexec.target" "plymouth-switch-root-initramfs.service" "poweroff.target" "reboot.target" ];
243 plymouth-switch-root.wantedBy = [ "initrd-switch-root.target" ];
244 };
245 # Link in runtime files before starting
246 services.plymouth-start.preStart = ''
247 mkdir -p /run/plymouth
248 ln -sf /etc/plymouth/{plymouthd.defaults,themes,plugins} /run/plymouth/
249 '';
250 };
251
252 # Insert required udev rules. We take stage 2 systemd because the udev
253 # rules are only generated when building with logind.
254 boot.initrd.services.udev.packages = [ (pkgs.runCommand "initrd-plymouth-udev-rules" {} ''
255 mkdir -p $out/etc/udev/rules.d
256 cp ${config.systemd.package.out}/lib/udev/rules.d/{70-uaccess,71-seat}.rules $out/etc/udev/rules.d
257 sed -i '/loginctl/d' $out/etc/udev/rules.d/71-seat.rules
258 '') ];
259
260 boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
261 copy_bin_and_libs ${plymouth}/bin/plymouth
262 copy_bin_and_libs ${plymouth}/bin/plymouthd
263
264 # Check if the actual requested theme is here
265 if [[ ! -d ${themesEnv}/share/plymouth/themes/${cfg.theme} ]]; then
266 echo "The requested theme: ${cfg.theme} is not provided by any of the packages in boot.plymouth.themePackages"
267 exit 1
268 fi
269
270 moduleName="$(sed -n 's,ModuleName *= *,,p' ${themesEnv}/share/plymouth/themes/${cfg.theme}/${cfg.theme}.plymouth)"
271
272 mkdir -p $out/lib/plymouth/renderers
273 # module might come from a theme
274 cp ${themesEnv}/lib/plymouth/*.so $out/lib/plymouth
275 cp ${plymouth}/lib/plymouth/renderers/*.so $out/lib/plymouth/renderers
276 # useless in the initrd, and adds several megabytes to the closure
277 rm $out/lib/plymouth/renderers/x11.so
278
279 mkdir -p $out/share/plymouth/themes
280 cp ${plymouth}/share/plymouth/plymouthd.defaults $out/share/plymouth
281
282 # Copy themes into working directory for patching
283 mkdir themes
284
285 # Use -L to copy the directories proper, not the symlinks to them.
286 # Copy all themes because they're not large assets, and bgrt depends on the ImageDir of
287 # the spinner theme.
288 cp -r -L ${themesEnv}/share/plymouth/themes/* themes
289
290 # Patch out any attempted references to the theme or plymouth's themes directory
291 chmod -R +w themes
292 find themes -type f | while read file
293 do
294 sed -i "s,${builtins.storeDir}/.*/share/plymouth/themes,$out/share/plymouth/themes,g" $file
295 done
296
297 # Install themes
298 cp -r themes/* $out/share/plymouth/themes
299
300 # Install logo
301 mkdir -p $out/etc/plymouth
302 cp -r -L ${themesEnv}/etc/plymouth $out/etc
303
304 # Setup font
305 mkdir -p $out/share/fonts
306 cp ${cfg.font} $out/share/fonts
307 mkdir -p $out/etc/fonts
308 cat > $out/etc/fonts/fonts.conf <<EOF
309 <?xml version="1.0"?>
310 <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
311 <fontconfig>
312 <dir>$out/share/fonts</dir>
313 </fontconfig>
314 EOF
315 '';
316
317 boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) ''
318 $out/bin/plymouthd --help >/dev/null
319 $out/bin/plymouth --help >/dev/null
320 '';
321
322 boot.initrd.extraUdevRulesCommands = mkIf (!config.boot.initrd.systemd.enable) ''
323 cp ${config.systemd.package}/lib/udev/rules.d/{70-uaccess,71-seat}.rules $out
324 sed -i '/loginctl/d' $out/71-seat.rules
325 '';
326
327 # We use `mkAfter` to ensure that LUKS password prompt would be shown earlier than the splash screen.
328 boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) (mkAfter ''
329 mkdir -p /etc/plymouth
330 mkdir -p /run/plymouth
331 ln -s $extraUtils/etc/plymouth/logo.png /etc/plymouth/logo.png
332 ln -s ${configFile} /etc/plymouth/plymouthd.conf
333 ln -s $extraUtils/share/plymouth/plymouthd.defaults /run/plymouth/plymouthd.defaults
334 ln -s $extraUtils/share/plymouth/themes /run/plymouth/themes
335 ln -s $extraUtils/lib/plymouth /run/plymouth/plugins
336 ln -s $extraUtils/etc/fonts /etc/fonts
337
338 plymouthd --mode=boot --pid-file=/run/plymouth/pid --attach-to-session
339 plymouth show-splash
340 '');
341
342 boot.initrd.postMountCommands = mkIf (!config.boot.initrd.systemd.enable) ''
343 plymouth update-root-fs --new-root-dir="$targetRoot"
344 '';
345
346 # `mkBefore` to ensure that any custom prompts would be visible.
347 boot.initrd.preFailCommands = mkIf (!config.boot.initrd.systemd.enable) (mkBefore ''
348 plymouth quit --wait
349 '');
350
351 };
352
353}