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