What is libexec?

Today, as I was working on importing and building geckodriver in mozilla-central, I found myself head first down a rabbit hole trying to figure out why the mach try command we use for testing changesets in continuous integration complained that I didn’t have git-cinnabar installed:

% ./mach try -b do -p all -u all -t none
mach try is under development, please file bugs blocking 1149670.
ERROR git-cinnabar is required to push from git to try withthe autotry command.

More information can by found at https://github.com/glandium/git-cinnabar

As I’ve explained previously, git-cinnabar is a git extension for interacting with remote Mercurial repositories. It’s a godsend written by fellow Mozillian Mike Hommey that lets me do my work without getting my hands dirty with hg.

As one might suspect, mach try uses whereis(1) to look for the git-cinnabar binary. However, as it is a helper program that is not meant to be invoked directly, but rather be despatched through git cinnabar (without hyphen), it gets installed into /usr/local/libexec/git-core. Since I had never heard about libexec before, I decided to do some research.

libexec is meant for system daemons and system utilities executed by other programs. That is, the binaries put in this namespaced directory are meant for the consumption of other programs, and are not intended to be executed directly by users.

In fact, libexec is defined in the Filesystem Hierarchy Standard published by the Linux Foundation in this way:

/usr/libexec includes internal binaries that are not intended to be executed directly by users or shell scripts. Applications may use a single subdirectory under /usr/libexec.

On my preferred Linux system, Debian, there is apparently also /usr/local/libexec, which as far as I understand is meant to complement /usr/libexec in the same way that /usr/local compliments /usr. It provides a tertiary hierarchy for local data and programs that may be shared amongst hosts and that are safe from being overwritten when the system is upgraded. This is exactly what I want, since I installed git-cinnabar from source.

It was somewhat surprising to me to find that whereis(1)—or at least the util-linux version of it—does not provide a flag for searching auxillary support programs located in libexec, when it is capable of searching for manuals and sources, in addition to executable binaries:

% whereis -h

 whereis [options] [-BMS <dir>... -f] <name>

Locate the binary, source, and manual-page files for a command.

 -b         search only for binaries
 -B <dirs>  define binaries lookup path
 -m         search only for manuals and infos
 -M <dirs>  define man and info lookup path
 -s         search only for sources
 -S <dirs>  define sources lookup path
 -f         terminate <dirs> argument list
 -u         search for unusual entries
 -l         output effective lookup paths

For more details see whereis(1).

To further complicate things, git itself has no option for emitting where it finds its internally-called programs from. It does have an --exec-path flag that tells you where internal git commands are kept, but not where optionally installed git extensions are.

I think the fix to my problem is to tell mach try to include both /usr/libexec/git-core and /usr/local/libexec/git-core in the search path when it looks for git extensions, but maybe there is a more elegant way to check if git has a particular subcommand available? Certainly it’s conceivable to just call git cinnabar --help or similar and check its exit code.