Skip to content

Use bash loadable builtins (when available) to replace frequently-invoked external commands like rm, mkfifo, etc. #3906

Description

@plengauer

Goal

Across the shell scripts in this repository, external commands (e.g. rm, mkfifo, and other frequently-used utilities) are invoked repeatedly. Each such invocation forks/execs a separate process, which carries overhead. Bash provides an optional mechanism — "loadable builtins" — that allows certain commands to run as builtins directly inside the running shell process instead of forking an external binary. This issue proposes investigating and adopting that mechanism where it is safe and beneficial to do so, without breaking compatibility with non-bash shells.

Background

  • Bash ships (in its source distribution, under examples/loadables/) a set of optional "loadable builtins" — small pieces of code that can be compiled into shared objects and loaded into a running bash session via the enable -f builtin command. Examples include unlink (wraps the unlink(2) syscall to remove a single file) and mkfifo (creates a FIFO), among others.
  • When loaded, these run inside the bash process itself rather than forking a new process, which is faster for scripts that call these commands often.
  • These loadable builtins are not compiled into bash by default on most systems. They are typically made available via a separate distro package (e.g. Debian/Ubuntu's bash-builtins package), and even then must be explicitly loaded at runtime with enable -f.
  • Because this mechanism is bash-specific and depends on an optional package being installed, it cannot be assumed to be present. The repository's shell scripts need to remain functional in its absence, and also need to remain functional under non-bash shells (e.g. dash, busybox ash), which have no equivalent loadable-builtin mechanism at all.

Specific ask

  1. Survey rm usage: There are multiple occurrences of the rm command across the .sh files in this codebase. These should all be identified.
  2. Introduce an internal wrapper function, e.g. _otel_rm:
    • In api.sh (or wherever the shared/bootstrap logic lives), check whether bash's unlink loadable builtin is available.
    • If it is available, load it and expose it under the internal name _otel_rm (e.g. via enable -f ... unlink followed by aliasing/renaming to _otel_rm, however that ends up being implemented).
    • If it is not available, declare a shell function named _otel_rm that simply wraps/calls the regular external rm command, so behavior is identical to today.
    • Replace all direct occurrences of rm in all .sh files in the repository with calls to _otel_rm.
  3. Do the same for mkfifo: survey all occurrences of mkfifo across the .sh files, and introduce an analogous internal wrapper (e.g. _otel_mkfifo) that uses bash's loadable mkfifo builtin when available, and falls back to a function wrapping the external mkfifo command otherwise. Replace all occurrences accordingly.
  4. Generalize the search: Beyond rm and mkfifo, search the codebase for other frequently-used external commands that have a known bash loadable-builtin equivalent (for example — not exhaustive — things like sleep, pwd, printenv, basename, dirname, head, stat, tee, uname, id, ln, mkdir, rmdir, whoami, hostname, etc., to the extent bash ships or has shipped loadable equivalents for them). For every such command that is used frequently in this codebase and has a suitable loadable-builtin equivalent, apply the same pattern: internal wrapper function, conditional load of the builtin if available, fallback function wrapping the external command if not.
  5. Portability constraint (hard requirement): All of this must keep working correctly under non-bash shells (e.g. dash, busybox ash) where:
    • There is no enable -f mechanism at all.
    • Loadable builtins are not a concept.
    • The wrapper functions must transparently fall back to invoking the plain external commands in that environment, with identical observable behavior to what the scripts do today.
  6. Packaging/dependency metadata: Wherever this repository lists recommended or optional dependencies for system package managers (deb, rpm, apk, and any others already referenced in the repo's packaging/dependency files), the relevant bash loadable-builtins package should be added as a recommended (not required/hard) dependency, for each package manager where such a package actually exists (e.g. bash-builtins on Debian/Ubuntu-based deb packaging). Where no such package exists for a given package manager/distro family, none should be added there.

Non-goals / clarifications

  • The change must be purely behavior-preserving: whether or not the loadable builtin is available at runtime, _otel_rm, _otel_mkfifo, and any other introduced wrapper must behave identically to the current direct calls to rm, mkfifo, etc., from the perspective of the rest of the codebase.
  • The detection/loading of builtins should happen centrally (e.g. in api.sh, as it's described as the common/shared entry point) rather than being repeated ad hoc in every script.
  • Any additional commands identified in step 4 should only be adopted if a real bash loadable-builtin equivalent exists and the command is genuinely used frequently enough in this codebase to justify the wrapper.

Metadata

Metadata

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions