Skip to content

Latest commit

 

History

History
207 lines (154 loc) · 8.14 KB

ConfigHooks.org

File metadata and controls

207 lines (154 loc) · 8.14 KB

Example usage:

run_hooks .shared_env.d

would be the last thing executed by ${ZDOTDIR:-$HOME}/.shared_env, and would check for files under .shared_env.d in the $ZDOTDIRPATH search path, which would be this (in order):

  • $ZDOTDIR/.shared_env.d/*
  • $HOME/.shared_env.d/*

i.e. from least specific to most specific.

Ditto for .zshrc, .bashrc etc.

Hook sub-directories

Additionally, if a directory entry has one of the following formats, it is treated specially:

  • person-$individual_uuid

    Run directory contents if $individual_uuid equals $ZDOTUSER. This should be unique to a person (not a uid); e-mail addresses are recommended, similar in idea to GNU arch’s global archive namespace.

  • host-$hostname

    Run directory contents if $hostname equals the current hostname. If there is ambiguity with regards to FQDN or non-FQDN, the code gives the benefit of the doubt in favour of a match.

  • uid-$username

    Run directory contents if $username equals the current (non-numeric) uid. An additional special case: if $username equals ‘OWNER’, the hook is only run if the current user owns the hook file. This allows a uid-specific hook to be installed in multiple home directories independently of its filename, effectively marking it as “for private use only”.

Compound filtering

FIXME: in rare cases, compound filtering (boolean composition of the above “atoms”) is required. Case study: running .xsession-progs.d/person-me/ from a demo account to get a nice X11 environment, e.g. ‘keyboard’ and ‘mouse’ are person-specific, ‘wallpaper’ is both person- and uid-specific, and ‘RSI’ is person-, uid-, and host-specific. (Conceivably there could even be something person- and host-specific, e.g. if I wanted to run a utility to set sound card output volumes in a particular way on a particular laptop.) So the current approach is too limited.

Is anything other than AND boolean ever required?

For example, filters implemented as arbitrary shell code, with the “atoms” simply being special cases? I would say yes, e.g. hardware-specific hooks, or hooks which need to be controlled manually such as composition of yum/smart repo/channel directories via a hostgroups-like approach (this refers to something I implemented in a previous job).

Immutable requirement: listing hooks without running them

Must be able to list which hooks would be run without running them, rather than embedding boolean tests as code within the hook itself. The latter is also prohibitive because not all hooks will be written in shell script (mutt config is one of many examples).

Axiom: filters have to be described out-of-band

Where are filters described?

Possibilities:

a) in the filename,

b) in the pathname (status quo),

c) stored separately as meta-data somewhere, with the hooks all in one directory, or

d) as meta-data embedded within the hook via comments, retrievable in a well-defined, consistent manner (like emacs local variables).

With c), a single flat file (e.g. .guards) in the hook directory isn’t going to work, because it doesn’t support the $foo.d methodology. Therefore it would have to be a .guards.d subdirectory, or a $foo.guard for each hook, either of which is a bit of a PITA.

Pros/cons of each

  1. a) and d) are easiest when creating new hooks … unless the right tools are built, in which case they probably require equal effort. But editing will still be slightly more awkward with c) than with the others, since it will involve an extra emacs buffer if changing both the hook and its filter :-)
  2. Only a) and b) offer easy viewing via standard fileutils, grep etc. but a list_hooks | xargs ... approach should deal with this, as long as list_hooks can be parametrized to fake a different environment (i.e. “list hooks which would have been run under $other_uid on $other_host”), and this should be easy to achieve.
  3. With c) and d), the file namespace is too flat and allows for collisions between the different $foo.d sources. And if you expand the namespace to avoid these collisions, you’re back with a) or b), which defeats the point.
  4. Only c) and d) allow a nice implementation of composite boolean filters. If it was only chained AND ops, a) and b) could do it but would result in ugly long filenames/pathnames respectively.
  5. Only c) and d) allow arbitrarily complex filter logic.
  6. a) and b) are virtually guaranteed to perform better, but premature optimization…
  7. Avoiding subdirectories makes hook ordering easier to implement (we don’t actually care, since implementation is a one-off cost which has already been done!), and a bit more visible (but again we don’t really care given a good list_hooks).

Points for each scheme:

a b c d

  1. 1 1 0 1
  2. 1 1 0 0
  3. 4 4 0 0
  4. 0 0 2 2
  5. 0 0 5 5
  6. 1 1 0 0
  7. 0 0 0 0

total: 7 7 7 8

Conclusion

In light of conflicting design goals, no single solution is ideal. Therefore a hybrid which allows multiple approaches is required.

Sanity check

Is the current 90% solution worth sticking with? And use different approach or hack for corner cases. Can we really justify substantial increases in complexity?

Requirements and rationale for search algorithm:

  • Must allow me to share config with friends. Accomplished by setting ZDOTDIR to point to my home directory when running as another uid. (We use the parameter name ZDOTDIR because zsh supports it natively when looking for core start-up files, defaulting to $HOME if it’s not set. But we extend the usage of the ZDOTDIR concept via this file to include our own non-core start-up files such as .shared_env.)
  • When others use my config, their environment must not be polluted with stuff specific to me. Accomplished by keeping all personal stuff in the .${dotfile}.d/person-$ZDOTUSER namespace, where $ZDOTUSER is something globally unique like the individual’s email address. (Although in my case, aspiers is almost certainly “unique enough” for practical purposes, if that expression’s not too much of an oxymoron.)
  • Must allow me to switch uid and still use config. Accomplished by setting ZDOTDIR=~aspiers and ZDOTUSER to my chosen individual UUID value.
  • Must allow per-uid config which would potentially span machines if home directories are shared (e.g. on NFS). It makes obvious sense to put any per-uid config in the uid’s home directory. This is what the .${dotfile}.d/uid-OWNER syntax is for.
  • Inheritance should be used wherever possible, i.e. ensuring that more settings for specific contexts can override less specific contexts by being loaded later. [Eh? You don’t need inheritance for that …]

Other considerations:

  • Current search order doesn’t allow specific contexts to override decisions made in less specific contexts without coupling more specific contexts to knowledge of the consequences of those decisions (i.e. you have to know what effects to manually reverse).
  • Finer granularity (more, smaller files) allows easier overriding in specific contexts which solves above issue and makes other things better e.g. setting users/hosts to complete. This could be prohibitively slow over NFS, but we’ll cross that bridge when we come to it, e.g. via some kind of prior compilation into one big .zwc ?
  • Would be nice to provide user with list of things which can be overridden. This can be achieved simply by grepping for run_hooks invocations.
  • Supports find_hooks for emacs, mutt, ssh, cron, and other non-shell-based environments which invoke the hooks in a different way - see rebuild_config function in ~/lib/libhooks.sh
  • ZDOTDIR=~aspiers as root, where ~aspiers is shared via NFS, reduces security of root a lot since trust level for NFS write access to ~aspiers is only by anyone who can get a port < 1024.