Creating useful Pacman hooks

I thought it would be nice to have a central thread for user created Pacman hooks. I am starting to get a bit of selection of them now, and I thought it's time the pacman hooks got shown a little bit of love on the forum. Pacman hook usage is not widely known, so I thought it could use a little more widespread exposure.

Pacman hooks allow you to automate any function you would like executed either before or after a Pacman install/uninstall or update takes place.

Hooks included with installed packages can be found in "/usr/share/libalpm/hooks".

User created hooks should be placed in "/etc/pacman.d/hooks".

Any associated scripts can be placed in "/etc/pacman.d/hooks.bin".

You will have to create these directories, as they are not created automatically.

Hook file names are required to have the suffix ".hook".

Hooks are run in alphanumeric order of their file name, (hooks prefixed with a lower number have precedence).


Index of contributed hooks:


Hook for Timeshift backups (2 cancelation methods)

Hook to create a list of all installed packages

Hook to notify about orphaned packages (v.1)

Hook to notify about orphaned packages (v.2)

Hook to check for for pacnew/pacsave files

Hook to disable baloo file indexer

Hook to update grub/lsb-release

Hook to fix Atheros QCA9377 firmware issue

16 Likes

How to automatically create a Timeshift backup prior to updating:

Many people often ask if it's possible to create a Timeshift backup before performing a system upgrade. It's not only possible, but it's very easy to implement with the use of a pacman hook. The hook will trigger an automatic Timeshift backup prior to installing any new system upgrade.

Follow these steps to create a timeshift pacman hook (and adjunct script):

Create a sub-directory for the pacman hook:

sudo mkdir /etc/pacman.d/hooks

Create a sub-directory for pacman hook related scripts:

sudo mkdir /etc/pacman.d/hooks.bin

First create the timeshift pacman hook:

/etc/pacman.d/hooks/50-timeshift.hook

With the following contents:

[Trigger]
Operation = Upgrade
Type = Package
Target = *

[Action]
Description = pre-upgrade timeshift snapshot
When = PreTransaction
Exec = /etc/pacman.d/hooks.bin/timeshift.sh

I have recently augmented the function of the old hook I was using with an additional script. The reason for the additional script is to limit repeated snapshots within a short period of time. Repeated snapshots become a major annoyance when you run into an upgrade problem that you need to debug. This script will prevent another snapshot from being initiated if an earlier snapshot was made in the prior half hour. To change the time frame, replace "-mmin -30" with another unit of time. If you would prefer the trigger to be set at an hour then simply change the time unit to "-mmin -60".

The script below will work for you if your timeshift backups are being written to an auto-mounted external drive. However, you will need to replace the path "/run/media/${user}/*/timeshift/snapshots-ondemand" with the correct backup path if you are using a different backup location. You may have to manually create the "/snapshots-ondemand" sub-directory if you have not made an on demand backup yet.

Next, create the timeshift script:

/etc/pacman.d/hooks.bin/timeshift.sh

With the following contents:

#!/bin/bash
#/etc/pacman.d/hooks.bin/timeshift.sh
user="$1"
find /run/media/${user}/*/timeshift/snapshots-ondemand -mmin -30 | grep $(date +%Y-%m-%d)
if [ $? -eq 0 ]; then
    echo timeshift backup aborted, time threshold not met
else
    /usr/bin/timeshift --create --comments "timeshift-pacman-hook-snapshot"
fi

Then, make the script executable:

chmod +x /etc/pacman.d/hooks.bin/timeshift.sh

Automatic backups can be accomplished with less effort by using a hook alone. However, I find repeated unnecessary snapshots to be a major annoyance (so the extra script is well worth the the minor effort it takes to write it IMO).

I hope the automated timeshift backup hook saves a few people some misery if an update meltdown occurs.

9 Likes

Pacman package backup list hook:

This hook compliments the above timeshift hook nicely in any backup strategy. This hook will backup a list of both your native and alien (AUR) installed packages. This ensures you will always have an up to date list of all your packages you can reinstall from.

Why do you need this you might ask (if you have a timeshift backup)? If you suffer a catastrophic hardware failure you may really appreciate being able to reinstall all your preferred packages from a backup automatically.

Create the following hook:

/etc/pacman.d/hooks/50-pacman-list.hook

With the following content:

#/etc/pacman.d/hooks/50-pacman-list.hook
[Trigger]
Type = Package
Operation = Install
Operation = Upgrade
Operation = Remove
Target = *

[Action]
Description = Create a backup list of all installed packages
When = PreTransaction
Exec = /bin/sh -c 'pacman -Qqen  > "/home/$USER/.cache/package_lists/$(date +%Y-%m-%d_%H:%M)_native.log"; pacman -Qqem > "/home/$USER/.cache/package_lists/$(date +%Y-%m-%d_%H:%M)_alien.txt" 2> /dev/null; exit'

You will most likely wish to change the path where you store your backup lists. Adding several backup locations is even better insurance in case of a hardware failure.

You can never be "too safe" when it comes to backups.

(edit) below is an example of how you would back up to your home directory, and also to an external backup drive in the same "Exec" line:

Exec = /bin/sh -c 'pacman -Qqen  > "/home/$USER/$(date +%Y-%m-%d@%H:%M)_native.log"; pacman -Qqem > "/home/$USER/$(date +%Y-%m-%d@%H:%M)_alien.txt"; pacman -Qqem > "/run/media/$USER/Backup/package_lists/$(date +%Y-%m-%d@%H:%M)_native.log"; pacman -Qqem > "/run/media/$USER/Backup/package_lists/$(date +%Y-%m-%d@%H:%M)_alien.txt " 2> /dev/null; exit'

It is often best practice to replace "$USER" with your own user name. In most cases "$USER" will work correctly, but in some cases it may insert the root user rather that your own user name. Therefore it is sometimes better to simply ensure that your correct username is inserted as a replacement for "$USER".

10 Likes

Nice work. :joy:

To be precise, I believe that the hooks are run in alphanumeric order, i.e., working from low to high numbers and then into the letters.

1 Like

Precisely, you are correct. That is why I renamed my original timeshift hook, because it was not getting executed before all updates because of other hooks having precedence.

I will amend my wording on the OP. Thanks for the heads up on missing that detail.

1 Like

That should be /home/$USER/.cache maybe?

1 Like

Thank you. I just added an edit to that hook using $USER, but of course I missed the one that you were referring to.

Much appreciated.

1 Like

Orphaned Package Hook:

Here's another very handy housekeeping hook.

You can use a pacman hook to notify you whenever orphaned packages are created.

To list all packages no longer required as dependencies create the following hook:

/etc/pacman.d/hooks/orphans.hook

Add the following content:

#/etc/pacman.d/hooks/orphans.hook

[Trigger]
Operation = Install
Operation = Upgrade
Operation = Remove
Type = Package
Target = *

[Action]
Description = Orphaned package notification
When = PostTransaction
Exec = /usr/bin/bash -c "/usr/bin/pacman -Qtd || /usr/bin/echo '=> No orphans found.'"

You could also have the hook automatically delete orphaned packages, but automating that function is a little too risky for my liking.

5 Likes

Just remember that just because something is an orphan does not mean it is unwanted :wink:

1 Like

Can you use $USER in a hook? They don't get executed by root?

1 Like

$USER is a bit dynamic.
You can check yourself
sudo echo $USER
will still give you the normal username from the account executing sudo.
if you su into root then echo $USER it will return root.

What I use in some places is
YOU=$(who -am | awk '{print $1}')

[ie - execute who -am | awk '{print $1}' .. that should work in all the ways.]

Isn't that because $USER is being interpreted by the shell before the call to sudo?

If you did

sudo bash -c 'echo $USER'

It will return root because it doesn't interpret $USER until bash is run.

I was curious so I created a hook to test it and here is the output:

:: Processing package changes...
(1/1) removing kwrite                                                                                                                                                                      
:: Running post-transaction hooks...
(1/4) Checking user
user is root
(2/4) Updating icon theme caches...
(3/4) Arming ConditionNeedsUpdate...
(4/4) Updating the desktop file MIME type cache...
2 Likes

Ah. Fair. If/then strikes again!

I get confused with the results of using $USER, because if you write a system service $USER usually defaults to root. Yet, I wrote a user service a couple of days ago and launching the script with $USER was fine, but the actual Python script would not accept the arguments using $USER.

Considering I don't feel like testing everything for everyone, I think it's just simpler sometimes to add the disclaimer to change it to your own user name.

At least that way people might realize what the problem is when testing something new if it's not working. It's kind of like using ~/ for home. Sometimes it works, sometimes it doesn't, and sometimes new users have no idea what the ~/stands for.

1 Like

For some time I've been using one that jonathon provided a while back (slightly modified). (No :crossed_swords: here. :grinning: just though you might be interested...)

1 Like

if we have some user for same manjaro, we have several backups in several directories ! or we can use pacman in chroot
Not sure with pamac : pamac have its own environment ($USER not exists)

best ? is to pass "user" (or directory) in hook

Exec = /etc/pacman.d/hooks.bin/timeshift.sh toto # or /run/media/toto
#!/usr/bin/bash
user="$1"
find /run/media/${user}/*/ ...
1 Like

Thank you again @papajoke I updated my script to replace "$USER" as per your suggestion. You have other timeshift script customizations to limit triggering excessive backups as well (don't you)?

Do you think your script could be integrated with my version as well?

Even better would be to get the orphan list and then compare with the list post transaction and notify only new orphans.

I thought a hook for notifying pacnew and pacsave files would be a good idea. So here is a working prototype. Although it only checks /etc I've no experience of .pac* files being elsewhere.

I'm sure @mbb 's comment applies to this also :wink: and in general if anyone who can actually code (rather than just noob-cut-paste) wants to make it better then please to do so.

4 Likes

That's a cool idea. I went another route and tied it in with bash aliases into my update process. You must have mlocate installed for this method to work. After my update is complete, a list of pacnew files is written to my desktop.

alias pacnew='sudo updatedb && locate --existing --regex "\.pac(new|save)$"'
alias pacdiff='sudo -H DIFFPROG=kompare pacdiff'
alias mirrors='sudo pacman-mirrors --country Canada United_States'
alias download='sudo pacman-mirrors --country Canada United_States && sudo pacman -Syyuw'
alias update='download && sudo pacman -Su && pacnew > ~/Desktop/pacnew.log'

Forum kindly sponsored by