How to make a script run after every initrd regeneration?

Similarly to Ubuntu’s /etc/initramfs/post-update.d, but executed by dracut or rpm.
It should be run only for the default kernel/initrd combination.
I need it to create a unified kernel image automatically after updates.

The initramfs is generated by /usr/lib/kernel/install.d/50-dracut.install. So if you want a script to run after that, you would need to place your script in /etc/kernel/install.d and name it so that it will be ordered later in the execution sequence (e.g. 51-whatever.install). Have a look at /usr/bin/kernel-install (its a Bash script) if you want to see more details about when and how the scripts are called.

P.S. If you want your custom script run instead of the normal 50-dracut.install (which it sounds like you might), you could give your script the same name but place it under /etc/kernel/install.d (i.e. /etc/kernel/install.d/50-dracut.install). But be careful with that. If your script were to fail, your system would be left without an initramfs for whatever version of the kernel was just installed.

Just FYI if you are curious, the following lines from the kernel-install script contain the logic that causes files by the same name under /etc/kernel/install.d to override those under /usr/lib/kernel/install.d.

    for f in "${files[@]}"; do
        for d in "$@"; do
            if [[ -e "$d/$f" ]]; then
                echo "$d/$f"
                continue 2
            fi
        done
    done

The d alternates between /etc/kernel/install.d and /usr/lib/kernel/install.d and the continue 2 command skips the to the next item in the outer (2nd from the perspective of the continue command) for loop. The above code is in the dropindirs_sort function which is called by the following lines in the kernel-install script.

readarray -t PLUGINS <<<"$(
    dropindirs_sort ".install" \
        "/etc/kernel/install.d" \
        "/usr/lib/kernel/install.d"
)"

I’m sure that was more detail than you needed but I was feeling generous. :slightly_smiling_face:

1 Like

Thanks. How about regenerating the initramfs without installing a new kernel? For example remove plymouth or change crypttab, which runs in the initramfs stage? Will that trigger scripts in these directories?

I’m not quite sure what you are asking. If the plymouth and/or crypttab rpms contain hooks to call kernel-install when they are installed/removed, then yes, your script would be called as well. I think you would have to inspect the pre/post execution scripts within those particular rpms to be sure.

Again, just FYI, these are the lines of code from the current kernel.spec file that cause the kernel-install script to be run whenever a Fedora Linux kernel rpm is installed. Other rpms (or scripts) could make a similar call as part of their run. But I don’t which if any do.

# This macro defines a %%posttrans script for a kernel package.
#	%%kernel_variant_posttrans [<subpackage>]
# More text can follow to go at the end of this variant's %%post.
#
%define kernel_variant_posttrans() \
%{expand:%%posttrans %{?1:%{1}-}core}\
%if 0%{!?fedora:1}\
if [ -x %{_sbindir}/weak-modules ]\
then\
    %{_sbindir}/weak-modules --add-kernel %{KVERREL}%{?1:+%{1}} || exit $?\
fi\
%endif\
rm -f %{_localstatedir}/lib/rpm-state/%{name}/installing_core_%{KVERREL}%{?1:+%{1}}\
/bin/kernel-install add %{KVERREL}%{?1:+%{1}} /lib/modules/%{KVERREL}%{?1:+%{1}}/vmlinuz || exit $?\
if [[ ! -e "/boot/symvers-%{KVERREL}%{?1:+%{1}}.gz" ]]; then\
    ln -s "/lib/modules/%{KVERREL}%{?1:+%{1}}/symvers.gz" "/boot/symvers-%{KVERREL}%{?1:+%{1}}.gz"\
    command -v restorecon &>/dev/null && restorecon "/boot/symvers-%{KVERREL}%{?1:+%{1}}.gz" \
fi\
%{nil}

I put a testing script in /usr/lib/kernel/install.d, then I ran sudo dracut --regenerate-all --force.
It didn’t invoke my script.
Do Fedora updates often change the initrd through dracut regenerate without installing a new kernel?

Not to my knowledge. I’m quite sure none would ever run dracut --regenerate-all --force since that has such great potential to leave the system in an unbootable state. Fedora Linux likes to leave the previous two kernels “untouched” so someone can always trust that they can fall back to the older one if something went wrong during the update.

There is some sort of search functionality for src.fedoraproject.org (the source for most of the Fedora Linux RPMs). I guess one could try searching that for “dracut” and see if there are any matches.

EDIT: I just tried searching for dracut using sourcegraph and I did get a match for twincam. That package’s spec file contains the following lines.

%post
dracut -f

However, that package is currently only available in the rawhide/development (Fedora 38) version of Fedora Linux.

P.S. I just noticed that Plymoth’s “breeze” theme creates a /usr/lib/dracut/dracut.conf.d/10-plymouth-theme-breeze.conf file with the following contents.

# This is a hack that is currently necessary for package: plymouth-theme-breeze
# due to the fact that `dracut` only includes the top-level directory of theme folders
# By default. See also https://bugs.kde.org/show_bug.cgi?id=371276

if [[ "$(plymouth-set-default-theme)" =~ ^breeze ]]; then
	readarray -t plymouth_theme_breeze_files < <( find /usr/share/plymouth/themes/breeze -mindepth 1 -type f )
	install_optional_items+=" ${plymouth_theme_breeze_files[@]} "
fi

It is news to me that those .conf files can contain Bash scripting code. But apparently so. That being the case, you might be able to do something by dropping custom scripts under /etc/dracut.conf.d. They should then get called whenever dracut is run (by 50-dracut.install or otherwise). I’m not sure how well supported or “future proof” that would be though.

I tried adding a script in those two directories, even tried with execution flags, but they weren’t run. (no files created)

No files created could be a write permissions or SELinux issue. Other than that, I don’t know.

Maybe try adding something like touch /tmp/it-ran to the top of the script just to see if it is running at all.