Twenty years of Nagios plugins exist: check_mk agents, vendor scripts,
the Perl thing a former colleague wrote in 2009. A legacy check runs any
of them unmodified, because the protocol they speak (exit code, stdout,
perfdata after a |) is frozen in jaque and supported forever. The cost is
one process per execution and code running with the engine's own
permissions; the benefit is the sector's largest check ecosystem, working
on day one, with nothing rewritten.
1. The protocol, unchanged
A legacy check executes a binary and reads its result the way Nagios
always has. Exit code 0, 1, 2 or 3 maps to OK, WARNING, CRITICAL or
UNKNOWN. Stdout is the check output; the first |
splits it into text and perfdata, the same convention every other check
type in jaque follows.
services: disk: check: {
type: "legacy"
path: "/usr/lib/nagios/plugins/check_disk"
args: ["-w", "20%", "-c", "10%", "-p", "/"]
}
path is executed directly with args as its argument vector. There is
no shell in between; a plugin invocation that needs a pipe or a redirect
is a command check with shell: true.
2. Perfdata grammar
The text after | is parsed as zero or more whitespace-separated metrics,
each of the form:
label=value[UOM];warn;crit;min;max
2.1 Label
label is everything before the first =. A label that starts with a
single quote (') runs to the next unescaped ' and may contain spaces;
a doubled '' inside a quoted label is a literal quote, not a close.
Space inside an unquoted label ends the token early and is not supported.
2.2 Value and unit
value is an int or float, optionally signed, optionally with an exponent
(1, -0.5, 1e3), immediately followed by an optional UOM made only
of letters, % and /. A token whose unit contains anything else fails
to parse.
2.3 Thresholds and bounds
;warn;crit;min;max are semicolon-separated and optional from the right:
label=value alone is valid, as are label=value;warn,
label=value;warn;crit and so on up to all four. warn and crit are
Nagios threshold range specs; min and max are plain
floats. A token with more than five ;-separated fields fails to parse.
2.4 Error handling
Metrics are separated by whitespace outside quoted labels. A malformed token is dropped without failing the metrics around it, so one bad field from a plugin does not cost the sink the rest of the line.
time=0.125s;1.0;2.0;0;5 'packet loss'=0%;20;40
3. Process isolation
Each execution runs in its own process group (Setpgid). On timeout jaque
kills the whole group, not the immediate child, so a plugin that forks a
helper and then hangs leaves no orphan behind. timeout is per check and
defaults to 10s from #Schedule. A plugin that times out, cannot be
found, is not executable, or dies on a signal is reported as UNKNOWN with
the reason in its output; it is never retried into a false OK.
4. Permanent, not a bridge
Legacy exec support is not a migration aid to be outgrown. Anything that speaks the exit-code protocol keeps working indefinitely; this is the same commitment the landing page makes and the status page tracks. WASM checks exist beside it for code written new, that should be one file and sandboxed by construction; they do not replace it.
5. Security considerations
A legacy plugin is arbitrary code with the engine process's user, ambient
environment and network position. jaque constrains its lifetime (the
process group and timeout) and nothing else; what it may read or reach
is the operator's job, through the user jaque runs as. args are passed
as an argument vector, never through a shell, so a value in config cannot
inject a second command.