Creating useful Pacman hooks

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'

you could also just use
pacdiff -o

5 Likes

Way to take all the fun out our cool little projects we dream up. :smile:

3 Likes

Forum kindly sponsored by