Friday, 13 January 2017

Gentoo adventures: running a script after a package is updated / installed (emerge hook)

So here's a neat little thing that I learned tonight: how to run a script after a specific package is installed or updated.

Why?

Because I use the net-fs/cifs-utils package to provide mount.cifs and I'd like scripts run by an unprivileged user to be able to mount and dismount entries in /etc/fstab. I'd also like user-space tools like the Dolphin file manager to be able to mount these points.

At some point, there was an suid USE flag for net-fs/cifs-utils but it was decided that the USE flag be removed due to security concerns. Which is probably a good decision to have as a default, but it's a little annoying when my script to sync media to my media player fails because it can't (re-)mount the samba share after net-fs/cifs-utils was updated.

I had a hunch that there would be a way to hook into the emerge process -- and Gentoo (or rather, Portage) didn't let me down. After finding https://dev.gentoo.org/~zmedico/portage/doc/portage.html#config-bashrc-ebuild-phase-hooks (which alluded to creating bash functions in a special file) and https://wiki.gentoo.org/wiki/Handbook:X86/Portage/Advanced#Using_.2Fetc.2Fportage.2Fenv which hinted at where to do such magic, and after a little play-testing, I finally got to adding the following function to the (new) file /etc/portage/bashrc:


function post_pkg_postinst() {
  if test "$CATEGORY/$PN" = "net-fs/cifs-utils"; then
    TARGET=/sbin/mount.cifs
    echo -e "\n\n\e[01;31m >>> Post-install hook: setting $TARGET SUID <<<\e[00m\n\n"
    chmod +s $TARGET
  fi
}


Which automatically sets the SUID bit and gives me a giant red warning that it did so.

https://wiki.gentoo.org/wiki/Handbook:X86/Portage/Advanced#Using_.2Fetc.2Fportage.2Fenv  seemed to suggest that I could create the hook in /etc/portage/env/net-fs/cifs-utils/bashrc, but that didn't work out for me (EDIT: putting stuff under there is a very bad idea unless you're only setting environment variables -- I initially put my hooks under there and found that subsequent emerges installed under /etc/portage/env !!) . The above did and was (reasonably) simple to implement, after a few install attempts and dumping the environment that the script runs in with the env command.

Refactoring time: I'd like to be able to write individual scripts for packages (if I ever do this again), like I do with USE flags, where I store all flags pertinent to the installation of a higher-order package in /etc/portage/package.use/higher-order-package so I know where lower-down USE flags come from, for example my /etc/portage/package.use/chromium file contains:

www-client/chromium -cups -hangouts
>=dev-libs/libxml2-2.9.4 icu
>=media-libs/libvpx-1.5.0 svc cpu_flags_x86_sse4_1 postproc


Wherein I can always reminisce about how I had to set the icu flag on libxml2 to successfully install chromium (https://wiki.gentoo.org/wiki/Qt/FAQ#qtwebkit_vs_chromium_block_caused_by_icu)

Similarly, it would be nice if I could just create code snippets under /etc/portage/env/${CATEGORY}/${PN}, perhaps even have category-wide scripts (which an individual package could override)... hm. Ok, so first, we move the script above to /etc/portage/env/net-fs/cifs-utils/post-install. Then we create a new /etc/portage/bashrc with these contents:

PORTAGE_HOOKS_PREFIX=/etc/portage/hooks
PACKAGE_HOOKS_DIR="$PORTAGE_HOOKS_PREFIX/$CATEGORY/$PN"
CATEGORY_HOOKS_DIR="$PORTAGE_HOOKS_PREFIX/$CATEGORY"
function source_all() {
  if test -d "$1"; then
    for f in $1/*; do
      if test -f "$f"; then
        source "$f"
      fi
    done
  fi
}
source_all "$CATEGORY_HOOKS_DIR"
source_all "$PACKAGE_HOOKS_DIR"


Which, as suggested above, loads all scripts from the category folder (if it exists) then overlays with scripts from the package folder (if that exists). So now it's trivial to add hooks for any of the emerge phases outlined in https://dev.gentoo.org/~zmedico/portage/doc/portage.html#config-bashrc-ebuild-phase-hooks

Side-note: since the scripts above can change any environment variable involved in the build, it's quite important to use nice, long, specific variable names. One of my first attempts used PREFIX instead of PORTAGE_HOOKS_PREFIX and that just ended up installing all new packages under the /etc/portage/hooks directory because, of course, $PREFIX is an environment variable respected by most build scripts... *facepalm*.

Once again, this becomes a testament to how Gentoo provides an operating system which is totally yours. You have the control to deal with all of it, its state is as a result of your actions and choices. Once again, I wish I'd switched years ago.

3 comments:

  1. Thank you very much for this blog post. I know it has been a while around, but quite now your post saved my day! :-)

    Cheers

    Sven

    ReplyDelete
  2. For those who found this via google, it is actually already built into portage, you just need to use the correct file names. Reference here:

    https://wiki.gentoo.org/wiki//etc/portage/package.env

    So set your scripts in the file:
    /etc/portage/env/category/pn
    or for specific package version only:
    /etc/portage/env/category/pn-version

    This file roughly behaves like bashrc, but is only included for that one package (or package-version)

    ReplyDelete
    Replies
    1. I originally tried something along that line and it b0rk3d (read about 1/3 of the way in). Perhaps things are different now :D

      Delete

What's new in PeanutButter?

Retrieving the post... Please hold. If the post doesn't load properly, you can check it out here: https://github.com/fluffynuts/blog/...