Krikri::LocalExecutor
Constants
How long to keep draining stdout/stderr after the process itself has already exited, before giving up on a pipe that hasn't reached EOF.
A shell command shaped like sleep N && daemon & backgrounds a shell
that blocks in its own wait() on daemon (a trailing & backgrounds
the whole &&-list, and nohup only suppresses SIGHUP - it doesn't
exempt a child from its parent's wait()), so if daemon never exits,
neither does that shell, and the pipe it's still holding open never
reaches EOF. Waiting unconditionally for EOF on our pipes would hang
this call forever even though the actual process we spawned already
finished - so once the process itself has exited, any pipe still not
at EOF gets this much grace before being force-closed and abandoned
(a real command's own output is already fully written into the pipe's
kernel buffer by the time it exits, so this window only matters for
draining that tail, plus the rare writer still running in the
background that we've chosen not to wait for).
A leading NAME=value token (no metacharacters of its own, so the
regex above misses it) is shell env-assignment syntax, not part of
argv[0] - DEBIAN_FRONTEND=noninteractive apt-get install ...
(apt.cr's own real command, no export/; involved) would
otherwise get argv-split with "DEBIAN_FRONTEND=noninteractive" as
the executable name and crash with ENOENT. Only checked at the
start of the string - = inside a later argument (--opt=value)
is completely ordinary and not env-assignment syntax.
A command whose first word is a shell BUILTIN with no binary of its
own (command -v snap, type apt-get, hash -r, ...) has no
metacharacter either, so the argv fast path would exec a file named
"command" and fail with ENOENT - plugins probing for a tool with a
bare command -v X reported the tool missing on every host. Only the
builtins that exist solely as builtins are listed (true, false,
test and echo also ship as binaries).
Any of these anywhere in the command string means it actually needs
shell semantics (pipes, redirection, substitution, globbing, home-dir
expansion, escaping, sequencing) - Ansible's own local/ssh
connection plugins make the same call (_low_level_execute_command's
executable handling). Absent all of these, splitting into argv and
exec'ing directly is behaviorally identical to bash -c but skips
forking a whole extra shell process per command.
Class methods
env carries the task's environment: (BasePlugin#remote_exec), applied
through the child process's own environment the way Ansible hands
the dict to subprocess - never through a string prefix in the command
itself, which would put the values in the shell's argv where any local
user's ps can read them. Crystal merges a non-nil env over this
process's inherited environment (nil values unset), which is exactly the
overlay the old export K='V'; prefix produced; with env present the
shell path is always taken, because the argv fast path resolves argv[0]
with execvp against the PARENT's PATH while a environment: PATH: ...
override must govern the lookup (the shell the child became resolves
with its own, overridden PATH - the same semantics the export prefix
had).