diff options
Diffstat (limited to 'plugins/check_by_ssh.c')
| -rw-r--r-- | plugins/check_by_ssh.c | 184 |
1 files changed, 135 insertions, 49 deletions
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 @@ | |||
| 26 | * | 26 | * |
| 27 | *****************************************************************************/ | 27 | *****************************************************************************/ |
| 28 | 28 | ||
| 29 | const char *progname = "check_by_ssh"; | ||
| 30 | const char *copyright = "2000-2024"; | ||
| 31 | const 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 | ||
| 36 | const char *progname = "check_by_ssh"; | ||
| 37 | const char *copyright = "2000-2024"; | ||
| 38 | const 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,101 @@ 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 | mp_subcheck sc_ssh_execution = mp_subcheck_init(); |
| 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 | xasprintf(&sc_ssh_execution.output, "SSH connection failed: %s", |
| 96 | return STATE_UNKNOWN; | 100 | child_result.stderr.lines > 0 ? child_result.stderr.line[0] |
| 101 | : "(no error output)"); | ||
| 102 | |||
| 103 | sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_UNKNOWN); | ||
| 104 | mp_add_subcheck_to_check(&overall, sc_ssh_execution); | ||
| 105 | mp_exit(overall); | ||
| 97 | } | 106 | } |
| 107 | xasprintf(&sc_ssh_execution.output, "SSH connection succeeded"); | ||
| 108 | sc_ssh_execution = mp_set_subcheck_state(sc_ssh_execution, STATE_OK); | ||
| 109 | mp_add_subcheck_to_check(&overall, sc_ssh_execution); | ||
| 98 | 110 | ||
| 99 | if (verbose) { | 111 | if (verbose) { |
| 100 | for (size_t i = 0; i < chld_out.lines; i++) { | 112 | for (size_t i = 0; i < child_result.stdout.lines; i++) { |
| 101 | printf("stdout: %s\n", chld_out.line[i]); | 113 | printf("stdout: %s\n", child_result.stdout.line[i]); |
| 102 | } | 114 | } |
| 103 | for (size_t i = 0; i < chld_err.lines; i++) { | 115 | for (size_t i = 0; i < child_result.stderr.lines; i++) { |
| 104 | printf("stderr: %s\n", chld_err.line[i]); | 116 | printf("stderr: %s\n", child_result.stderr.line[i]); |
| 105 | } | 117 | } |
| 106 | } | 118 | } |
| 107 | 119 | ||
| 108 | size_t skip_stdout = 0; | 120 | size_t skip_stdout = 0; |
| 109 | if (config.skip_stdout == -1) { /* --skip-stdout specified without argument */ | 121 | if (config.skip_stdout) { /* --skip-stdout specified without argument */ |
| 110 | skip_stdout = chld_out.lines; | 122 | skip_stdout = child_result.stdout.lines; |
| 111 | } else { | 123 | } else { |
| 112 | skip_stdout = config.skip_stdout; | 124 | skip_stdout = config.stdout_lines_to_ignore; |
| 113 | } | 125 | } |
| 114 | 126 | ||
| 115 | size_t skip_stderr = 0; | 127 | size_t skip_stderr = 0; |
| 116 | if (config.skip_stderr == -1) { /* --skip-stderr specified without argument */ | 128 | if (config.skip_stderr) { /* --skip-stderr specified without argument */ |
| 117 | skip_stderr = chld_err.lines; | 129 | skip_stderr = child_result.stderr.lines; |
| 118 | } else { | 130 | } else { |
| 119 | skip_stderr = config.skip_stderr; | 131 | skip_stderr = config.sterr_lines_to_ignore; |
| 120 | } | 132 | } |
| 121 | 133 | ||
| 122 | /* Allow UNKNOWN or WARNING state for (non-skipped) output found on stderr */ | 134 | /* 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)) { | 135 | if (child_result.stderr.lines > skip_stderr && |
| 124 | printf(_("Remote command execution failed: %s\n"), chld_err.line[skip_stderr]); | 136 | (config.unknown_on_stderr || config.warn_on_stderr)) { |
| 137 | mp_subcheck sc_stderr = mp_subcheck_init(); | ||
| 138 | xasprintf(&sc_stderr.output, "remote command execution failed: %s", | ||
| 139 | child_result.stderr.line[skip_stderr]); | ||
| 140 | |||
| 125 | if (config.unknown_on_stderr) { | 141 | if (config.unknown_on_stderr) { |
| 126 | return max_state_alt(result, STATE_UNKNOWN); | 142 | sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_UNKNOWN); |
| 127 | } else if (config.warn_on_stderr) { | 143 | } |
| 128 | return max_state_alt(result, STATE_WARNING); | 144 | |
| 145 | if (config.warn_on_stderr) { | ||
| 146 | sc_stderr = mp_set_subcheck_state(sc_stderr, STATE_WARNING); | ||
| 129 | } | 147 | } |
| 148 | |||
| 149 | mp_add_subcheck_to_check(&overall, sc_stderr); | ||
| 150 | // TODO still exit here? | ||
| 130 | } | 151 | } |
| 131 | 152 | ||
| 132 | /* this is simple if we're not supposed to be passive. | 153 | /* this is simple if we're not supposed to be passive. |
| 133 | * Wrap up quickly and keep the tricks below */ | 154 | * Wrap up quickly and keep the tricks below */ |
| 134 | if (!config.passive) { | 155 | if (!config.passive) { |
| 135 | if (chld_out.lines > (size_t)skip_stdout) { | 156 | mp_subcheck sc_active_check = mp_subcheck_init(); |
| 136 | for (size_t i = skip_stdout; i < chld_out.lines; i++) { | 157 | xasprintf(&sc_active_check.output, "command stdout:"); |
| 137 | puts(chld_out.line[i]); | 158 | |
| 159 | if (child_result.stdout.lines > skip_stdout) { | ||
| 160 | for (size_t i = skip_stdout; i < child_result.stdout.lines; i++) { | ||
| 161 | xasprintf(&sc_active_check.output, "%s\n%s", sc_active_check.output, | ||
| 162 | child_result.stdout.line[i]); | ||
| 138 | } | 163 | } |
| 139 | } else { | 164 | } else { |
| 140 | printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), | 165 | xasprintf(&sc_active_check.output, "remote command '%s' returned status %d", |
| 141 | state_text(result), config.remotecmd, result); | 166 | config.remotecmd, child_result.cmd_error_code); |
| 167 | } | ||
| 168 | |||
| 169 | /* return error status from remote command */ | ||
| 170 | |||
| 171 | switch (child_result.cmd_error_code) { | ||
| 172 | case 0: | ||
| 173 | sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_OK); | ||
| 174 | break; | ||
| 175 | case 1: | ||
| 176 | sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_WARNING); | ||
| 177 | break; | ||
| 178 | case 2: | ||
| 179 | sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_CRITICAL); | ||
| 180 | break; | ||
| 181 | default: | ||
| 182 | sc_active_check = mp_set_subcheck_state(sc_active_check, STATE_UNKNOWN); | ||
| 183 | break; | ||
| 142 | } | 184 | } |
| 143 | return result; /* return error status from remote command */ | 185 | |
| 186 | mp_add_subcheck_to_check(&overall, sc_active_check); | ||
| 187 | mp_exit(overall); | ||
| 144 | } | 188 | } |
| 145 | 189 | ||
| 146 | /* | 190 | /* |
| @@ -148,36 +192,57 @@ int main(int argc, char **argv) { | |||
| 148 | */ | 192 | */ |
| 149 | 193 | ||
| 150 | /* process output */ | 194 | /* process output */ |
| 151 | FILE *file_pointer = NULL; | 195 | mp_subcheck sc_passive_file = mp_subcheck_init(); |
| 152 | if (!(file_pointer = fopen(config.outputfile, "a"))) { | 196 | FILE *output_file = NULL; |
| 153 | printf(_("SSH WARNING: could not open %s\n"), config.outputfile); | 197 | if (!(output_file = fopen(config.outputfile, "a"))) { |
| 154 | exit(STATE_UNKNOWN); | 198 | xasprintf(&sc_passive_file.output, "could not open %s", config.outputfile); |
| 199 | sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_UNKNOWN); | ||
| 200 | |||
| 201 | mp_add_subcheck_to_check(&overall, sc_passive_file); | ||
| 202 | mp_exit(overall); | ||
| 155 | } | 203 | } |
| 156 | 204 | ||
| 205 | xasprintf(&sc_passive_file.output, "opened output file %s", config.outputfile); | ||
| 206 | sc_passive_file = mp_set_subcheck_state(sc_passive_file, STATE_OK); | ||
| 207 | mp_add_subcheck_to_check(&overall, sc_passive_file); | ||
| 208 | |||
| 157 | time_t local_time = time(NULL); | 209 | time_t local_time = time(NULL); |
| 158 | unsigned int commands = 0; | 210 | unsigned int commands = 0; |
| 159 | char *status_text; | 211 | char *status_text; |
| 160 | int cresult; | 212 | int cresult; |
| 161 | for (size_t i = skip_stdout; i < chld_out.lines; i++) { | 213 | mp_subcheck sc_parse_passive = mp_subcheck_init(); |
| 162 | status_text = chld_out.line[i++]; | 214 | 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) { | 215 | status_text = child_result.stdout.line[i++]; |
| 164 | die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); | 216 | if (i == child_result.stdout.lines || |
| 217 | strstr(child_result.stdout.line[i], "STATUS CODE: ") == NULL) { | ||
| 218 | |||
| 219 | sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_UNKNOWN); | ||
| 220 | xasprintf(&sc_parse_passive.output, "failed to parse output"); | ||
| 221 | mp_add_subcheck_to_check(&overall, sc_parse_passive); | ||
| 222 | mp_exit(overall); | ||
| 165 | } | 223 | } |
| 166 | 224 | ||
| 167 | if (config.service[commands] && status_text && | 225 | if (config.service[commands] && status_text && |
| 168 | sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { | 226 | 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", | 227 | 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, | 228 | config.host_shortname, config.service[commands++], cresult, status_text); |
| 171 | status_text); | ||
| 172 | } | 229 | } |
| 173 | } | 230 | } |
| 174 | 231 | ||
| 232 | sc_parse_passive = mp_set_subcheck_state(sc_parse_passive, STATE_OK); | ||
| 233 | xasprintf(&sc_parse_passive.output, "parsed and wrote output"); | ||
| 234 | mp_add_subcheck_to_check(&overall, sc_parse_passive); | ||
| 235 | |||
| 175 | /* Multiple commands and passive checking should always return OK */ | 236 | /* Multiple commands and passive checking should always return OK */ |
| 176 | exit(result); | 237 | mp_exit(overall); |
| 177 | } | 238 | } |
| 178 | 239 | ||
| 179 | /* process command-line arguments */ | 240 | /* process command-line arguments */ |
| 180 | check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { | 241 | check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { |
| 242 | enum { | ||
| 243 | output_format_index = CHAR_MAX + 1, | ||
| 244 | }; | ||
| 245 | |||
| 181 | static struct option longopts[] = { | 246 | static struct option longopts[] = { |
| 182 | {"version", no_argument, 0, 'V'}, | 247 | {"version", no_argument, 0, 'V'}, |
| 183 | {"help", no_argument, 0, 'h'}, | 248 | {"help", no_argument, 0, 'h'}, |
| @@ -207,6 +272,7 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { | |||
| 207 | {"ssh-option", required_argument, 0, 'o'}, | 272 | {"ssh-option", required_argument, 0, 'o'}, |
| 208 | {"quiet", no_argument, 0, 'q'}, | 273 | {"quiet", no_argument, 0, 'q'}, |
| 209 | {"configfile", optional_argument, 0, 'F'}, | 274 | {"configfile", optional_argument, 0, 'F'}, |
| 275 | {"output-format", required_argument, 0, output_format_index}, | ||
| 210 | {0, 0, 0, 0}}; | 276 | {0, 0, 0, 0}}; |
| 211 | 277 | ||
| 212 | check_by_ssh_config_wrapper result = { | 278 | check_by_ssh_config_wrapper result = { |
| @@ -327,20 +393,27 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { | |||
| 327 | break; | 393 | break; |
| 328 | case 'S': /* skip n (or all) lines on stdout */ | 394 | case 'S': /* skip n (or all) lines on stdout */ |
| 329 | if (optarg == NULL) { | 395 | if (optarg == NULL) { |
| 330 | result.config.skip_stdout = -1; /* skip all output on stdout */ | 396 | result.config.skip_stdout = true; /* skip all output on stdout */ |
| 397 | |||
| 398 | if (verbose) { | ||
| 399 | printf("Setting the skip_stdout flag\n"); | ||
| 400 | } | ||
| 331 | } else if (!is_integer(optarg)) { | 401 | } else if (!is_integer(optarg)) { |
| 332 | usage_va(_("skip-stdout argument must be an integer")); | 402 | usage_va(_("skip-stdout argument must be an integer")); |
| 333 | } else { | 403 | } else { |
| 334 | result.config.skip_stdout = atoi(optarg); | 404 | result.config.stdout_lines_to_ignore = atoi(optarg); |
| 335 | } | 405 | } |
| 336 | break; | 406 | break; |
| 337 | case 'E': /* skip n (or all) lines on stderr */ | 407 | case 'E': /* skip n (or all) lines on stderr */ |
| 338 | if (optarg == NULL) { | 408 | if (optarg == NULL) { |
| 339 | result.config.skip_stderr = -1; /* skip all output on stderr */ | 409 | result.config.skip_stderr = true; /* skip all output on stderr */ |
| 410 | if (verbose) { | ||
| 411 | printf("Setting the skip_stderr flag\n"); | ||
| 412 | } | ||
| 340 | } else if (!is_integer(optarg)) { | 413 | } else if (!is_integer(optarg)) { |
| 341 | usage_va(_("skip-stderr argument must be an integer")); | 414 | usage_va(_("skip-stderr argument must be an integer")); |
| 342 | } else { | 415 | } else { |
| 343 | result.config.skip_stderr = atoi(optarg); | 416 | result.config.sterr_lines_to_ignore = atoi(optarg); |
| 344 | } | 417 | } |
| 345 | break; | 418 | break; |
| 346 | case 'e': /* exit with unknown if there is an output on stderr */ | 419 | 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) { | |||
| 360 | result.config.cmd = comm_append(result.config.cmd, "-F"); | 433 | result.config.cmd = comm_append(result.config.cmd, "-F"); |
| 361 | result.config.cmd = comm_append(result.config.cmd, optarg); | 434 | result.config.cmd = comm_append(result.config.cmd, optarg); |
| 362 | break; | 435 | break; |
| 436 | case output_format_index: { | ||
| 437 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 438 | if (!parser.parsing_success) { | ||
| 439 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 440 | printf("Invalid output format: %s\n", optarg); | ||
| 441 | exit(STATE_UNKNOWN); | ||
| 442 | } | ||
| 443 | |||
| 444 | result.config.output_format_is_set = true; | ||
| 445 | result.config.output_format = parser.output_format; | ||
| 446 | break; | ||
| 447 | } | ||
| 363 | default: /* help */ | 448 | default: /* help */ |
| 364 | usage5(); | 449 | usage5(); |
| 365 | } | 450 | } |
| @@ -502,6 +587,7 @@ void print_help(void) { | |||
| 502 | printf(" %s\n", "-U, --unknown-timeout"); | 587 | printf(" %s\n", "-U, --unknown-timeout"); |
| 503 | printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL")); | 588 | printf(" %s\n", _("Make connection problems return UNKNOWN instead of CRITICAL")); |
| 504 | printf(UT_VERBOSE); | 589 | printf(UT_VERBOSE); |
| 590 | printf(UT_OUTPUT_FORMAT); | ||
| 505 | printf("\n"); | 591 | printf("\n"); |
| 506 | printf(" %s\n", _("The most common mode of use is to refer to a local identity file with")); | 592 | 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")); | 593 | printf(" %s\n", _("the '-i' option. In this mode, the identity pair should have a null")); |
