summaryrefslogtreecommitdiffstats
path: root/plugins/check_by_ssh.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_by_ssh.c')
-rw-r--r--plugins/check_by_ssh.c187
1 files changed, 135 insertions, 52 deletions
diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c
index a43c0d34..df8907d9 100644
--- a/plugins/check_by_ssh.c
+++ b/plugins/check_by_ssh.c
@@ -26,16 +26,17 @@
26 * 26 *
27 *****************************************************************************/ 27 *****************************************************************************/
28 28
29const char *progname = "check_by_ssh";
30const char *copyright = "2000-2024";
31const char *email = "devel@monitoring-plugins.org";
32
33#include "common.h" 29#include "common.h"
30#include "output.h"
34#include "utils.h" 31#include "utils.h"
35#include "utils_cmd.h" 32#include "utils_cmd.h"
36#include "check_by_ssh.d/config.h" 33#include "check_by_ssh.d/config.h"
37#include "states.h" 34#include "states.h"
38 35
36const char *progname = "check_by_ssh";
37const char *copyright = "2000-2024";
38const char *email = "devel@monitoring-plugins.org";
39
39#ifndef NP_MAXARGS 40#ifndef NP_MAXARGS
40# define NP_MAXARGS 1024 41# define NP_MAXARGS 1024
41#endif 42#endif
@@ -71,6 +72,10 @@ int main(int argc, char **argv) {
71 72
72 const check_by_ssh_config config = tmp_config.config; 73 const check_by_ssh_config config = tmp_config.config;
73 74
75 if (config.output_format_is_set) {
76 mp_set_format(config.output_format);
77 }
78
74 /* Set signal handling and alarm timeout */ 79 /* Set signal handling and alarm timeout */
75 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 80 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
76 usage_va(_("Cannot catch SIGALRM")); 81 usage_va(_("Cannot catch SIGALRM"));
@@ -85,62 +90,99 @@ int main(int argc, char **argv) {
85 } 90 }
86 } 91 }
87 92
88 output chld_out; 93 cmd_run_result child_result = cmd_run_array2(config.cmd.commargv, 0);
89 output chld_err; 94 mp_check overall = mp_check_init();
90 mp_state_enum result = cmd_run_array(config.cmd.commargv, &chld_out, &chld_err, 0);
91 95
92 /* SSH returns 255 if connection attempt fails; include the first line of error output */ 96 /* SSH returns 255 if connection attempt fails; include the first line of error output */
93 if (result == 255 && config.unknown_timeout) { 97 // we can sadly not detect other SSH errors
94 printf(_("SSH connection failed: %s\n"), 98 if (child_result.cmd_error_code == 255 && config.unknown_timeout) {
95 chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); 99 mp_subcheck sc_ssh_execution = mp_subcheck_init();
96 return STATE_UNKNOWN; 100 xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s",
101 child_result.stderr.lines > 0 ? child_result.stderr.line[0]
102 : "(no error output)");
103
104 sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN);
105 mp_add_subcheck_to_check(&overall, sc_ssh_execution);
106 mp_exit(overall);
97 } 107 }
98 108
99 if (verbose) { 109 if (verbose) {
100 for (size_t i = 0; i < chld_out.lines; i++) { 110 for (size_t i = 0; i < child_result.stdout.lines; i++) {
101 printf("stdout: %s\n", chld_out.line[i]); 111 printf("stdout: %s\n", child_result.stdout.line[i]);
102 } 112 }
103 for (size_t i = 0; i < chld_err.lines; i++) { 113 for (size_t i = 0; i < child_result.stderr.lines; i++) {
104 printf("stderr: %s\n", chld_err.line[i]); 114 printf("stderr: %s\n", child_result.stderr.line[i]);
105 } 115 }
106 } 116 }
107 117
108 size_t skip_stdout = 0; 118 size_t skip_stdout = 0;
109 if (config.skip_stdout == -1) { /* --skip-stdout specified without argument */ 119 if (config.skip_stdout) { /* --skip-stdout specified without argument */
110 skip_stdout = chld_out.lines; 120 skip_stdout = child_result.stdout.lines;
111 } else { 121 } else {
112 skip_stdout = config.skip_stdout; 122 skip_stdout = config.stdout_lines_to_ignore;
113 } 123 }
114 124
115 size_t skip_stderr = 0; 125 size_t skip_stderr = 0;
116 if (config.skip_stderr == -1) { /* --skip-stderr specified without argument */ 126 if (config.skip_stderr) { /* --skip-stderr specified without argument */
117 skip_stderr = chld_err.lines; 127 skip_stderr = child_result.stderr.lines;
118 } else { 128 } else {
119 skip_stderr = config.skip_stderr; 129 skip_stderr = config.sterr_lines_to_ignore;
120 } 130 }
121 131
122 /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */ 132 /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */
123 if (chld_err.lines > (size_t)skip_stderr && (config.unknown_on_stderr || config.warn_on_stderr)) { 133 if (child_result.stderr.lines > skip_stderr &&
124 printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]); 134 (config.unknown_on_stderr || config.warn_on_stderr)) {
135 mp_subcheck sc_stderr = mp_subcheck_init();
136 xasprintf(&sc_stderr.output, "remote command execution failed: %s",
137 child_result.stderr.line[skip_stderr]);
138
125 if (config.unknown_on_stderr) { 139 if (config.unknown_on_stderr) {
126 return max_state_alt(result, STATE_UNKNOWN); 140 sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_UNKNOWN);
127 } else if (config.warn_on_stderr) { 141 }
128 return max_state_alt(result, STATE_WARNING); 142
143 if (config.warn_on_stderr) {
144 sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_WARNING);
129 } 145 }
146
147 mp_add_subcheck_to_check(&overall, sc_stderr);
148 // TODO still exit here?
130 } 149 }
131 150
132 /* this is simple if we're not supposed to be passive. 151 /* this is simple if we're not supposed to be passive.
133 * Wrap up quickly and keep the tricks below */ 152 * Wrap up quickly and keep the tricks below */
134 if (!config.passive) { 153 if (!config.passive) {
135 if (chld_out.lines > (size_t)skip_stdout) { 154 mp_subcheck sc_active_check = mp_subcheck_init();
136 for (size_t i = skip_stdout; i < chld_out.lines; i++) { 155 xasprintf(&sc_active_check.output, "command stdout:");
137 puts(chld_out.line[i]); 156
157 if (child_result.stdout.lines > skip_stdout) {
158 for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) {
159 xasprintf(&sc_active_check.output, "%s\n%s", sc_active_check.output,
160 child_result.stdout.line[i]);
138 } 161 }
139 } else { 162 } else {
140 printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), 163 xasprintf(&sc_active_check.output, "remote command '%s' returned status %d",
141 state_text(result), config.remotecmd, result); 164 config.remotecmd, child_result.cmd_error_code);
165 }
166
167 /* return error status from remote command */
168
169 switch (child_result.cmd_error_code) {
170 case 0:
171 sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_OK);
172 break;
173 case 1:
174 sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_WARNING);
175 break;
176 case 2:
177 sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_CRITICAL);
178 break;
179 default:
180 sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_UNKNOWN);
181 break;
142 } 182 }
143 return result; /* return error status from remote command */ 183
184 mp_add_subcheck_to_check(&overall, sc_active_check);
185 mp_exit(overall);
144 } 186 }
145 187
146 /* 188 /*
@@ -148,36 +190,57 @@ int main(int argc, char **argv) {
148 */ 190 */
149 191
150 /* process output */ 192 /* process output */
151 FILE *file_pointer = NULL; 193 mp_subcheck sc_passive_file = mp_subcheck_init();
152 if (!(file_pointer = fopen(config.outputfile, "a"))) { 194 FILE *output_file = NULL;
153 printf(_("SSH WARNING: could not open %s\n"), config.outputfile); 195 if (!(output_file = fopen(config.outputfile, "a"))) {
154 exit(STATE_UNKNOWN); 196 xasprintf(&sc_passive_file.output, "could not open %s", config.outputfile);
197 sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_UNKNOWN);
198
199 mp_add_subcheck_to_check(&overall, sc_passive_file);
200 mp_exit(overall);
155 } 201 }
156 202
203 xasprintf(&sc_passive_file.output, "opened output file %s", config.outputfile);
204 sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_OK);
205 mp_add_subcheck_to_check(&overall, sc_passive_file);
206
157 time_t local_time = time(NULL); 207 time_t local_time = time(NULL);
158 unsigned int commands = 0; 208 unsigned int commands = 0;
159 char *status_text; 209 char *status_text;
160 int cresult; 210 int cresult;
161 for (size_t i = skip_stdout; i < chld_out.lines; i++) { 211 mp_subcheck sc_parse_passive = mp_subcheck_init();
162 status_text = chld_out.line[i++]; 212 for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) {
163 if (i == chld_out.lines || strstr(chld_out.line[i], "STATUS CODE: ") == NULL) { 213 status_text = child_result.stdout.line[i++];
164 die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); 214 if (i == child_result.stdout.lines ||
215 strstr(child_result.stdout.line[i], "STATUS CODE: ") == NULL) {
216
217 sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_UNKNOWN);
218 xasprintf(&sc_parse_passive.output, "failed to parse output");
219 mp_add_subcheck_to_check(&overall, sc_parse_passive);
220 mp_exit(overall);
165 } 221 }
166 222
167 if (config.service[commands] && status_text && 223 if (config.service[commands] && status_text &&
168 sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { 224 sscanf(child_result.stdout.line[i], "STATUS CODE: %d", &cresult) == 1) {
169 fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", 225 fprintf(output_file, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time,
170 (int)local_time, config.host_shortname, config.service[commands++], cresult, 226 config.host_shortname, config.service[commands++], cresult, status_text);
171 status_text);
172 } 227 }
173 } 228 }
174 229
230 sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_OK);
231 xasprintf(&sc_parse_passive.output, "parsed and wrote output");
232 mp_add_subcheck_to_check(&overall, sc_parse_passive);
233
175 /* Multiple commands and passive checking should always return OK */ 234 /* Multiple commands and passive checking should always return OK */
176 exit(result); 235 mp_exit(overall);
177} 236}
178 237
179/* process command-line arguments */ 238/* process command-line arguments */
180check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { 239check_by_ssh_config_wrapper process_arguments(int argc, char **argv) {
240 enum {
241 output_format_index = CHAR_MAX + 1,
242 };
243
181 static struct option longopts[] = { 244 static struct option longopts[] = {
182 {"version", no_argument, 0, 'V'}, 245 {"version", no_argument, 0, 'V'},
183 {"help", no_argument, 0, 'h'}, 246 {"help", no_argument, 0, 'h'},
@@ -207,6 +270,7 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) {
207 {"ssh-option", required_argument, 0, 'o'}, 270 {"ssh-option", required_argument, 0, 'o'},
208 {"quiet", no_argument, 0, 'q'}, 271 {"quiet", no_argument, 0, 'q'},
209 {"configfile", optional_argument, 0, 'F'}, 272 {"configfile", optional_argument, 0, 'F'},
273 {"output-format", required_argument, 0, output_format_index},
210 {0, 0, 0, 0}}; 274 {0, 0, 0, 0}};
211 275
212 check_by_ssh_config_wrapper result = { 276 check_by_ssh_config_wrapper result = {
@@ -327,20 +391,27 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) {
327 break; 391 break;
328 case 'S': /* skip n (or all) lines on stdout */ 392 case 'S': /* skip n (or all) lines on stdout */
329 if (optarg == NULL) { 393 if (optarg == NULL) {
330 result.config.skip_stdout = -1; /* skip all output on stdout */ 394 result.config.skip_stdout = true; /* skip all output on stdout */
395
396 if (verbose) {
397 printf("Setting the skip_stdout flag\n");
398 }
331 } else if (!is_integer(optarg)) { 399 } else if (!is_integer(optarg)) {
332 usage_va(_("skip-stdout argument must be an integer")); 400 usage_va(_("skip-stdout argument must be an integer"));
333 } else { 401 } else {
334 result.config.skip_stdout = atoi(optarg); 402 result.config.stdout_lines_to_ignore = atoi(optarg);
335 } 403 }
336 break; 404 break;
337 case 'E': /* skip n (or all) lines on stderr */ 405 case 'E': /* skip n (or all) lines on stderr */
338 if (optarg == NULL) { 406 if (optarg == NULL) {
339 result.config.skip_stderr = -1; /* skip all output on stderr */ 407 result.config.skip_stderr = true; /* skip all output on stderr */
408 if (verbose) {
409 printf("Setting the skip_stderr flag\n");
410 }
340 } else if (!is_integer(optarg)) { 411 } else if (!is_integer(optarg)) {
341 usage_va(_("skip-stderr argument must be an integer")); 412 usage_va(_("skip-stderr argument must be an integer"));
342 } else { 413 } else {
343 result.config.skip_stderr = atoi(optarg); 414 result.config.sterr_lines_to_ignore = atoi(optarg);
344 } 415 }
345 break; 416 break;
346 case 'e': /* exit with unknown if there is an output on stderr */ 417 case 'e': /* exit with unknown if there is an output on stderr */
@@ -360,6 +431,18 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) {
360 result.config.cmd = comm_append(result.config.cmd, "-F"); 431 result.config.cmd = comm_append(result.config.cmd, "-F");
361 result.config.cmd = comm_append(result.config.cmd, optarg); 432 result.config.cmd = comm_append(result.config.cmd, optarg);
362 break; 433 break;
434 case output_format_index: {
435 parsed_output_format parser = mp_parse_output_format(optarg);
436 if (!parser.parsing_success) {
437 // TODO List all available formats here, maybe add anothoer usage function
438 printf("Invalid output format: %s\n", optarg);
439 exit(STATE_UNKNOWN);
440 }
441
442 result.config.output_format_is_set = true;
443 result.config.output_format = parser.output_format;
444 break;
445 }
363 default: /* help */ 446 default: /* help */
364 usage5(); 447 usage5();
365 } 448 }
@@ -502,6 +585,7 @@ void print_help(void) {
502 printf(" %s\n", "-U, --unknown-timeout"); 585 printf(" %s\n", "-U, --unknown-timeout");
503 printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL")); 586 printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL"));
504 printf(UT_VERBOSE); 587 printf(UT_VERBOSE);
588 printf(UT_OUTPUT_FORMAT);
505 printf("\n"); 589 printf("\n");
506 printf(" %s\n", _("The most common mode of use is to refer to a local identity file with")); 590 printf(" %s\n", _("The most common mode of use is to refer to a local identity file with"));
507 printf(" %s\n", _("the '-i' option. In this mode, the identity pair should have a null")); 591 printf(" %s\n", _("the '-i' option. In this mode, the identity pair should have a null"));
@@ -515,9 +599,8 @@ void print_help(void) {
515 printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)")); 599 printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)"));
516 printf("\n"); 600 printf("\n");
517 printf("%s\n", _("Examples:")); 601 printf("%s\n", _("Examples:"));
518 printf( 602 printf(" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C "
519 " %s\n", 603 "uptime -O /tmp/foo");
520 "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo");
521 printf(" %s\n", "$ cat /tmp/foo"); 604 printf(" %s\n", "$ cat /tmp/foo");
522 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days"); 605 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days");
523 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days"); 606 printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days");