From 802e46f8ea36c344f112d7e1dd8d64d17a4cc939 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 12:59:37 +0200 Subject: Run clang-format again --- plugins/check_by_ssh.c | 108 +++++++++++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 44 deletions(-) (limited to 'plugins/check_by_ssh.c') diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index 2bc38d49..74b7a46f 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -45,7 +45,8 @@ typedef struct { check_by_ssh_config config; } check_by_ssh_config_wrapper; static check_by_ssh_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static check_by_ssh_config_wrapper validate_arguments(check_by_ssh_config_wrapper /*config_wrapper*/); +static check_by_ssh_config_wrapper + validate_arguments(check_by_ssh_config_wrapper /*config_wrapper*/); static command_construct comm_append(command_construct /*cmd*/, const char * /*str*/); static void print_help(void); @@ -90,7 +91,8 @@ int main(int argc, char **argv) { /* SSH returns 255 if connection attempt fails; include the first line of error output */ if (result == 255 && config.unknown_timeout) { - printf(_("SSH connection failed: %s\n"), chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); + printf(_("SSH connection failed: %s\n"), + chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); return STATE_UNKNOWN; } @@ -134,7 +136,8 @@ int main(int argc, char **argv) { puts(chld_out.line[i]); } } else { - printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), state_text(result), config.remotecmd, result); + printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), + state_text(result), config.remotecmd, result); } return result; /* return error status from remote command */ } @@ -160,9 +163,11 @@ int main(int argc, char **argv) { die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); } - if (config.service[commands] && status_text && sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { - fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time, config.host_shortname, - config.service[commands++], cresult, status_text); + if (config.service[commands] && status_text && + sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { + fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", + (int)local_time, config.host_shortname, config.service[commands++], cresult, + status_text); } } @@ -172,34 +177,35 @@ int main(int argc, char **argv) { /* process command-line arguments */ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { - static struct option longopts[] = {{"version", no_argument, 0, 'V'}, - {"help", no_argument, 0, 'h'}, - {"verbose", no_argument, 0, 'v'}, - {"fork", no_argument, 0, 'f'}, - {"timeout", required_argument, 0, 't'}, - {"unknown-timeout", no_argument, 0, 'U'}, - {"host", required_argument, 0, 'H'}, /* backward compatibility */ - {"hostname", required_argument, 0, 'H'}, - {"port", required_argument, 0, 'p'}, - {"output", required_argument, 0, 'O'}, - {"name", required_argument, 0, 'n'}, - {"services", required_argument, 0, 's'}, - {"identity", required_argument, 0, 'i'}, - {"user", required_argument, 0, 'u'}, - {"logname", required_argument, 0, 'l'}, - {"command", required_argument, 0, 'C'}, - {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ - {"skip-stdout", optional_argument, 0, 'S'}, - {"skip-stderr", optional_argument, 0, 'E'}, - {"warn-on-stderr", no_argument, 0, 'W'}, - {"proto1", no_argument, 0, '1'}, - {"proto2", no_argument, 0, '2'}, - {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, - {"ssh-option", required_argument, 0, 'o'}, - {"quiet", no_argument, 0, 'q'}, - {"configfile", optional_argument, 0, 'F'}, - {0, 0, 0, 0}}; + static struct option longopts[] = { + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, + {"fork", no_argument, 0, 'f'}, + {"timeout", required_argument, 0, 't'}, + {"unknown-timeout", no_argument, 0, 'U'}, + {"host", required_argument, 0, 'H'}, /* backward compatibility */ + {"hostname", required_argument, 0, 'H'}, + {"port", required_argument, 0, 'p'}, + {"output", required_argument, 0, 'O'}, + {"name", required_argument, 0, 'n'}, + {"services", required_argument, 0, 's'}, + {"identity", required_argument, 0, 'i'}, + {"user", required_argument, 0, 'u'}, + {"logname", required_argument, 0, 'l'}, + {"command", required_argument, 0, 'C'}, + {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ + {"skip-stdout", optional_argument, 0, 'S'}, + {"skip-stderr", optional_argument, 0, 'E'}, + {"warn-on-stderr", no_argument, 0, 'W'}, + {"proto1", no_argument, 0, '1'}, + {"proto2", no_argument, 0, '2'}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"ssh-option", required_argument, 0, 'o'}, + {"quiet", no_argument, 0, 'q'}, + {"configfile", optional_argument, 0, 'F'}, + {0, 0, 0, 0}}; check_by_ssh_config_wrapper result = { .errorcode = OK, @@ -221,7 +227,8 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { int option = 0; while (true) { - int opt_index = getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option); + int opt_index = + getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option); if (opt_index == -1 || opt_index == EOF) { break; @@ -266,11 +273,13 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { char *p2; p1 = optarg; - result.config.service = realloc(result.config.service, (++result.config.number_of_services) * sizeof(char *)); + result.config.service = realloc(result.config.service, + (++result.config.number_of_services) * sizeof(char *)); while ((p2 = index(p1, ':'))) { *p2 = '\0'; result.config.service[result.config.number_of_services - 1] = p1; - result.config.service = realloc(result.config.service, (++result.config.number_of_services) * sizeof(char *)); + result.config.service = realloc( + result.config.service, (++result.config.number_of_services) * sizeof(char *)); p1 = p2 + 1; } result.config.service[result.config.number_of_services - 1] = p1; @@ -309,7 +318,8 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { case 'C': /* Command for remote machine */ result.config.commands++; if (result.config.commands > 1) { - xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;", result.config.remotecmd); + xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;", + result.config.remotecmd); } xasprintf(&result.config.remotecmd, "%s%s", result.config.remotecmd, optarg); break; @@ -396,7 +406,8 @@ command_construct comm_append(command_construct cmd, const char *str) { die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS); } - if ((cmd.commargv = (char **)realloc(cmd.commargv, (cmd.commargc + 1) * sizeof(char *))) == NULL) { + if ((cmd.commargv = (char **)realloc(cmd.commargv, (cmd.commargc + 1) * sizeof(char *))) == + NULL) { die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n")); } @@ -412,12 +423,18 @@ check_by_ssh_config_wrapper validate_arguments(check_by_ssh_config_wrapper confi return config_wrapper; } - if (config_wrapper.config.passive && config_wrapper.config.commands != config_wrapper.config.number_of_services) { - die(STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname); + if (config_wrapper.config.passive && + config_wrapper.config.commands != config_wrapper.config.number_of_services) { + die(STATE_UNKNOWN, + _("%s: In passive mode, you must provide a service name for each command.\n"), + progname); } if (config_wrapper.config.passive && config_wrapper.config.host_shortname == NULL) { - die(STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the monitoring configs.\n"), progname); + die(STATE_UNKNOWN, + _("%s: In passive mode, you must provide the host short name from the monitoring " + "configs.\n"), + progname); } return config_wrapper; @@ -454,7 +471,8 @@ void print_help(void) { printf(" %s\n", "-W, --warn-on-stderr]"); printf(" %s\n", _("Exit with an warning, if there is an output on STDERR")); printf(" %s\n", "-f"); - printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always return OK if ssh is executed")); + printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always " + "return OK if ssh is executed")); printf(" %s\n", "-C, --command='COMMAND STRING'"); printf(" %s\n", _("command to execute on the remote machine")); printf(" %s\n", "-l, --logname=USERNAME"); @@ -490,7 +508,9 @@ void print_help(void) { printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)")); printf("\n"); printf("%s\n", _("Examples:")); - printf(" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo"); + printf( + " %s\n", + "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo"); printf(" %s\n", "$ cat /tmp/foo"); printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days"); printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days"); -- cgit v1.2.3-74-g34f1 From 4b3f684d33af7459024011a06704cf4ca85dd0a3 Mon Sep 17 00:00:00 2001 From: Alvar Penning Date: Mon, 15 Sep 2025 22:20:08 +0200 Subject: check_by_ssh: Ignore output on stderr by default check_by_ssh no longer returns UNKNOWN if ssh(1) returns data on stderr. But it can be enforced again by the new "--unknown-on-stderr" option. --- The default logic of check_by_ssh results in an UNKNOWN state if the ssh(1) process produces output on stderr. Using the "--skip-stderr=[n]" option allows ignoring a certain amount of lines or disabling this check altogether. Furthermore, passing the "--warn-on-stderr" option reduces the exit code to WARNING. The "--help" output does not document this behavior, only states that "--warn-on-stderr" will result in the WARNING, but does not mention the UNKNOWN by default. The man page of ssh(1) mentions that debug information is logged to stderr. This conflicts with the described logic, resulting in check_by_ssh to go UNKNOWN, unless additional options are set. Starting with OpenSSH version 10.1, ssh(1) will report warnings to stderr if the opposite server does not support post-quantum cryptography, . This change, slowly being rolled out throughout the next months/years, might result in mass-breakages of check_by_ssh. By introducing a new "--unknown-on-stderr" option, enforcing the prior default logic of an UNKNOWN state for data on stderr, and ignoring output on stderr by default, check_by_ssh will continue to work. One might even argue that this change converges actual implementation and the documented behavior, as argued above. --- $ ssh example '/usr/lib/nagios/plugins/check_dummy 0 demo' ** WARNING: connection is not using a post-quantum key exchange algorithm. ** This session may be vulnerable to "store now, decrypt later" attacks. ** The server may need to be upgraded. See https://openssh.com/pq.html OK: demo $ echo $? 0 $ ./check_by_ssh -H example -C '/usr/lib/nagios/plugins/check_dummy 0 demo' OK: demo $ echo $? 0 $ ./check_by_ssh -H example -C '/usr/lib/nagios/plugins/check_dummy 0 demo' --warn-on-stderr Remote command execution failed: ** WARNING: connection is not using a post-quantum key exchange algorithm. $ echo $? 1 $ ./check_by_ssh -H example -C '/usr/lib/nagios/plugins/check_dummy 0 demo' --unknown-on-stderr Remote command execution failed: ** WARNING: connection is not using a post-quantum key exchange algorithm. $ echo $? 3 --- Fixes #2147. --- plugins/check_by_ssh.c | 23 +++++++++++++++-------- plugins/check_by_ssh.d/config.h | 2 ++ 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'plugins/check_by_ssh.c') diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index 74b7a46f..a43c0d34 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -119,13 +119,14 @@ int main(int argc, char **argv) { skip_stderr = config.skip_stderr; } - /* UNKNOWN or worse if (non-skipped) output found on stderr */ - if (chld_err.lines > (size_t)skip_stderr) { + /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */ + if (chld_err.lines > (size_t)skip_stderr && (config.unknown_on_stderr || config.warn_on_stderr)) { printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]); - if (config.warn_on_stderr) { + if (config.unknown_on_stderr) { + return max_state_alt(result, STATE_UNKNOWN); + } else if (config.warn_on_stderr) { return max_state_alt(result, STATE_WARNING); } - return max_state_alt(result, STATE_UNKNOWN); } /* this is simple if we're not supposed to be passive. @@ -191,12 +192,13 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { {"name", required_argument, 0, 'n'}, {"services", required_argument, 0, 's'}, {"identity", required_argument, 0, 'i'}, - {"user", required_argument, 0, 'u'}, + {"user", required_argument, 0, 'u'}, /* backwards compatibility */ {"logname", required_argument, 0, 'l'}, {"command", required_argument, 0, 'C'}, {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ {"skip-stdout", optional_argument, 0, 'S'}, {"skip-stderr", optional_argument, 0, 'E'}, + {"unknown-on-stderr", no_argument, 0, 'e'}, {"warn-on-stderr", no_argument, 0, 'W'}, {"proto1", no_argument, 0, '1'}, {"proto2", no_argument, 0, '2'}, @@ -341,6 +343,9 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { result.config.skip_stderr = atoi(optarg); } break; + case 'e': /* exit with unknown if there is an output on stderr */ + result.config.unknown_on_stderr = true; + break; case 'W': /* exit with warning if there is an output on stderr */ result.config.warn_on_stderr = true; break; @@ -468,8 +473,10 @@ void print_help(void) { printf(" %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]")); printf(" %s\n", "-E, --skip-stderr[=n]"); printf(" %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]")); - printf(" %s\n", "-W, --warn-on-stderr]"); - printf(" %s\n", _("Exit with an warning, if there is an output on STDERR")); + printf(" %s\n", "-e, --unknown-on-stderr"); + printf(" %s\n", _("Exit with UNKNOWN, if there is output on STDERR")); + printf(" %s\n", "-W, --warn-on-stderr"); + printf(" %s\n", _("Exit with WARNING, if there is output on STDERR")); printf(" %s\n", "-f"); printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always " "return OK if ssh is executed")); @@ -522,7 +529,7 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); printf(" %s -H -C [-fqvU] [-1|-2] [-4|-6]\n" - " [-S [lines]] [-E [lines]] [-W] [-t timeout] [-i identity]\n" + " [-S [lines]] [-E [lines]] [-e|-W] [-t timeout] [-i identity]\n" " [-l user] [-n name] [-s servicelist] [-O outputfile]\n" " [-p port] [-o ssh-option] [-F configfile]\n", progname); diff --git a/plugins/check_by_ssh.d/config.h b/plugins/check_by_ssh.d/config.h index 05435def..0e4b56d4 100644 --- a/plugins/check_by_ssh.d/config.h +++ b/plugins/check_by_ssh.d/config.h @@ -21,6 +21,7 @@ typedef struct { command_construct cmd; bool unknown_timeout; + bool unknown_on_stderr; bool warn_on_stderr; int skip_stdout; int skip_stderr; @@ -46,6 +47,7 @@ check_by_ssh_config check_by_ssh_config_init() { }, .unknown_timeout = false, + .unknown_on_stderr = false, .warn_on_stderr = false, .skip_stderr = 0, .skip_stdout = 0, -- cgit v1.2.3-74-g34f1 From e77ce530c44178521b7e0c4012feed1e8006e41e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:32:03 +0100 Subject: check_by_ssh: Implement modern output functionality --- plugins/check_by_ssh.c | 184 +++++++++++++++++++++++++++++----------- plugins/check_by_ssh.d/config.h | 21 ++++- 2 files changed, 152 insertions(+), 53 deletions(-) (limited to 'plugins/check_by_ssh.c') diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index a43c0d34..ad385fbd 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -26,16 +26,17 @@ * *****************************************************************************/ -const char *progname = "check_by_ssh"; -const char *copyright = "2000-2024"; -const char *email = "devel@monitoring-plugins.org"; - #include "common.h" +#include "output.h" #include "utils.h" #include "utils_cmd.h" #include "check_by_ssh.d/config.h" #include "states.h" +const char *progname = "check_by_ssh"; +const char *copyright = "2000-2024"; +const char *email = "devel@monitoring-plugins.org"; + #ifndef NP_MAXARGS # define NP_MAXARGS 1024 #endif @@ -71,6 +72,10 @@ int main(int argc, char **argv) { const check_by_ssh_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + /* Set signal handling and alarm timeout */ if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { usage_va(_("Cannot catch SIGALRM")); @@ -85,62 +90,101 @@ int main(int argc, char **argv) { } } - output chld_out; - output chld_err; - mp_state_enum result = cmd_run_array(config.cmd.commargv, &chld_out, &chld_err, 0); + cmd_run_result child_result = cmd_run_array2(config.cmd.commargv, 0); + mp_check overall = mp_check_init(); /* SSH returns 255 if connection attempt fails; include the first line of error output */ - if (result == 255 && config.unknown_timeout) { - printf(_("SSH connection failed: %s\n"), - chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); - return STATE_UNKNOWN; + mp_subcheck sc_ssh_execution = mp_subcheck_init(); + if (child_result.cmd_error_code == 255 && config.unknown_timeout) { + xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s", + child_result.stderr.lines > 0 ? child_result.stderr.line[0] + : "(no error output)"); + + sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); + mp_add_subcheck_to_check(&overall, sc_ssh_execution); + mp_exit(overall); } + xasprintf(&sc_ssh_execution.output, "SSH connection succeeded"); + sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_ssh_execution); if (verbose) { - for (size_t i = 0; i < chld_out.lines; i++) { - printf("stdout: %s\n", chld_out.line[i]); + for (size_t i = 0; i < child_result.stdout.lines; i++) { + printf("stdout: %s\n", child_result.stdout.line[i]); } - for (size_t i = 0; i < chld_err.lines; i++) { - printf("stderr: %s\n", chld_err.line[i]); + for (size_t i = 0; i < child_result.stderr.lines; i++) { + printf("stderr: %s\n", child_result.stderr.line[i]); } } size_t skip_stdout = 0; - if (config.skip_stdout == -1) { /* --skip-stdout specified without argument */ - skip_stdout = chld_out.lines; + if (config.skip_stdout) { /* --skip-stdout specified without argument */ + skip_stdout = child_result.stdout.lines; } else { - skip_stdout = config.skip_stdout; + skip_stdout = config.stdout_lines_to_ignore; } size_t skip_stderr = 0; - if (config.skip_stderr == -1) { /* --skip-stderr specified without argument */ - skip_stderr = chld_err.lines; + if (config.skip_stderr) { /* --skip-stderr specified without argument */ + skip_stderr = child_result.stderr.lines; } else { - skip_stderr = config.skip_stderr; + skip_stderr = config.sterr_lines_to_ignore; } /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */ - if (chld_err.lines > (size_t)skip_stderr && (config.unknown_on_stderr || config.warn_on_stderr)) { - printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]); + if (child_result.stderr.lines > skip_stderr && + (config.unknown_on_stderr || config.warn_on_stderr)) { + mp_subcheck sc_stderr = mp_subcheck_init(); + xasprintf(&sc_stderr.output, "remote command execution failed: %s", + child_result.stderr.line[skip_stderr]); + if (config.unknown_on_stderr) { - return max_state_alt(result, STATE_UNKNOWN); - } else if (config.warn_on_stderr) { - return max_state_alt(result, STATE_WARNING); + sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_UNKNOWN); + } + + if (config.warn_on_stderr) { + sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_WARNING); } + + mp_add_subcheck_to_check(&overall, sc_stderr); + // TODO still exit here? } /* this is simple if we're not supposed to be passive. * Wrap up quickly and keep the tricks below */ if (!config.passive) { - if (chld_out.lines > (size_t)skip_stdout) { - for (size_t i = skip_stdout; i < chld_out.lines; i++) { - puts(chld_out.line[i]); + mp_subcheck sc_active_check = mp_subcheck_init(); + xasprintf(&sc_active_check.output, "command stdout:"); + + if (child_result.stdout.lines > skip_stdout) { + for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { + xasprintf(&sc_active_check.output, "%s\n%s", sc_active_check.output, + child_result.stdout.line[i]); } } else { - printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), - state_text(result), config.remotecmd, result); + xasprintf(&sc_active_check.output, "remote command '%s' returned status %d", + config.remotecmd, child_result.cmd_error_code); + } + + /* return error status from remote command */ + + switch (child_result.cmd_error_code) { + case 0: + sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_OK); + break; + case 1: + sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_WARNING); + break; + case 2: + sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_CRITICAL); + break; + default: + sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_UNKNOWN); + break; } - return result; /* return error status from remote command */ + + mp_add_subcheck_to_check(&overall, sc_active_check); + mp_exit(overall); } /* @@ -148,36 +192,57 @@ int main(int argc, char **argv) { */ /* process output */ - FILE *file_pointer = NULL; - if (!(file_pointer = fopen(config.outputfile, "a"))) { - printf(_("SSH WARNING: could not open %s\n"), config.outputfile); - exit(STATE_UNKNOWN); + mp_subcheck sc_passive_file = mp_subcheck_init(); + FILE *output_file = NULL; + if (!(output_file = fopen(config.outputfile, "a"))) { + xasprintf(&sc_passive_file.output, "could not open %s", config.outputfile); + sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_UNKNOWN); + + mp_add_subcheck_to_check(&overall, sc_passive_file); + mp_exit(overall); } + xasprintf(&sc_passive_file.output, "opened output file %s", config.outputfile); + sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_passive_file); + time_t local_time = time(NULL); unsigned int commands = 0; char *status_text; int cresult; - for (size_t i = skip_stdout; i < chld_out.lines; i++) { - status_text = chld_out.line[i++]; - if (i == chld_out.lines || strstr(chld_out.line[i], "STATUS CODE: ") == NULL) { - die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); + mp_subcheck sc_parse_passive = mp_subcheck_init(); + for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { + status_text = child_result.stdout.line[i++]; + if (i == child_result.stdout.lines || + strstr(child_result.stdout.line[i], "STATUS CODE: ") == NULL) { + + sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_UNKNOWN); + xasprintf(&sc_parse_passive.output, "failed to parse output"); + mp_add_subcheck_to_check(&overall, sc_parse_passive); + mp_exit(overall); } if (config.service[commands] && status_text && - sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { - fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", - (int)local_time, config.host_shortname, config.service[commands++], cresult, - status_text); + sscanf(child_result.stdout.line[i], "STATUS CODE: %d", &cresult) == 1) { + fprintf(output_file, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time, + config.host_shortname, config.service[commands++], cresult, status_text); } } + sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_OK); + xasprintf(&sc_parse_passive.output, "parsed and wrote output"); + mp_add_subcheck_to_check(&overall, sc_parse_passive); + /* Multiple commands and passive checking should always return OK */ - exit(result); + mp_exit(overall); } /* process command-line arguments */ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { + enum { + output_format_index = CHAR_MAX + 1, + }; + static struct option longopts[] = { {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, @@ -207,6 +272,7 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { {"ssh-option", required_argument, 0, 'o'}, {"quiet", no_argument, 0, 'q'}, {"configfile", optional_argument, 0, 'F'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; check_by_ssh_config_wrapper result = { @@ -327,20 +393,27 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { break; case 'S': /* skip n (or all) lines on stdout */ if (optarg == NULL) { - result.config.skip_stdout = -1; /* skip all output on stdout */ + result.config.skip_stdout = true; /* skip all output on stdout */ + + if (verbose) { + printf("Setting the skip_stdout flag\n"); + } } else if (!is_integer(optarg)) { usage_va(_("skip-stdout argument must be an integer")); } else { - result.config.skip_stdout = atoi(optarg); + result.config.stdout_lines_to_ignore = atoi(optarg); } break; case 'E': /* skip n (or all) lines on stderr */ if (optarg == NULL) { - result.config.skip_stderr = -1; /* skip all output on stderr */ + result.config.skip_stderr = true; /* skip all output on stderr */ + if (verbose) { + printf("Setting the skip_stderr flag\n"); + } } else if (!is_integer(optarg)) { usage_va(_("skip-stderr argument must be an integer")); } else { - result.config.skip_stderr = atoi(optarg); + result.config.sterr_lines_to_ignore = atoi(optarg); } break; case 'e': /* exit with unknown if there is an output on stderr */ @@ -360,6 +433,18 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { result.config.cmd = comm_append(result.config.cmd, "-F"); result.config.cmd = comm_append(result.config.cmd, optarg); break; + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } default: /* help */ usage5(); } @@ -502,6 +587,7 @@ void print_help(void) { printf(" %s\n", "-U, --unknown-timeout"); printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL")); printf(UT_VERBOSE); + printf(UT_OUTPUT_FORMAT); printf("\n"); printf(" %s\n", _("The most common mode of use is to refer to a local identity file with")); printf(" %s\n", _("the '-i' option. In this mode, the identity pair should have a null")); diff --git a/plugins/check_by_ssh.d/config.h b/plugins/check_by_ssh.d/config.h index 0e4b56d4..b6a57964 100644 --- a/plugins/check_by_ssh.d/config.h +++ b/plugins/check_by_ssh.d/config.h @@ -1,6 +1,7 @@ #pragma once #include "../../config.h" +#include "output.h" #include typedef struct { @@ -23,10 +24,16 @@ typedef struct { bool unknown_timeout; bool unknown_on_stderr; bool warn_on_stderr; - int skip_stdout; - int skip_stderr; + bool skip_stdout; + size_t stdout_lines_to_ignore; + bool skip_stderr; + size_t sterr_lines_to_ignore; + bool passive; char *outputfile; + + bool output_format_is_set; + mp_output_format output_format; } check_by_ssh_config; check_by_ssh_config check_by_ssh_config_init() { @@ -49,10 +56,16 @@ check_by_ssh_config check_by_ssh_config_init() { .unknown_timeout = false, .unknown_on_stderr = false, .warn_on_stderr = false, - .skip_stderr = 0, - .skip_stdout = 0, + + .skip_stderr = false, + .stdout_lines_to_ignore = 0, + .skip_stdout = false, + .sterr_lines_to_ignore = 0, + .passive = false, .outputfile = NULL, + + .output_format_is_set = false, }; return tmp; } -- cgit v1.2.3-74-g34f1 From 463223790cb67421d420ba982c1f5ee6cc6f2b4a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:52:07 +0100 Subject: check_by_ssh: handle errrors of ssh (1) directly --- plugins/check_by_ssh.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'plugins/check_by_ssh.c') diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index ad385fbd..8036ffa4 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -100,10 +100,27 @@ int main(int argc, char **argv) { child_result.stderr.lines > 0 ? child_result.stderr.line[0] : "(no error output)"); + sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); + mp_add_subcheck_to_check(&overall, sc_ssh_execution); + mp_exit(overall); + } else if (child_result.cmd_error_code != 0) { + xasprintf(&sc_ssh_execution.output, "SSH connection failed: "); + + if (child_result.stderr.lines > 0) { + for (size_t i = 0; i < child_result.stderr.lines; i++) { + xasprintf(&sc_ssh_execution.output, "%s\n%s", sc_ssh_execution.output, + child_result.stderr.line[i]); + } + } else { + xasprintf(&sc_ssh_execution.output, "%s %s", sc_ssh_execution.output, + "no output on stderr"); + } + sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); mp_add_subcheck_to_check(&overall, sc_ssh_execution); mp_exit(overall); } + xasprintf(&sc_ssh_execution.output, "SSH connection succeeded"); sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_OK); mp_add_subcheck_to_check(&overall, sc_ssh_execution); -- cgit v1.2.3-74-g34f1 From 62242ddcf5caa09eee79c15a94a5d861b9be95c8 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 16 Nov 2025 15:27:19 +0100 Subject: check_by_ssh: do not incorrectly assume that ssh (1) succeeded --- plugins/check_by_ssh.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) (limited to 'plugins/check_by_ssh.c') diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index 8036ffa4..ebe54b80 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -94,37 +94,18 @@ int main(int argc, char **argv) { mp_check overall = mp_check_init(); /* SSH returns 255 if connection attempt fails; include the first line of error output */ - mp_subcheck sc_ssh_execution = mp_subcheck_init(); + // we can sadly not detect other SSH errors if (child_result.cmd_error_code == 255 && config.unknown_timeout) { + mp_subcheck sc_ssh_execution = mp_subcheck_init(); xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s", child_result.stderr.lines > 0 ? child_result.stderr.line[0] : "(no error output)"); - sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); - mp_add_subcheck_to_check(&overall, sc_ssh_execution); - mp_exit(overall); - } else if (child_result.cmd_error_code != 0) { - xasprintf(&sc_ssh_execution.output, "SSH connection failed: "); - - if (child_result.stderr.lines > 0) { - for (size_t i = 0; i < child_result.stderr.lines; i++) { - xasprintf(&sc_ssh_execution.output, "%s\n%s", sc_ssh_execution.output, - child_result.stderr.line[i]); - } - } else { - xasprintf(&sc_ssh_execution.output, "%s %s", sc_ssh_execution.output, - "no output on stderr"); - } - sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); mp_add_subcheck_to_check(&overall, sc_ssh_execution); mp_exit(overall); } - xasprintf(&sc_ssh_execution.output, "SSH connection succeeded"); - sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_OK); - mp_add_subcheck_to_check(&overall, sc_ssh_execution); - if (verbose) { for (size_t i = 0; i < child_result.stdout.lines; i++) { printf("stdout: %s\n", child_result.stdout.line[i]); -- cgit v1.2.3-74-g34f1 From c3d931fa1acc9ad61328f873cf0206c765d93ac6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 16 Nov 2025 15:27:58 +0100 Subject: check_by_ssh: some formatting --- plugins/check_by_ssh.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'plugins/check_by_ssh.c') diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index ebe54b80..df8907d9 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -599,9 +599,8 @@ void print_help(void) { printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)")); printf("\n"); printf("%s\n", _("Examples:")); - printf( - " %s\n", - "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo"); + printf(" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C " + "uptime -O /tmp/foo"); printf(" %s\n", "$ cat /tmp/foo"); printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days"); printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days"); -- cgit v1.2.3-74-g34f1 From 6ce11bc44f5fe2344083a94175a1667ca02e016c Mon Sep 17 00:00:00 2001 From: Alvar Penning Date: Thu, 11 Dec 2025 10:53:07 +0100 Subject: lib/utils_cmd: Rename stdout, stderr in cmd_run_result On OpenBSD's "stdio.h", stdin, stdout, and stderr are not directly FILE*, but #defines. Thus, naming the output struct fields stdout and stderr resulted in compiler errors, after replacing the #define. https://codeberg.org/OpenBSD/src/src/commit/a762189c5efbb2811f3c853bc0e5578fd5fb919d/include/stdio.h#L75-L77 --- lib/utils_cmd.c | 12 ++++++------ lib/utils_cmd.h | 4 ++-- plugins/check_by_ssh.c | 34 +++++++++++++++++----------------- 3 files changed, 25 insertions(+), 25 deletions(-) (limited to 'plugins/check_by_ssh.c') diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c index 42c81793..23d42168 100644 --- a/lib/utils_cmd.c +++ b/lib/utils_cmd.c @@ -489,14 +489,14 @@ cmd_run_result cmd_run2(const char *cmd_string, int flags) { cmd_run_result result = { .cmd_error_code = 0, .error_code = 0, - .stderr = + .err = { .buf = NULL, .buflen = 0, .line = NULL, .lines = 0, }, - .stdout = + .out = { .buf = NULL, .buflen = 0, @@ -581,14 +581,14 @@ cmd_run_result cmd_run_array2(char *const *cmd, int flags) { cmd_run_result result = { .cmd_error_code = 0, .error_code = 0, - .stderr = + .err = { .buf = NULL, .buflen = 0, .line = NULL, .lines = 0, }, - .stdout = + .out = { .buf = NULL, .buflen = 0, @@ -610,9 +610,9 @@ cmd_run_result cmd_run_array2(char *const *cmd, int flags) { int pfd_err[2] = {cmd_open_result.stderr_pipe_fd[0], cmd_open_result.stderr_pipe_fd[1]}; int_cmd_fetch_output2 tmp_stdout = _cmd_fetch_output2(pfd_out[0], flags); - result.stdout = tmp_stdout.output_container; + result.out = tmp_stdout.output_container; int_cmd_fetch_output2 tmp_stderr = _cmd_fetch_output2(pfd_err[0], flags); - result.stderr = tmp_stderr.output_container; + result.err = tmp_stderr.output_container; result.cmd_error_code = _cmd_close(file_descriptor); return result; diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h index d3a8f14f..04a624b8 100644 --- a/lib/utils_cmd.h +++ b/lib/utils_cmd.h @@ -24,8 +24,8 @@ int cmd_file_read(const char *, output *, int); typedef struct { int error_code; int cmd_error_code; - output stdout; - output stderr; + output out; + output err; } cmd_run_result; cmd_run_result cmd_run2(const char *cmd, int flags); cmd_run_result cmd_run_array2(char * const *cmd, int flags); diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index df8907d9..7ffa0ded 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -98,7 +98,7 @@ int main(int argc, char **argv) { if (child_result.cmd_error_code == 255 && config.unknown_timeout) { mp_subcheck sc_ssh_execution = mp_subcheck_init(); xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s", - child_result.stderr.lines > 0 ? child_result.stderr.line[0] + child_result.err.lines > 0 ? child_result.err.line[0] : "(no error output)"); sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); @@ -107,34 +107,34 @@ int main(int argc, char **argv) { } if (verbose) { - for (size_t i = 0; i < child_result.stdout.lines; i++) { - printf("stdout: %s\n", child_result.stdout.line[i]); + for (size_t i = 0; i < child_result.out.lines; i++) { + printf("stdout: %s\n", child_result.out.line[i]); } - for (size_t i = 0; i < child_result.stderr.lines; i++) { - printf("stderr: %s\n", child_result.stderr.line[i]); + for (size_t i = 0; i < child_result.err.lines; i++) { + printf("stderr: %s\n", child_result.err.line[i]); } } size_t skip_stdout = 0; if (config.skip_stdout) { /* --skip-stdout specified without argument */ - skip_stdout = child_result.stdout.lines; + skip_stdout = child_result.out.lines; } else { skip_stdout = config.stdout_lines_to_ignore; } size_t skip_stderr = 0; if (config.skip_stderr) { /* --skip-stderr specified without argument */ - skip_stderr = child_result.stderr.lines; + skip_stderr = child_result.err.lines; } else { skip_stderr = config.sterr_lines_to_ignore; } /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */ - if (child_result.stderr.lines > skip_stderr && + if (child_result.err.lines > skip_stderr && (config.unknown_on_stderr || config.warn_on_stderr)) { mp_subcheck sc_stderr = mp_subcheck_init(); xasprintf(&sc_stderr.output, "remote command execution failed: %s", - child_result.stderr.line[skip_stderr]); + child_result.err.line[skip_stderr]); if (config.unknown_on_stderr) { sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_UNKNOWN); @@ -154,10 +154,10 @@ int main(int argc, char **argv) { mp_subcheck sc_active_check = mp_subcheck_init(); xasprintf(&sc_active_check.output, "command stdout:"); - if (child_result.stdout.lines > skip_stdout) { - for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { + if (child_result.out.lines > skip_stdout) { + for (size_t i = skip_stdout; i < child_result.out.lines; i++) { xasprintf(&sc_active_check.output, "%s\n%s", sc_active_check.output, - child_result.stdout.line[i]); + child_result.out.line[i]); } } else { xasprintf(&sc_active_check.output, "remote command '%s' returned status %d", @@ -209,10 +209,10 @@ int main(int argc, char **argv) { char *status_text; int cresult; mp_subcheck sc_parse_passive = mp_subcheck_init(); - for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { - status_text = child_result.stdout.line[i++]; - if (i == child_result.stdout.lines || - strstr(child_result.stdout.line[i], "STATUS CODE: ") == NULL) { + for (size_t i = skip_stdout; i < child_result.out.lines; i++) { + status_text = child_result.out.line[i++]; + if (i == child_result.out.lines || + strstr(child_result.out.line[i], "STATUS CODE: ") == NULL) { sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_UNKNOWN); xasprintf(&sc_parse_passive.output, "failed to parse output"); @@ -221,7 +221,7 @@ int main(int argc, char **argv) { } if (config.service[commands] && status_text && - sscanf(child_result.stdout.line[i], "STATUS CODE: %d", &cresult) == 1) { + sscanf(child_result.out.line[i], "STATUS CODE: %d", &cresult) == 1) { fprintf(output_file, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time, config.host_shortname, config.service[commands++], cresult, status_text); } -- cgit v1.2.3-74-g34f1