summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDennis <13968467+Decstasy@users.noreply.github.com>2025-11-25 15:02:52 +0100
committerGitHub <noreply@github.com>2025-11-25 15:02:52 +0100
commitcedfc166d42f4e89dddc1caa44e0655157d35a0a (patch)
treeee980d8df72bc9611099f6a997274831098d4175
parent978343ed03d3327597fa3317901f1c1741a7e3c4 (diff)
parent3657197cf77ca78f6e2d003a71d48dc5d4dc45ae (diff)
downloadmonitoring-plugins-cedfc166.tar.gz
Merge branch 'master' into check_dig_flags_feature
-rw-r--r--.github/workflows/codeql-analysis.yml6
-rw-r--r--.github/workflows/spellcheck.yml2
-rw-r--r--.github/workflows/test-next.yml4
-rw-r--r--.github/workflows/test.yml4
-rw-r--r--lib/utils_cmd.c300
-rw-r--r--lib/utils_cmd.h10
-rw-r--r--plugins/check_by_ssh.c187
-rw-r--r--plugins/check_by_ssh.d/config.h21
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c20
-rw-r--r--plugins/check_dbi.c715
-rw-r--r--plugins/check_dbi.d/config.h31
-rw-r--r--plugins/check_ldap.c333
-rw-r--r--plugins/check_ldap.d/config.h24
-rw-r--r--plugins/check_mysql.c277
-rw-r--r--plugins/check_mysql.d/config.h8
-rw-r--r--plugins/check_mysql_query.c105
-rw-r--r--plugins/check_mysql_query.d/config.h4
-rw-r--r--plugins/check_ntp_peer.c360
-rw-r--r--plugins/check_ntp_peer.d/config.h63
-rw-r--r--plugins/check_ntp_time.c188
-rw-r--r--plugins/check_ntp_time.d/config.h19
-rw-r--r--plugins/check_pgsql.c292
-rw-r--r--plugins/check_pgsql.d/config.h29
-rw-r--r--plugins/check_smtp.c766
-rw-r--r--plugins/check_smtp.d/config.h28
-rw-r--r--plugins/netutils.h20
-rw-r--r--plugins/runcmd.c5
-rw-r--r--plugins/sslutils.c132
-rw-r--r--plugins/t/check_by_ssh.t50
-rw-r--r--plugins/t/check_dbi.t10
-rw-r--r--plugins/t/check_ldap.t14
-rw-r--r--plugins/t/check_mysql.t7
-rw-r--r--plugins/t/check_mysql_query.t2
-rw-r--r--plugins/t/check_smtp.t7
34 files changed, 2638 insertions, 1405 deletions
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index e01aa5fc..bd1037f4 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -41,11 +41,11 @@ jobs:
41 41
42 steps: 42 steps:
43 - name: Checkout repository 43 - name: Checkout repository
44 uses: actions/checkout@v5 44 uses: actions/checkout@v6
45 45
46 # Initializes the CodeQL tools for scanning. 46 # Initializes the CodeQL tools for scanning.
47 - name: Initialize CodeQL 47 - name: Initialize CodeQL
48 uses: github/codeql-action/init@v3 48 uses: github/codeql-action/init@v4
49 with: 49 with:
50 languages: ${{ matrix.language }} 50 languages: ${{ matrix.language }}
51 # If you wish to specify custom queries, you can do so here or in a config file. 51 # If you wish to specify custom queries, you can do so here or in a config file.
@@ -82,4 +82,4 @@ jobs:
82 make 82 make
83 83
84 - name: Perform CodeQL Analysis 84 - name: Perform CodeQL Analysis
85 uses: github/codeql-action/analyze@v3 85 uses: github/codeql-action/analyze@v4
diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml
index 14b82781..f19cc920 100644
--- a/.github/workflows/spellcheck.yml
+++ b/.github/workflows/spellcheck.yml
@@ -18,7 +18,7 @@ jobs:
18 runs-on: ubuntu-latest 18 runs-on: ubuntu-latest
19 steps: 19 steps:
20 - name: Checkout 20 - name: Checkout
21 uses: actions/checkout@v5 21 uses: actions/checkout@v6
22 - name: Codespell 22 - name: Codespell
23 uses: codespell-project/actions-codespell@v2 23 uses: codespell-project/actions-codespell@v2
24 with: 24 with:
diff --git a/.github/workflows/test-next.yml b/.github/workflows/test-next.yml
index 0e69c251..a7e9b9d6 100644
--- a/.github/workflows/test-next.yml
+++ b/.github/workflows/test-next.yml
@@ -30,7 +30,7 @@ jobs:
30 prepare: .github/prepare_debian.sh 30 prepare: .github/prepare_debian.sh
31 steps: 31 steps:
32 - name: Git clone repository 32 - name: Git clone repository
33 uses: actions/checkout@v5 33 uses: actions/checkout@v6
34 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate 34 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate
35 uses: mxschmitt/action-tmate@v3 35 uses: mxschmitt/action-tmate@v3
36 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} 36 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
@@ -68,7 +68,7 @@ jobs:
68 - {"distro": "fedora:rawhide", "build": ".github/mock.sh"} 68 - {"distro": "fedora:rawhide", "build": ".github/mock.sh"}
69 steps: 69 steps:
70 - name: Git clone repository 70 - name: Git clone repository
71 uses: actions/checkout@v5 71 uses: actions/checkout@v6
72 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate 72 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate
73 uses: mxschmitt/action-tmate@v3 73 uses: mxschmitt/action-tmate@v3
74 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} 74 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 1ac8aaf3..5a0b2943 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -28,7 +28,7 @@ jobs:
28 prepare: .github/prepare_debian.sh 28 prepare: .github/prepare_debian.sh
29 steps: 29 steps:
30 - name: Git clone repository 30 - name: Git clone repository
31 uses: actions/checkout@v5 31 uses: actions/checkout@v6
32 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate 32 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate
33 uses: mxschmitt/action-tmate@v3 33 uses: mxschmitt/action-tmate@v3
34 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} 34 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
@@ -69,7 +69,7 @@ jobs:
69# - {"distro": "oraclelinux:9", "build": ".github/mock.sh"} 69# - {"distro": "oraclelinux:9", "build": ".github/mock.sh"}
70 steps: 70 steps:
71 - name: Git clone repository 71 - name: Git clone repository
72 uses: actions/checkout@v5 72 uses: actions/checkout@v6
73 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate 73 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate
74 uses: mxschmitt/action-tmate@v3 74 uses: mxschmitt/action-tmate@v3
75 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} 75 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c
index 35b83297..42c81793 100644
--- a/lib/utils_cmd.c
+++ b/lib/utils_cmd.c
@@ -56,6 +56,7 @@ static pid_t *_cmd_pids = NULL;
56#include "./maxfd.h" 56#include "./maxfd.h"
57 57
58#include <fcntl.h> 58#include <fcntl.h>
59#include <stddef.h>
59 60
60#ifdef HAVE_SYS_WAIT_H 61#ifdef HAVE_SYS_WAIT_H
61# include <sys/wait.h> 62# include <sys/wait.h>
@@ -79,7 +80,8 @@ static pid_t *_cmd_pids = NULL;
79static int _cmd_open(char *const *argv, int *pfd, int *pfderr) 80static int _cmd_open(char *const *argv, int *pfd, int *pfderr)
80 __attribute__((__nonnull__(1, 2, 3))); 81 __attribute__((__nonnull__(1, 2, 3)));
81 82
82static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) __attribute__((__nonnull__(2))); 83static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags)
84 __attribute__((__nonnull__(2)));
83 85
84static int _cmd_close(int fileDescriptor); 86static int _cmd_close(int fileDescriptor);
85 87
@@ -102,14 +104,85 @@ void cmd_init(void) {
102 } 104 }
103} 105}
104 106
107typedef struct {
108 int stdout_pipe_fd[2];
109 int stderr_pipe_fd[2];
110 int file_descriptor;
111 int error_code;
112} int_cmd_open_result;
113static int_cmd_open_result _cmd_open2(char *const *argv) {
114#ifdef RLIMIT_CORE
115 struct rlimit limit;
116#endif
117
118 if (!_cmd_pids) {
119 CMD_INIT;
120 }
121
122 setenv("LC_ALL", "C", 1);
123
124 int_cmd_open_result result = {
125 .error_code = 0,
126 .stdout_pipe_fd = {0, 0},
127 .stderr_pipe_fd = {0, 0},
128 };
129 pid_t pid;
130 if (pipe(result.stdout_pipe_fd) < 0 || pipe(result.stderr_pipe_fd) < 0 || (pid = fork()) < 0) {
131 result.error_code = -1;
132 return result; /* errno set by the failing function */
133 }
134
135 /* child runs exceve() and _exit. */
136 if (pid == 0) {
137#ifdef RLIMIT_CORE
138 /* the program we execve shouldn't leave core files */
139 getrlimit(RLIMIT_CORE, &limit);
140 limit.rlim_cur = 0;
141 setrlimit(RLIMIT_CORE, &limit);
142#endif
143 close(result.stdout_pipe_fd[0]);
144 if (result.stdout_pipe_fd[1] != STDOUT_FILENO) {
145 dup2(result.stdout_pipe_fd[1], STDOUT_FILENO);
146 close(result.stdout_pipe_fd[1]);
147 }
148 close(result.stderr_pipe_fd[0]);
149 if (result.stderr_pipe_fd[1] != STDERR_FILENO) {
150 dup2(result.stderr_pipe_fd[1], STDERR_FILENO);
151 close(result.stderr_pipe_fd[1]);
152 }
153
154 /* close all descriptors in _cmd_pids[]
155 * This is executed in a separate address space (pure child),
156 * so we don't have to worry about async safety */
157 long maxfd = mp_open_max();
158 for (int i = 0; i < maxfd; i++) {
159 if (_cmd_pids[i] > 0) {
160 close(i);
161 }
162 }
163
164 execve(argv[0], argv, environ);
165 _exit(STATE_UNKNOWN);
166 }
167
168 /* parent picks up execution here */
169 /* close children descriptors in our address space */
170 close(result.stdout_pipe_fd[1]);
171 close(result.stderr_pipe_fd[1]);
172
173 /* tag our file's entry in the pid-list and return it */
174 _cmd_pids[result.stdout_pipe_fd[0]] = pid;
175
176 result.file_descriptor = result.stdout_pipe_fd[0];
177 return result;
178}
179
105/* Start running a command, array style */ 180/* Start running a command, array style */
106static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { 181static int _cmd_open(char *const *argv, int *pfd, int *pfderr) {
107#ifdef RLIMIT_CORE 182#ifdef RLIMIT_CORE
108 struct rlimit limit; 183 struct rlimit limit;
109#endif 184#endif
110 185
111 int i = 0;
112
113 if (!_cmd_pids) { 186 if (!_cmd_pids) {
114 CMD_INIT; 187 CMD_INIT;
115 } 188 }
@@ -144,7 +217,7 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) {
144 * This is executed in a separate address space (pure child), 217 * This is executed in a separate address space (pure child),
145 * so we don't have to worry about async safety */ 218 * so we don't have to worry about async safety */
146 long maxfd = mp_open_max(); 219 long maxfd = mp_open_max();
147 for (i = 0; i < maxfd; i++) { 220 for (int i = 0; i < maxfd; i++) {
148 if (_cmd_pids[i] > 0) { 221 if (_cmd_pids[i] > 0) {
149 close(i); 222 close(i);
150 } 223 }
@@ -192,6 +265,87 @@ static int _cmd_close(int fileDescriptor) {
192 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; 265 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1;
193} 266}
194 267
268typedef struct {
269 int error_code;
270 output output_container;
271} int_cmd_fetch_output2;
272static int_cmd_fetch_output2 _cmd_fetch_output2(int fileDescriptor, int flags) {
273 char tmpbuf[4096];
274
275 int_cmd_fetch_output2 result = {
276 .error_code = 0,
277 .output_container =
278 {
279 .buf = NULL,
280 .buflen = 0,
281 .line = NULL,
282 .lines = 0,
283 },
284 };
285 ssize_t ret;
286 while ((ret = read(fileDescriptor, tmpbuf, sizeof(tmpbuf))) > 0) {
287 size_t len = (size_t)ret;
288 result.output_container.buf =
289 realloc(result.output_container.buf, result.output_container.buflen + len + 1);
290 memcpy(result.output_container.buf + result.output_container.buflen, tmpbuf, len);
291 result.output_container.buflen += len;
292 }
293
294 if (ret < 0) {
295 printf("read() returned %zd: %s\n", ret, strerror(errno));
296 result.error_code = -1;
297 return result;
298 }
299
300 /* some plugins may want to keep output unbroken, and some commands
301 * will yield no output, so return here for those */
302 if (flags & CMD_NO_ARRAYS || !result.output_container.buf || !result.output_container.buflen) {
303 return result;
304 }
305
306 /* and some may want both */
307 char *buf = NULL;
308 if (flags & CMD_NO_ASSOC) {
309 buf = malloc(result.output_container.buflen);
310 memcpy(buf, result.output_container.buf, result.output_container.buflen);
311 } else {
312 buf = result.output_container.buf;
313 }
314
315 result.output_container.line = NULL;
316 size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */
317 size_t rsf = 6;
318 size_t lineno = 0;
319 for (size_t i = 0; i < result.output_container.buflen;) {
320 /* make sure we have enough memory */
321 if (lineno >= ary_size) {
322 /* ary_size must never be zero */
323 do {
324 ary_size = result.output_container.buflen >> --rsf;
325 } while (!ary_size);
326
327 result.output_container.line =
328 realloc(result.output_container.line, ary_size * sizeof(char *));
329 }
330
331 /* set the pointer to the string */
332 result.output_container.line[lineno] = &buf[i];
333
334 /* hop to next newline or end of buffer */
335 while (buf[i] != '\n' && i < result.output_container.buflen) {
336 i++;
337 }
338 buf[i] = '\0';
339
340 lineno++;
341 i++;
342 }
343
344 result.output_container.lines = lineno;
345
346 return result;
347}
348
195static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) { 349static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) {
196 char tmpbuf[4096]; 350 char tmpbuf[4096];
197 cmd_output->buf = NULL; 351 cmd_output->buf = NULL;
@@ -225,7 +379,6 @@ static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags)
225 } 379 }
226 380
227 cmd_output->line = NULL; 381 cmd_output->line = NULL;
228 cmd_output->lens = NULL;
229 size_t i = 0; 382 size_t i = 0;
230 size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ 383 size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */
231 size_t rsf = 6; 384 size_t rsf = 6;
@@ -239,7 +392,6 @@ static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags)
239 } while (!ary_size); 392 } while (!ary_size);
240 393
241 cmd_output->line = realloc(cmd_output->line, ary_size * sizeof(char *)); 394 cmd_output->line = realloc(cmd_output->line, ary_size * sizeof(char *));
242 cmd_output->lens = realloc(cmd_output->lens, ary_size * sizeof(size_t));
243 } 395 }
244 396
245 /* set the pointer to the string */ 397 /* set the pointer to the string */
@@ -251,9 +403,6 @@ static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags)
251 } 403 }
252 buf[i] = '\0'; 404 buf[i] = '\0';
253 405
254 /* calculate the string length using pointer difference */
255 cmd_output->lens[lineno] = (size_t)&buf[i] - (size_t)cmd_output->line[lineno];
256
257 lineno++; 406 lineno++;
258 i++; 407 i++;
259 } 408 }
@@ -336,6 +485,139 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) {
336 return cmd_run_array(argv, out, err, flags); 485 return cmd_run_array(argv, out, err, flags);
337} 486}
338 487
488cmd_run_result cmd_run2(const char *cmd_string, int flags) {
489 cmd_run_result result = {
490 .cmd_error_code = 0,
491 .error_code = 0,
492 .stderr =
493 {
494 .buf = NULL,
495 .buflen = 0,
496 .line = NULL,
497 .lines = 0,
498 },
499 .stdout =
500 {
501 .buf = NULL,
502 .buflen = 0,
503 .line = NULL,
504 .lines = 0,
505 },
506 };
507
508 if (cmd_string == NULL) {
509 result.error_code = -1;
510 return result;
511 }
512
513 /* make copy of command string so strtok() doesn't silently modify it */
514 /* (the calling program may want to access it later) */
515 char *cmd = strdup(cmd_string);
516 if (cmd == NULL) {
517 result.error_code = -1;
518 return result;
519 }
520
521 /* This is not a shell, so we don't handle "???" */
522 if (strstr(cmd, "\"")) {
523 result.error_code = -1;
524 return result;
525 }
526
527 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
528 if (strstr(cmd, " ' ") || strstr(cmd, "'''")) {
529 result.error_code = -1;
530 return result;
531 }
532
533 /* each arg must be whitespace-separated, so args can be a maximum
534 * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */
535 size_t cmdlen = strlen(cmd_string);
536 size_t argc = (cmdlen >> 1) + 2;
537 char **argv = calloc(argc, sizeof(char *));
538
539 if (argv == NULL) {
540 printf("%s\n", _("Could not malloc argv array in popen()"));
541 result.error_code = -1;
542 return result;
543 }
544
545 /* get command arguments (stupidly, but fairly quickly) */
546 for (int i = 0; cmd; i++) {
547 char *str = cmd;
548 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
549
550 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
551 str++;
552 if (!strstr(str, "'")) {
553 result.error_code = -1;
554 return result; /* balanced? */
555 }
556
557 cmd = 1 + strstr(str, "'");
558 str[strcspn(str, "'")] = 0;
559 } else {
560 if (strpbrk(str, " \t\r\n")) {
561 cmd = 1 + strpbrk(str, " \t\r\n");
562 str[strcspn(str, " \t\r\n")] = 0;
563 } else {
564 cmd = NULL;
565 }
566 }
567
568 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
569 cmd = NULL;
570 }
571
572 argv[i++] = str;
573 }
574
575 result = cmd_run_array2(argv, flags);
576
577 return result;
578}
579
580cmd_run_result cmd_run_array2(char *const *cmd, int flags) {
581 cmd_run_result result = {
582 .cmd_error_code = 0,
583 .error_code = 0,
584 .stderr =
585 {
586 .buf = NULL,
587 .buflen = 0,
588 .line = NULL,
589 .lines = 0,
590 },
591 .stdout =
592 {
593 .buf = NULL,
594 .buflen = 0,
595 .line = NULL,
596 .lines = 0,
597 },
598 };
599
600 int_cmd_open_result cmd_open_result = _cmd_open2(cmd);
601 if (cmd_open_result.error_code != 0) {
602 // result.error_code = -1;
603 // return result;
604 // TODO properly handle this without dying
605 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd[0]);
606 }
607
608 int file_descriptor = cmd_open_result.file_descriptor;
609 int pfd_out[2] = {cmd_open_result.stdout_pipe_fd[0], cmd_open_result.stdout_pipe_fd[1]};
610 int pfd_err[2] = {cmd_open_result.stderr_pipe_fd[0], cmd_open_result.stderr_pipe_fd[1]};
611
612 int_cmd_fetch_output2 tmp_stdout = _cmd_fetch_output2(pfd_out[0], flags);
613 result.stdout = tmp_stdout.output_container;
614 int_cmd_fetch_output2 tmp_stderr = _cmd_fetch_output2(pfd_err[0], flags);
615 result.stderr = tmp_stderr.output_container;
616
617 result.cmd_error_code = _cmd_close(file_descriptor);
618 return result;
619}
620
339int cmd_run_array(char *const *argv, output *out, output *err, int flags) { 621int cmd_run_array(char *const *argv, output *out, output *err, int flags) {
340 /* initialize the structs */ 622 /* initialize the structs */
341 if (out) { 623 if (out) {
diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h
index 3672cdc9..d3a8f14f 100644
--- a/lib/utils_cmd.h
+++ b/lib/utils_cmd.h
@@ -13,7 +13,6 @@ typedef struct {
13 char *buf; /* output buffer */ 13 char *buf; /* output buffer */
14 size_t buflen; /* output buffer content length */ 14 size_t buflen; /* output buffer content length */
15 char **line; /* array of lines (points to buf) */ 15 char **line; /* array of lines (points to buf) */
16 size_t *lens; /* string lengths */
17 size_t lines; /* lines of output */ 16 size_t lines; /* lines of output */
18} output; 17} output;
19 18
@@ -22,6 +21,15 @@ int cmd_run(const char *, output *, output *, int);
22int cmd_run_array(char *const *, output *, output *, int); 21int cmd_run_array(char *const *, output *, output *, int);
23int cmd_file_read(const char *, output *, int); 22int cmd_file_read(const char *, output *, int);
24 23
24typedef struct {
25 int error_code;
26 int cmd_error_code;
27 output stdout;
28 output stderr;
29} cmd_run_result;
30cmd_run_result cmd_run2(const char *cmd, int flags);
31cmd_run_result cmd_run_array2(char * const *cmd, int flags);
32
25/* only multi-threaded plugins need to bother with this */ 33/* only multi-threaded plugins need to bother with this */
26void cmd_init(void); 34void cmd_init(void);
27#define CMD_INIT cmd_init() 35#define CMD_INIT cmd_init()
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");
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 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
4#include <stddef.h> 5#include <stddef.h>
5 6
6typedef struct { 7typedef struct {
@@ -23,10 +24,16 @@ typedef struct {
23 bool unknown_timeout; 24 bool unknown_timeout;
24 bool unknown_on_stderr; 25 bool unknown_on_stderr;
25 bool warn_on_stderr; 26 bool warn_on_stderr;
26 int skip_stdout; 27 bool skip_stdout;
27 int skip_stderr; 28 size_t stdout_lines_to_ignore;
29 bool skip_stderr;
30 size_t sterr_lines_to_ignore;
31
28 bool passive; 32 bool passive;
29 char *outputfile; 33 char *outputfile;
34
35 bool output_format_is_set;
36 mp_output_format output_format;
30} check_by_ssh_config; 37} check_by_ssh_config;
31 38
32check_by_ssh_config check_by_ssh_config_init() { 39check_by_ssh_config check_by_ssh_config_init() {
@@ -49,10 +56,16 @@ check_by_ssh_config check_by_ssh_config_init() {
49 .unknown_timeout = false, 56 .unknown_timeout = false,
50 .unknown_on_stderr = false, 57 .unknown_on_stderr = false,
51 .warn_on_stderr = false, 58 .warn_on_stderr = false,
52 .skip_stderr = 0, 59
53 .skip_stdout = 0, 60 .skip_stderr = false,
61 .stdout_lines_to_ignore = 0,
62 .skip_stdout = false,
63 .sterr_lines_to_ignore = 0,
64
54 .passive = false, 65 .passive = false,
55 .outputfile = NULL, 66 .outputfile = NULL,
67
68 .output_format_is_set = false,
56 }; 69 };
57 return tmp; 70 return tmp;
58} 71}
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c
index c3c2ba55..d49d8f07 100644
--- a/plugins/check_curl.d/check_curl_helpers.c
+++ b/plugins/check_curl.d/check_curl_helpers.c
@@ -4,6 +4,7 @@
4#include <netinet/in.h> 4#include <netinet/in.h>
5#include <netdb.h> 5#include <netdb.h>
6#include <stdlib.h> 6#include <stdlib.h>
7#include <string.h>
7#include "../utils.h" 8#include "../utils.h"
8#include "check_curl.d/config.h" 9#include "check_curl.d/config.h"
9#include "output.h" 10#include "output.h"
@@ -816,7 +817,10 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line)
816 buf = start; 817 buf = start;
817 } 818 }
818 819
819 char *first_line_end = strstr(buf, "\r\n"); 820 // Accept either LF or CRLF as end of line for the status line
821 // CRLF is the standard (RFC9112), but it is recommended to accept both
822 size_t length_of_first_line = strcspn(buf, "\r\n");
823 const char *first_line_end = &buf[length_of_first_line];
820 if (first_line_end == NULL) { 824 if (first_line_end == NULL) {
821 return -1; 825 return -1;
822 } 826 }
@@ -826,6 +830,7 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line)
826 if (status_line->first_line == NULL) { 830 if (status_line->first_line == NULL) {
827 return -1; 831 return -1;
828 } 832 }
833
829 memcpy(status_line->first_line, buf, first_line_len); 834 memcpy(status_line->first_line, buf, first_line_len);
830 status_line->first_line[first_line_len] = '\0'; 835 status_line->first_line[first_line_len] = '\0';
831 char *first_line_buf = strdup(status_line->first_line); 836 char *first_line_buf = strdup(status_line->first_line);
@@ -833,23 +838,34 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line)
833 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ 838 /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */
834 char *temp_string = strtok(first_line_buf, "/"); 839 char *temp_string = strtok(first_line_buf, "/");
835 if (temp_string == NULL) { 840 if (temp_string == NULL) {
841 if (verbose > 1) {
842 printf("%s: no / found\n", __func__);
843 }
836 free(first_line_buf); 844 free(first_line_buf);
837 return -1; 845 return -1;
838 } 846 }
847
839 if (strcmp(temp_string, "HTTP") != 0) { 848 if (strcmp(temp_string, "HTTP") != 0) {
849 if (verbose > 1) {
850 printf("%s: string 'HTTP' not found\n", __func__);
851 }
840 free(first_line_buf); 852 free(first_line_buf);
841 return -1; 853 return -1;
842 } 854 }
843 855
856 // try to find a space in the remaining string?
857 // the space after HTTP/1.1 probably
844 temp_string = strtok(NULL, " "); 858 temp_string = strtok(NULL, " ");
845 if (temp_string == NULL) { 859 if (temp_string == NULL) {
860 if (verbose > 1) {
861 printf("%s: no space after protocol definition\n", __func__);
862 }
846 free(first_line_buf); 863 free(first_line_buf);
847 return -1; 864 return -1;
848 } 865 }
849 866
850 char *temp_string_2; 867 char *temp_string_2;
851 if (strchr(temp_string, '.') != NULL) { 868 if (strchr(temp_string, '.') != NULL) {
852
853 /* HTTP 1.x case */ 869 /* HTTP 1.x case */
854 strtok(temp_string, "."); 870 strtok(temp_string, ".");
855 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); 871 status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10);
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c
index 468ded31..9bc68eb3 100644
--- a/plugins/check_dbi.c
+++ b/plugins/check_dbi.c
@@ -34,6 +34,10 @@ const char *copyright = "2011-2024";
34const char *email = "devel@monitoring-plugins.org"; 34const char *email = "devel@monitoring-plugins.org";
35 35
36#include "../lib/monitoringplug.h" 36#include "../lib/monitoringplug.h"
37#include "thresholds.h"
38#include "perfdata.h"
39#include "output.h"
40#include "states.h"
37#include "check_dbi.d/config.h" 41#include "check_dbi.d/config.h"
38#include "common.h" 42#include "common.h"
39#include "utils.h" 43#include "utils.h"
@@ -63,7 +67,6 @@ typedef struct {
63} check_dbi_config_wrapper; 67} check_dbi_config_wrapper;
64 68
65static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); 69static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
66static check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper /*config_wrapper*/);
67void print_usage(void); 70void print_usage(void);
68static void print_help(void); 71static void print_help(void);
69 72
@@ -71,26 +74,18 @@ static double timediff(struct timeval /*start*/, struct timeval /*end*/);
71 74
72static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...); 75static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...);
73 76
74static mp_state_enum do_query(dbi_conn /*conn*/, const char ** /*res_val_str*/, 77typedef struct {
75 double * /*res_val*/, double * /*res_time*/, mp_dbi_metric /*metric*/, 78 char *result_string;
76 mp_dbi_type /*type*/, char * /*np_dbi_query*/); 79 double result_number;
80 double query_duration;
81 int error_code;
82 const char *error_string;
83 mp_state_enum query_processing_status;
84} do_query_result;
85static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type,
86 char *query);
77 87
78int main(int argc, char **argv) { 88int main(int argc, char **argv) {
79 int status = STATE_UNKNOWN;
80
81 dbi_driver driver;
82 dbi_conn conn;
83
84 unsigned int server_version;
85
86 struct timeval start_timeval;
87 struct timeval end_timeval;
88 double conn_time = 0.0;
89 double query_time = 0.0;
90
91 const char *query_val_str = NULL;
92 double query_val = 0.0;
93
94 setlocale(LC_ALL, ""); 89 setlocale(LC_ALL, "");
95 bindtextdomain(PACKAGE, LOCALEDIR); 90 bindtextdomain(PACKAGE, LOCALEDIR);
96 textdomain(PACKAGE); 91 textdomain(PACKAGE);
@@ -106,6 +101,10 @@ int main(int argc, char **argv) {
106 101
107 const check_dbi_config config = tmp.config; 102 const check_dbi_config config = tmp.config;
108 103
104 if (config.output_format_is_set) {
105 mp_set_format(config.output_format);
106 }
107
109 /* Set signal handling and alarm */ 108 /* Set signal handling and alarm */
110 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 109 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
111 usage4(_("Cannot catch SIGALRM")); 110 usage4(_("Cannot catch SIGALRM"));
@@ -116,44 +115,46 @@ int main(int argc, char **argv) {
116 printf("Initializing DBI\n"); 115 printf("Initializing DBI\n");
117 } 116 }
118 117
119 dbi_inst *instance_p = {0}; 118 dbi_inst instance_p = NULL;
120 119 if (dbi_initialize_r(NULL, &instance_p) < 0) {
121 if (dbi_initialize_r(NULL, instance_p) < 0) { 120 printf("failed to initialize DBI; possibly you don't have any drivers installed.\n");
122 printf( 121 exit(STATE_UNKNOWN);
123 "UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n");
124 return STATE_UNKNOWN;
125 } 122 }
126 123
124 // Try to prevent libdbi from printing stuff on stderr
125 // Who thought that would be a good idea anyway?
126 dbi_set_verbosity_r(0, instance_p);
127
127 if (instance_p == NULL) { 128 if (instance_p == NULL) {
128 printf("UNKNOWN - failed to initialize DBI.\n"); 129 printf("failed to initialize DBI.\n");
129 return STATE_UNKNOWN; 130 exit(STATE_UNKNOWN);
130 } 131 }
131 132
132 if (verbose) { 133 if (verbose) {
133 printf("Opening DBI driver '%s'\n", config.dbi_driver); 134 printf("Opening DBI driver '%s'\n", config.dbi_driver);
134 } 135 }
135 136
136 driver = dbi_driver_open_r(config.dbi_driver, instance_p); 137 dbi_driver driver = dbi_driver_open_r(config.dbi_driver, instance_p);
137 if (!driver) { 138 if (!driver) {
138 printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", 139 printf("failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver);
139 config.dbi_driver);
140 140
141 printf("Known drivers:\n"); 141 printf("Known drivers:\n");
142 for (driver = dbi_driver_list_r(NULL, instance_p); driver; 142 for (driver = dbi_driver_list_r(NULL, instance_p); driver;
143 driver = dbi_driver_list_r(driver, instance_p)) { 143 driver = dbi_driver_list_r(driver, instance_p)) {
144 printf(" - %s\n", dbi_driver_get_name(driver)); 144 printf(" - %s\n", dbi_driver_get_name(driver));
145 } 145 }
146 return STATE_UNKNOWN; 146 exit(STATE_UNKNOWN);
147 } 147 }
148 148
149 /* make a connection to the database */ 149 /* make a connection to the database */
150 struct timeval start_timeval;
150 gettimeofday(&start_timeval, NULL); 151 gettimeofday(&start_timeval, NULL);
151 152
152 conn = dbi_conn_open(driver); 153 dbi_conn conn = dbi_conn_open(driver);
153 if (!conn) { 154 if (!conn) {
154 printf("UNKNOWN - failed top open connection object.\n"); 155 printf("UNKNOWN - failed top open connection object.\n");
155 dbi_conn_close(conn); 156 dbi_conn_close(conn);
156 return STATE_UNKNOWN; 157 exit(STATE_UNKNOWN);
157 } 158 }
158 159
159 for (size_t i = 0; i < config.dbi_options_num; ++i) { 160 for (size_t i = 0; i < config.dbi_options_num; ++i) {
@@ -167,10 +168,10 @@ int main(int argc, char **argv) {
167 if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) { 168 if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) {
168 continue; 169 continue;
169 } 170 }
170 /* else: status != 0 */
171 171
172 np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'", 172 // Failing to set option
173 config.dbi_options[i].key, config.dbi_options[i].value); 173 np_dbi_print_error(conn, "failed to set option '%s' to '%s'", config.dbi_options[i].key,
174 config.dbi_options[i].value);
174 printf("Known driver options:\n"); 175 printf("Known driver options:\n");
175 176
176 for (opt = dbi_conn_get_option_list(conn, NULL); opt; 177 for (opt = dbi_conn_get_option_list(conn, NULL); opt;
@@ -178,7 +179,7 @@ int main(int argc, char **argv) {
178 printf(" - %s\n", opt); 179 printf(" - %s\n", opt);
179 } 180 }
180 dbi_conn_close(conn); 181 dbi_conn_close(conn);
181 return STATE_UNKNOWN; 182 exit(STATE_UNKNOWN);
182 } 183 }
183 184
184 if (config.host) { 185 if (config.host) {
@@ -206,80 +207,216 @@ int main(int argc, char **argv) {
206 } 207 }
207 208
208 if (dbi_conn_connect(conn) < 0) { 209 if (dbi_conn_connect(conn) < 0) {
209 np_dbi_print_error(conn, "UNKNOWN - failed to connect to database"); 210 np_dbi_print_error(conn, "failed to connect to database");
210 return STATE_UNKNOWN; 211 exit(STATE_UNKNOWN);
211 } 212 }
212 213
214 struct timeval end_timeval;
213 gettimeofday(&end_timeval, NULL); 215 gettimeofday(&end_timeval, NULL);
214 conn_time = timediff(start_timeval, end_timeval); 216 double conn_time = timediff(start_timeval, end_timeval);
215
216 server_version = dbi_conn_get_engine_version(conn);
217 if (verbose) { 217 if (verbose) {
218 printf("Connected to server version %u\n", server_version); 218 printf("Time elapsed: %f\n", conn_time);
219 } 219 }
220 220
221 if (config.metric == METRIC_SERVER_VERSION) { 221 mp_check overall = mp_check_init();
222 status = get_status(server_version, config.dbi_thresholds); 222
223 mp_subcheck sc_connection_time = mp_subcheck_init();
224 sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_OK);
225 xasprintf(&sc_connection_time.output, "Connection time: %f", conn_time);
226
227 mp_perfdata pd_conn_duration = perfdata_init();
228 pd_conn_duration.label = "conntime";
229 pd_conn_duration = mp_set_pd_value(pd_conn_duration, conn_time);
230
231 if (config.metric == METRIC_CONN_TIME) {
232 pd_conn_duration = mp_pd_set_thresholds(pd_conn_duration, config.thresholds);
233 mp_state_enum status = mp_get_pd_status(pd_conn_duration);
234 sc_connection_time = mp_set_subcheck_state(sc_connection_time, status);
235 if (status != STATE_OK) {
236 xasprintf(&sc_connection_time.output, "%s violates thresholds",
237 sc_connection_time.output);
238 }
223 } 239 }
224 240
241 mp_add_perfdata_to_subcheck(&sc_connection_time, pd_conn_duration);
242 mp_add_subcheck_to_check(&overall, sc_connection_time);
243
244 unsigned int server_version = dbi_conn_get_engine_version(conn);
225 if (verbose) { 245 if (verbose) {
226 printf("Time elapsed: %f\n", conn_time); 246 printf("Connected to server version %u\n", server_version);
227 } 247 }
228 248
229 if (config.metric == METRIC_CONN_TIME) { 249 mp_subcheck sc_server_version = mp_subcheck_init();
230 status = get_status(conn_time, config.dbi_thresholds); 250 sc_server_version = mp_set_subcheck_default_state(sc_server_version, STATE_OK);
231 } 251 xasprintf(&sc_server_version.output, "Connected to server version %u", server_version);
252
253 if (config.metric == METRIC_SERVER_VERSION) {
254 mp_perfdata pd_server_version = perfdata_init();
255 pd_server_version = mp_set_pd_value(pd_server_version, server_version);
256 pd_server_version = mp_pd_set_thresholds(pd_server_version, config.thresholds);
257 mp_state_enum status = mp_get_pd_status(pd_server_version);
258 mp_add_perfdata_to_subcheck(&sc_server_version, pd_server_version);
259
260 sc_server_version = mp_set_subcheck_state(sc_server_version, status);
261
262 if (status != STATE_OK) {
263 xasprintf(&sc_server_version.output, "%s violates thresholds",
264 sc_server_version.output);
265 }
266 };
267 mp_add_subcheck_to_check(&overall, sc_server_version);
232 268
233 /* select a database */ 269 /* select a database */
234 if (config.dbi_database) { 270 if (config.database) {
235 if (verbose > 1) { 271 if (verbose > 1) {
236 printf("Selecting database '%s'\n", config.dbi_database); 272 printf("Selecting database '%s'\n", config.database);
237 } 273 }
238 274
239 if (dbi_conn_select_db(conn, config.dbi_database)) { 275 mp_subcheck sc_select_db = mp_subcheck_init();
240 np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", 276 sc_select_db = mp_set_subcheck_default_state(sc_select_db, STATE_OK);
241 config.dbi_database); 277
242 return STATE_UNKNOWN; 278 if (dbi_conn_select_db(conn, config.database)) {
279 np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", config.database);
280 exit(STATE_UNKNOWN);
281 } else {
282 mp_add_subcheck_to_check(&overall, sc_select_db);
243 } 283 }
244 } 284 }
245 285
246 if (config.dbi_query) { 286 // Do a query (if configured)
287 if (config.query) {
288 mp_subcheck sc_query = mp_subcheck_init();
289 sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN);
290
247 /* execute query */ 291 /* execute query */
248 status = do_query(conn, &query_val_str, &query_val, &query_time, config.metric, config.type, 292 do_query_result query_res = do_query(conn, config.metric, config.type, config.query);
249 config.dbi_query); 293
250 if (status != STATE_OK) { 294 if (query_res.error_code != 0) {
251 /* do_query prints an error message in this case */ 295 xasprintf(&sc_query.output, "Query failed: %s", query_res.error_string);
252 return status; 296 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
253 } 297 } else if (query_res.query_processing_status != STATE_OK) {
298 if (query_res.error_string) {
299 xasprintf(&sc_query.output, "Failed to process query: %s", query_res.error_string);
300 } else {
301 xasprintf(&sc_query.output, "Failed to process query");
302 }
303 sc_query = mp_set_subcheck_state(sc_query, query_res.query_processing_status);
304 } else {
305 // query succeeded in general
306 xasprintf(&sc_query.output, "Query '%s' succeeded", config.query);
307
308 // that's a OK by default now
309 sc_query = mp_set_subcheck_default_state(sc_query, STATE_OK);
310
311 // query duration first
312 mp_perfdata pd_query_duration = perfdata_init();
313 pd_query_duration = mp_set_pd_value(pd_query_duration, query_res.query_duration);
314 pd_query_duration.label = "querytime";
315 if (config.metric == METRIC_QUERY_TIME) {
316 pd_query_duration = mp_pd_set_thresholds(pd_query_duration, config.thresholds);
317 }
318
319 mp_add_perfdata_to_subcheck(&sc_query, pd_query_duration);
254 320
255 if (config.metric == METRIC_QUERY_RESULT) { 321 if (config.metric == METRIC_QUERY_RESULT) {
256 if (config.expect) { 322 if (config.expect) {
257 if ((!query_val_str) || strcmp(query_val_str, config.expect)) { 323 if ((!query_res.result_string) ||
258 status = STATE_CRITICAL; 324 strcmp(query_res.result_string, config.expect)) {
325 xasprintf(&sc_query.output, "Found string '%s' in query result",
326 config.expect);
327 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
328 } else {
329 xasprintf(&sc_query.output, "Did not find string '%s' in query result",
330 config.expect);
331 sc_query = mp_set_subcheck_state(sc_query, STATE_OK);
332 }
333 } else if (config.expect_re_str) {
334 int comp_err;
335 regex_t expect_re = {};
336 comp_err = regcomp(&expect_re, config.expect_re_str, config.expect_re_cflags);
337 if (comp_err != 0) {
338 // TODO error, failed to compile regex
339 // TODO move this to config sanitatisation
340 printf("Failed to compile regex from string '%s'", config.expect_re_str);
341 exit(STATE_UNKNOWN);
342 }
343
344 int err =
345 regexec(&expect_re, query_res.result_string, 0, NULL, /* flags = */ 0);
346 if (!err) {
347 sc_query = mp_set_subcheck_state(sc_query, STATE_OK);
348 xasprintf(&sc_query.output, "Found regular expression '%s' in query result",
349 config.expect_re_str);
350 } else if (err == REG_NOMATCH) {
351 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
352 xasprintf(&sc_query.output,
353 "Did not find regular expression '%s' in query result",
354 config.expect_re_str);
355 } else {
356 char errmsg[1024];
357 regerror(err, &expect_re, errmsg, sizeof(errmsg));
358 xasprintf(&sc_query.output,
359 "ERROR - failed to execute regular expression: %s\n", errmsg);
360 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
361 }
259 } else { 362 } else {
260 status = STATE_OK; 363 // no string matching
364 if (isnan(query_res.result_number)) {
365 // The query result is not a number, but no string checking was configured
366 // so we expected a number
367 // this is a CRITICAL
368 xasprintf(&sc_query.output, "Query '%s' result is not numeric",
369 config.query);
370 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
371
372 } else {
373
374 mp_perfdata pd_query_val = perfdata_init();
375 pd_query_val = mp_set_pd_value(pd_query_val, query_res.result_number);
376 pd_query_val.label = "query";
377 pd_query_val = mp_pd_set_thresholds(pd_query_val, config.thresholds);
378
379 mp_add_perfdata_to_subcheck(&sc_query, pd_query_val);
380 mp_state_enum query_numerical_result = mp_get_pd_status(pd_query_val);
381
382 sc_query = mp_set_subcheck_state(sc_query, query_numerical_result);
383 // TODO set pd thresholds
384 // if (config.dbi_thresholds->warning) {
385 // pd_query_val.warn= config.dbi_thresholds->warning
386 // } else {
387 // }
388
389 if (query_numerical_result == STATE_OK) {
390 xasprintf(&sc_query.output,
391 "Query result '%f' is within given thresholds",
392 query_res.result_number);
393 } else {
394 xasprintf(&sc_query.output,
395 "Query result '%f' violates the given thresholds",
396 query_res.result_number);
397 }
398 }
261 } 399 }
262 } else if (config.expect_re_str) { 400 } else if (config.metric == METRIC_QUERY_TIME) {
263 int err; 401 mp_state_enum query_time_status = mp_get_pd_status(pd_query_duration);
264 402 mp_set_subcheck_state(sc_query, query_time_status);
265 regex_t expect_re = {}; 403
266 err = regexec(&expect_re, query_val_str, 0, NULL, /* flags = */ 0); 404 if (query_time_status == STATE_OK) {
267 if (!err) { 405 xasprintf(&sc_query.output, "Query duration '%f' is within given thresholds",
268 status = STATE_OK; 406 query_res.query_duration);
269 } else if (err == REG_NOMATCH) {
270 status = STATE_CRITICAL;
271 } else { 407 } else {
272 char errmsg[1024]; 408 xasprintf(&sc_query.output, "Query duration '%f' violates the given thresholds",
273 regerror(err, &expect_re, errmsg, sizeof(errmsg)); 409 query_res.query_duration);
274 printf("ERROR - failed to execute regular expression: %s\n", errmsg);
275 status = STATE_CRITICAL;
276 } 410 }
277 } else { 411 } else {
278 status = get_status(query_val, config.dbi_thresholds); 412 /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error
413 * which should have been reported and handled (abort) before
414 * ... unless we expected a string to be returned */
415 assert((!isnan(query_res.result_number)) || (config.type == TYPE_STRING));
279 } 416 }
280 } else if (config.metric == METRIC_QUERY_TIME) {
281 status = get_status(query_time, config.dbi_thresholds);
282 } 417 }
418
419 mp_add_subcheck_to_check(&overall, sc_query);
283 } 420 }
284 421
285 if (verbose) { 422 if (verbose) {
@@ -287,71 +424,17 @@ int main(int argc, char **argv) {
287 } 424 }
288 dbi_conn_close(conn); 425 dbi_conn_close(conn);
289 426
290 /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error 427 mp_exit(overall);
291 * which should have been reported and handled (abort) before
292 * ... unless we expected a string to be returned */
293 assert((config.metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) ||
294 (config.type == TYPE_STRING));
295
296 assert((config.type != TYPE_STRING) || (config.expect || config.expect_re_str));
297
298 printf("%s - connection time: %fs", state_text(status), conn_time);
299 if (config.dbi_query) {
300 if (config.type == TYPE_STRING) {
301 assert(config.expect || config.expect_re_str);
302 printf(", '%s' returned '%s' in %fs", config.dbi_query,
303 query_val_str ? query_val_str : "<nothing>", query_time);
304 if (status != STATE_OK) {
305 if (config.expect) {
306 printf(" (expected '%s')", config.expect);
307 } else if (config.expect_re_str) {
308 printf(" (expected regex /%s/%s)", config.expect_re_str,
309 ((config.expect_re_cflags & REG_ICASE) ? "i" : ""));
310 }
311 }
312 } else if (isnan(query_val)) {
313 printf(", '%s' query execution time: %fs", config.dbi_query, query_time);
314 } else {
315 printf(", '%s' returned %f in %fs", config.dbi_query, query_val, query_time);
316 }
317 }
318
319 printf(
320 " | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time,
321 ((config.metric == METRIC_CONN_TIME) && config.warning_range) ? config.warning_range : "",
322 ((config.metric == METRIC_CONN_TIME) && config.critical_range) ? config.critical_range : "",
323 server_version,
324 ((config.metric == METRIC_SERVER_VERSION) && config.warning_range) ? config.warning_range
325 : "",
326 ((config.metric == METRIC_SERVER_VERSION) && config.critical_range) ? config.critical_range
327 : "");
328 if (config.dbi_query) {
329 if (!isnan(query_val)) { /* this is also true when -e is used */
330 printf(" query=%f;%s;%s;;", query_val,
331 ((config.metric == METRIC_QUERY_RESULT) && config.warning_range)
332 ? config.warning_range
333 : "",
334 ((config.metric == METRIC_QUERY_RESULT) && config.critical_range)
335 ? config.critical_range
336 : "");
337 }
338 printf(" querytime=%fs;%s;%s;0;", query_time,
339 ((config.metric == METRIC_QUERY_TIME) && config.warning_range) ? config.warning_range
340 : "",
341 ((config.metric == METRIC_QUERY_TIME) && config.critical_range)
342 ? config.critical_range
343 : "");
344 }
345 printf("\n");
346 return status;
347} 428}
348 429
349/* process command-line arguments */ 430/* process command-line arguments */
350check_dbi_config_wrapper process_arguments(int argc, char **argv) { 431check_dbi_config_wrapper process_arguments(int argc, char **argv) {
432 enum {
433 output_format_index = CHAR_MAX + 1,
434 };
351 435
352 int option = 0; 436 int option = 0;
353 static struct option longopts[] = {STD_LONG_OPTS, 437 static struct option longopts[] = {STD_LONG_OPTS,
354
355 {"expect", required_argument, 0, 'e'}, 438 {"expect", required_argument, 0, 'e'},
356 {"regex", required_argument, 0, 'r'}, 439 {"regex", required_argument, 0, 'r'},
357 {"regexi", required_argument, 0, 'R'}, 440 {"regexi", required_argument, 0, 'R'},
@@ -360,6 +443,7 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
360 {"option", required_argument, 0, 'o'}, 443 {"option", required_argument, 0, 'o'},
361 {"query", required_argument, 0, 'q'}, 444 {"query", required_argument, 0, 'q'},
362 {"database", required_argument, 0, 'D'}, 445 {"database", required_argument, 0, 'D'},
446 {"output-format", required_argument, 0, output_format_index},
363 {0, 0, 0, 0}}; 447 {0, 0, 0, 0}};
364 448
365 check_dbi_config_wrapper result = { 449 check_dbi_config_wrapper result = {
@@ -384,14 +468,22 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
384 print_revision(progname, NP_VERSION); 468 print_revision(progname, NP_VERSION);
385 exit(STATE_UNKNOWN); 469 exit(STATE_UNKNOWN);
386 470
387 case 'c': /* critical range */ 471 case 'c': /* critical range */ {
388 result.config.critical_range = optarg; 472 mp_range_parsed tmp = mp_parse_range_string(optarg);
473 if (tmp.error != MP_PARSING_SUCCES) {
474 die(STATE_UNKNOWN, "failed to parse critical threshold");
475 }
476 result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range);
389 result.config.type = TYPE_NUMERIC; 477 result.config.type = TYPE_NUMERIC;
390 break; 478 } break;
391 case 'w': /* warning range */ 479 case 'w': /* warning range */ {
392 result.config.warning_range = optarg; 480 mp_range_parsed tmp = mp_parse_range_string(optarg);
481 if (tmp.error != MP_PARSING_SUCCES) {
482 die(STATE_UNKNOWN, "failed to parse warning threshold");
483 }
484 result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range);
393 result.config.type = TYPE_NUMERIC; 485 result.config.type = TYPE_NUMERIC;
394 break; 486 } break;
395 case 'e': 487 case 'e':
396 result.config.expect = optarg; 488 result.config.expect = optarg;
397 result.config.type = TYPE_STRING; 489 result.config.type = TYPE_STRING;
@@ -418,7 +510,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
418 } 510 }
419 break; 511 break;
420 } 512 }
421
422 case 'm': 513 case 'm':
423 if (!strcasecmp(optarg, "CONN_TIME")) { 514 if (!strcasecmp(optarg, "CONN_TIME")) {
424 result.config.metric = METRIC_CONN_TIME; 515 result.config.metric = METRIC_CONN_TIME;
@@ -438,7 +529,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
438 } else { 529 } else {
439 timeout_interval = atoi(optarg); 530 timeout_interval = atoi(optarg);
440 } 531 }
441
442 break; 532 break;
443 case 'H': /* host */ 533 case 'H': /* host */
444 if (!is_host(optarg)) { 534 if (!is_host(optarg)) {
@@ -450,7 +540,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
450 case 'v': 540 case 'v':
451 verbose++; 541 verbose++;
452 break; 542 break;
453
454 case 'd': 543 case 'd':
455 result.config.dbi_driver = optarg; 544 result.config.dbi_driver = optarg;
456 break; 545 break;
@@ -482,61 +571,68 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) {
482 new->value = value; 571 new->value = value;
483 } break; 572 } break;
484 case 'q': 573 case 'q':
485 result.config.dbi_query = optarg; 574 result.config.query = optarg;
486 break; 575 break;
487 case 'D': 576 case 'D':
488 result.config.dbi_database = optarg; 577 result.config.database = optarg;
578 break;
579 case output_format_index: {
580 parsed_output_format parser = mp_parse_output_format(optarg);
581 if (!parser.parsing_success) {
582 // TODO List all available formats here, maybe add anothoer usage function
583 printf("Invalid output format: %s\n", optarg);
584 exit(STATE_UNKNOWN);
585 }
586
587 result.config.output_format_is_set = true;
588 result.config.output_format = parser.output_format;
489 break; 589 break;
490 } 590 }
591 }
491 } 592 }
492 593
493 set_thresholds(&result.config.dbi_thresholds, result.config.warning_range, 594 if (!result.config.dbi_driver) {
494 result.config.critical_range);
495
496 return validate_arguments(result);
497}
498
499check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrapper) {
500 if (!config_wrapper.config.dbi_driver) {
501 usage("Must specify a DBI driver"); 595 usage("Must specify a DBI driver");
502 } 596 }
503 597
504 if (((config_wrapper.config.metric == METRIC_QUERY_RESULT) || 598 if (((result.config.metric == METRIC_QUERY_RESULT) ||
505 (config_wrapper.config.metric == METRIC_QUERY_TIME)) && 599 (result.config.metric == METRIC_QUERY_TIME)) &&
506 (!config_wrapper.config.dbi_query)) { 600 (!result.config.query)) {
507 usage("Must specify a query to execute (metric == QUERY_RESULT)"); 601 usage("Must specify a query to execute (metric == QUERY_RESULT)");
508 } 602 }
509 603
510 if ((config_wrapper.config.metric != METRIC_CONN_TIME) && 604 if ((result.config.metric != METRIC_CONN_TIME) &&
511 (config_wrapper.config.metric != METRIC_SERVER_VERSION) && 605 (result.config.metric != METRIC_SERVER_VERSION) &&
512 (config_wrapper.config.metric != METRIC_QUERY_RESULT) && 606 (result.config.metric != METRIC_QUERY_RESULT) &&
513 (config_wrapper.config.metric != METRIC_QUERY_TIME)) { 607 (result.config.metric != METRIC_QUERY_TIME)) {
514 usage("Invalid metric specified"); 608 usage("Invalid metric specified");
515 } 609 }
516 610
517 if (config_wrapper.config.expect && 611 if (result.config.expect &&
518 (config_wrapper.config.warning_range || config_wrapper.config.critical_range || 612 (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set ||
519 config_wrapper.config.expect_re_str)) { 613 result.config.expect_re_str)) {
520 usage("Do not mix -e and -w/-c/-r/-R"); 614 usage("Do not mix -e and -w/-c/-r/-R");
521 } 615 }
522 616
523 if (config_wrapper.config.expect_re_str && 617 if (result.config.expect_re_str &&
524 (config_wrapper.config.warning_range || config_wrapper.config.critical_range || 618 (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set ||
525 config_wrapper.config.expect)) { 619 result.config.expect)) {
526 usage("Do not mix -r/-R and -w/-c/-e"); 620 usage("Do not mix -r/-R and -w/-c/-e");
527 } 621 }
528 622
529 if (config_wrapper.config.expect && (config_wrapper.config.metric != METRIC_QUERY_RESULT)) { 623 if (result.config.expect && (result.config.metric != METRIC_QUERY_RESULT)) {
530 usage("Option -e requires metric QUERY_RESULT"); 624 usage("Option -e requires metric QUERY_RESULT");
531 } 625 }
532 626
533 if (config_wrapper.config.expect_re_str && 627 if (result.config.expect_re_str && (result.config.metric != METRIC_QUERY_RESULT)) {
534 (config_wrapper.config.metric != METRIC_QUERY_RESULT)) {
535 usage("Options -r/-R require metric QUERY_RESULT"); 628 usage("Options -r/-R require metric QUERY_RESULT");
536 } 629 }
537 630
538 config_wrapper.errorcode = OK; 631 if (result.config.type == TYPE_STRING) {
539 return config_wrapper; 632 assert(result.config.expect || result.config.expect_re_str);
633 }
634
635 return result;
540} 636}
541 637
542void print_help(void) { 638void print_help(void) {
@@ -642,21 +738,12 @@ void print_usage(void) {
642 printf(" [-e <string>] [-r|-R <regex>]\n"); 738 printf(" [-e <string>] [-r|-R <regex>]\n");
643} 739}
644 740
645const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type, 741const char *get_field_str(dbi_result res, check_dbi_metric metric, check_dbi_type type) {
646 mp_dbi_metric metric, mp_dbi_type type) { 742 const char *str = dbi_result_get_string_idx(res, 1);
647 const char *str;
648
649 if (field_type != DBI_TYPE_STRING) {
650 printf("CRITICAL - result value is not a string\n");
651 return NULL;
652 }
653
654 str = dbi_result_get_string_idx(res, 1);
655 if ((!str) || (strcmp(str, "ERROR") == 0)) { 743 if ((!str) || (strcmp(str, "ERROR") == 0)) {
656 if (metric != METRIC_QUERY_RESULT) { 744 if (metric != METRIC_QUERY_RESULT) {
657 return NULL; 745 return NULL;
658 } 746 }
659 np_dbi_print_error(conn, "CRITICAL - failed to fetch string value");
660 return NULL; 747 return NULL;
661 } 748 }
662 749
@@ -666,36 +753,50 @@ const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_ty
666 return str; 753 return str;
667} 754}
668 755
669double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_dbi_metric metric, 756typedef struct {
670 mp_dbi_type type) { 757 double value;
671 double val = NAN; 758 int error_code;
759 int dbi_error_code; // not sure if useful
760} get_field_wrapper;
761get_field_wrapper get_field(dbi_result res, check_dbi_metric metric, check_dbi_type type) {
762
763 unsigned short field_type = dbi_result_get_field_type_idx(res, 1);
764 get_field_wrapper result = {
765 .value = NAN,
766 .error_code = OK,
767 };
672 768
673 if (*field_type == DBI_TYPE_INTEGER) { 769 if (field_type == DBI_TYPE_INTEGER) {
674 val = (double)dbi_result_get_longlong_idx(res, 1); 770 result.value = (double)dbi_result_get_longlong_idx(res, 1);
675 } else if (*field_type == DBI_TYPE_DECIMAL) { 771 } else if (field_type == DBI_TYPE_DECIMAL) {
676 val = dbi_result_get_double_idx(res, 1); 772 result.value = dbi_result_get_double_idx(res, 1);
677 } else if (*field_type == DBI_TYPE_STRING) { 773 } else if (field_type == DBI_TYPE_STRING) {
678 const char *val_str; 774 const char *val_str;
679 char *endptr = NULL; 775 char *endptr = NULL;
680 776
681 val_str = get_field_str(conn, res, *field_type, metric, type); 777 val_str = get_field_str(res, metric, type);
682 if (!val_str) { 778 if (!val_str) {
683 if (metric != METRIC_QUERY_RESULT) { 779 result.error_code = ERROR;
684 return NAN; 780 field_type = DBI_TYPE_ERROR;
685 } 781 return result;
686 *field_type = DBI_TYPE_ERROR;
687 return NAN;
688 } 782 }
689 783
690 val = strtod(val_str, &endptr); 784 result.value = strtod(val_str, &endptr);
691 if (endptr == val_str) { 785 if (endptr == val_str) {
692 if (metric != METRIC_QUERY_RESULT) { 786 if (metric != METRIC_QUERY_RESULT) {
693 return NAN; 787 result.error_code = ERROR;
788 return result;
694 } 789 }
695 printf("CRITICAL - result value is not a numeric: %s\n", val_str); 790
696 *field_type = DBI_TYPE_ERROR; 791 if (verbose) {
697 return NAN; 792 printf("CRITICAL - result value is not a numeric: %s\n", val_str);
793 }
794
795 field_type = DBI_TYPE_ERROR;
796 result.error_code = ERROR;
797 return result;
698 } 798 }
799
699 if ((endptr != NULL) && (*endptr != '\0')) { 800 if ((endptr != NULL) && (*endptr != '\0')) {
700 if (verbose) { 801 if (verbose) {
701 printf("Garbage after value: %s\n", endptr); 802 printf("Garbage after value: %s\n", endptr);
@@ -703,123 +804,127 @@ double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_d
703 } 804 }
704 } else { 805 } else {
705 if (metric != METRIC_QUERY_RESULT) { 806 if (metric != METRIC_QUERY_RESULT) {
706 return NAN; 807 result.error_code = ERROR;
808 return result;
707 } 809 }
708 printf("CRITICAL - cannot parse value of type %s (%i)\n", 810 // printf("CRITICAL - cannot parse value of type %s (%i)\n",
709 (*field_type == DBI_TYPE_BINARY) ? "BINARY" 811 // (*field_type == DBI_TYPE_BINARY) ? "BINARY"
710 : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME" 812 // : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME"
711 : "<unknown>", 813 // : "<unknown>",
712 *field_type); 814 // *field_type);
713 *field_type = DBI_TYPE_ERROR; 815 field_type = DBI_TYPE_ERROR;
714 return NAN; 816 result.error_code = ERROR;
715 } 817 }
716 return val; 818 return result;
717} 819}
718 820
719mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str, 821static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type,
720 double *res_val, mp_dbi_metric metric, mp_dbi_type type) { 822 char *query) {
721 unsigned short field_type; 823 assert(query);
722 double val = NAN;
723
724 if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
725 if (metric != METRIC_QUERY_RESULT) {
726 return STATE_OK;
727 }
728 np_dbi_print_error(conn, "CRITICAL - failed to fetch rows");
729 return STATE_CRITICAL;
730 }
731 824
732 if (dbi_result_get_numrows(res) < 1) { 825 if (verbose) {
733 if (metric != METRIC_QUERY_RESULT) { 826 printf("Executing query '%s'\n", query);
734 return STATE_OK;
735 }
736 printf("WARNING - no rows returned\n");
737 return STATE_WARNING;
738 }
739
740 if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) {
741 if (metric != METRIC_QUERY_RESULT) {
742 return STATE_OK;
743 }
744 np_dbi_print_error(conn, "CRITICAL - failed to fetch fields");
745 return STATE_CRITICAL;
746 }
747
748 if (dbi_result_get_numfields(res) < 1) {
749 if (metric != METRIC_QUERY_RESULT) {
750 return STATE_OK;
751 }
752 printf("WARNING - no fields returned\n");
753 return STATE_WARNING;
754 }
755
756 if (dbi_result_first_row(res) != 1) {
757 if (metric != METRIC_QUERY_RESULT) {
758 return STATE_OK;
759 }
760 np_dbi_print_error(conn, "CRITICAL - failed to fetch first row");
761 return STATE_CRITICAL;
762 } 827 }
763 828
764 field_type = dbi_result_get_field_type_idx(res, 1); 829 do_query_result result = {
765 if (field_type != DBI_TYPE_ERROR) { 830 .query_duration = 0,
766 if (type == TYPE_STRING) { 831 .result_string = NULL,
767 /* the value will be freed in dbi_result_free */ 832 .result_number = 0,
768 *res_val_str = strdup(get_field_str(conn, res, field_type, metric, type)); 833 .error_code = 0,
769 } else { 834 .query_processing_status = STATE_UNKNOWN,
770 val = get_field(conn, res, &field_type, metric, type); 835 };
771 }
772 }
773 836
774 *res_val = val; 837 struct timeval timeval_start;
838 gettimeofday(&timeval_start, NULL);
775 839
776 if (field_type == DBI_TYPE_ERROR) { 840 dbi_result res = dbi_conn_query(conn, query);
777 if (metric != METRIC_QUERY_RESULT) { 841 if (!res) {
778 return STATE_OK; 842 dbi_conn_error(conn, &result.error_string);
779 } 843 result.error_code = 1;
780 np_dbi_print_error(conn, "CRITICAL - failed to fetch data"); 844 return result;
781 return STATE_CRITICAL;
782 } 845 }
783 846
784 dbi_result_free(res);
785 return STATE_OK;
786}
787
788mp_state_enum do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time,
789 mp_dbi_metric metric, mp_dbi_type type, char *np_dbi_query) {
790 dbi_result res;
791
792 struct timeval timeval_start;
793 struct timeval timeval_end; 847 struct timeval timeval_end;
794 mp_state_enum status = STATE_OK; 848 gettimeofday(&timeval_end, NULL);
795 849 result.query_duration = timediff(timeval_start, timeval_end);
796 assert(np_dbi_query);
797 850
798 if (verbose) { 851 if (verbose) {
799 printf("Executing query '%s'\n", np_dbi_query); 852 printf("Query duration: %f\n", result.query_duration);
800 } 853 }
801 854
802 gettimeofday(&timeval_start, NULL); 855 // Default state is OK, all error will be set explicitly
856 mp_state_enum query_processing_state = STATE_OK;
857 {
803 858
804 res = dbi_conn_query(conn, np_dbi_query); 859 if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
805 if (!res) { 860 if (metric != METRIC_QUERY_RESULT) {
806 np_dbi_print_error(conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); 861 query_processing_state = STATE_OK;
807 return STATE_CRITICAL; 862 } else {
863 dbi_conn_error(conn, &result.error_string);
864 query_processing_state = STATE_CRITICAL;
865 }
866 } else if (dbi_result_get_numrows(res) < 1) {
867 if (metric != METRIC_QUERY_RESULT) {
868 query_processing_state = STATE_OK;
869 } else {
870 result.error_string = "no rows returned";
871 // printf("WARNING - no rows returned\n");
872 query_processing_state = STATE_WARNING;
873 }
874 } else if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) {
875 if (metric != METRIC_QUERY_RESULT) {
876 query_processing_state = STATE_OK;
877 } else {
878 dbi_conn_error(conn, &result.error_string);
879 // np_dbi_print_error(conn, "CRITICAL - failed to fetch fields");
880 query_processing_state = STATE_CRITICAL;
881 }
882 } else if (dbi_result_get_numfields(res) < 1) {
883 if (metric != METRIC_QUERY_RESULT) {
884 query_processing_state = STATE_OK;
885 } else {
886 result.error_string = "no fields returned";
887 // printf("WARNING - no fields returned\n");
888 query_processing_state = STATE_WARNING;
889 }
890 } else if (dbi_result_first_row(res) != 1) {
891 if (metric != METRIC_QUERY_RESULT) {
892 query_processing_state = STATE_OK;
893 } else {
894 dbi_conn_error(conn, &result.error_string);
895 // np_dbi_print_error(conn, "CRITICAL - failed to fetch first row");
896 query_processing_state = STATE_CRITICAL;
897 }
898 } else {
899 unsigned short field_type = dbi_result_get_field_type_idx(res, 1);
900 if (field_type != DBI_TYPE_ERROR) {
901 if (type == TYPE_STRING) {
902 result.result_string = strdup(get_field_str(res, metric, type));
903 } else {
904 get_field_wrapper gfw = get_field(res, metric, type);
905 result.result_number = gfw.value;
906 }
907 } else {
908 // Error when retrieving the field, that is OK if the Query result is not of
909 // interest
910 if (metric != METRIC_QUERY_RESULT) {
911 query_processing_state = STATE_OK;
912 } else {
913 dbi_conn_error(conn, &result.error_string);
914 // np_dbi_print_error(conn, "CRITICAL - failed to fetch data");
915 query_processing_state = STATE_CRITICAL;
916 }
917 }
918 }
808 } 919 }
920 dbi_result_free(res);
809 921
810 status = get_query_result(conn, res, res_val_str, res_val, metric, type); 922 result.query_processing_status = query_processing_state;
811
812 gettimeofday(&timeval_end, NULL);
813 *res_time = timediff(timeval_start, timeval_end);
814
815 if (verbose) {
816 printf("Time elapsed: %f\n", *res_time);
817 }
818 923
819 return status; 924 return result;
820} 925}
821 926
822double timediff(struct timeval start, struct timeval end) { 927static double timediff(struct timeval start, struct timeval end) {
823 double diff; 928 double diff;
824 929
825 while (start.tv_usec > end.tv_usec) { 930 while (start.tv_usec > end.tv_usec) {
@@ -830,7 +935,7 @@ double timediff(struct timeval start, struct timeval end) {
830 return diff; 935 return diff;
831} 936}
832 937
833void np_dbi_print_error(dbi_conn conn, char *fmt, ...) { 938static void np_dbi_print_error(dbi_conn conn, char *fmt, ...) {
834 const char *errmsg = NULL; 939 const char *errmsg = NULL;
835 va_list ap; 940 va_list ap;
836 941
diff --git a/plugins/check_dbi.d/config.h b/plugins/check_dbi.d/config.h
index f6f0d7b3..25d74a12 100644
--- a/plugins/check_dbi.d/config.h
+++ b/plugins/check_dbi.d/config.h
@@ -3,18 +3,19 @@
3#include "../../config.h" 3#include "../../config.h"
4#include <stddef.h> 4#include <stddef.h>
5#include "../../lib/monitoringplug.h" 5#include "../../lib/monitoringplug.h"
6#include "thresholds.h"
6 7
7typedef enum { 8typedef enum {
8 METRIC_CONN_TIME, 9 METRIC_CONN_TIME,
9 METRIC_SERVER_VERSION, 10 METRIC_SERVER_VERSION,
10 METRIC_QUERY_RESULT, 11 METRIC_QUERY_RESULT,
11 METRIC_QUERY_TIME, 12 METRIC_QUERY_TIME,
12} mp_dbi_metric; 13} check_dbi_metric;
13 14
14typedef enum { 15typedef enum {
15 TYPE_NUMERIC, 16 TYPE_NUMERIC,
16 TYPE_STRING, 17 TYPE_STRING,
17} mp_dbi_type; 18} check_dbi_type;
18 19
19typedef struct { 20typedef struct {
20 char *key; 21 char *key;
@@ -24,20 +25,22 @@ typedef struct {
24typedef struct { 25typedef struct {
25 char *dbi_driver; 26 char *dbi_driver;
26 char *host; 27 char *host;
28
27 driver_option_t *dbi_options; 29 driver_option_t *dbi_options;
28 size_t dbi_options_num; 30 size_t dbi_options_num;
29 char *dbi_database; 31
30 char *dbi_query; 32 char *database;
33 char *query;
31 34
32 char *expect; 35 char *expect;
33 char *expect_re_str; 36 char *expect_re_str;
34 int expect_re_cflags; 37 int expect_re_cflags;
35 mp_dbi_metric metric; 38 check_dbi_metric metric;
36 mp_dbi_type type; 39 check_dbi_type type;
37 char *warning_range; 40 mp_thresholds thresholds;
38 char *critical_range;
39 thresholds *dbi_thresholds;
40 41
42 bool output_format_is_set;
43 mp_output_format output_format;
41} check_dbi_config; 44} check_dbi_config;
42 45
43check_dbi_config check_dbi_config_init() { 46check_dbi_config check_dbi_config_init() {
@@ -46,8 +49,8 @@ check_dbi_config check_dbi_config_init() {
46 .host = NULL, 49 .host = NULL,
47 .dbi_options = NULL, 50 .dbi_options = NULL,
48 .dbi_options_num = 0, 51 .dbi_options_num = 0,
49 .dbi_database = NULL, 52 .database = NULL,
50 .dbi_query = NULL, 53 .query = NULL,
51 54
52 .expect = NULL, 55 .expect = NULL,
53 .expect_re_str = NULL, 56 .expect_re_str = NULL,
@@ -55,9 +58,9 @@ check_dbi_config check_dbi_config_init() {
55 .metric = METRIC_QUERY_RESULT, 58 .metric = METRIC_QUERY_RESULT,
56 .type = TYPE_NUMERIC, 59 .type = TYPE_NUMERIC,
57 60
58 .warning_range = NULL, 61 .thresholds = mp_thresholds_init(),
59 .critical_range = NULL, 62
60 .dbi_thresholds = NULL, 63 .output_format_is_set = false,
61 }; 64 };
62 return tmp; 65 return tmp;
63} 66}
diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c
index 77a33304..1b2e2826 100644
--- a/plugins/check_ldap.c
+++ b/plugins/check_ldap.c
@@ -27,12 +27,11 @@
27 *****************************************************************************/ 27 *****************************************************************************/
28 28
29/* progname may be check_ldaps */ 29/* progname may be check_ldaps */
30char *progname = "check_ldap"; 30#include "output.h"
31const char *copyright = "2000-2024";
32const char *email = "devel@monitoring-plugins.org";
33
34#include "common.h" 31#include "common.h"
35#include "netutils.h" 32#include "netutils.h"
33#include "perfdata.h"
34#include "thresholds.h"
36#include "utils.h" 35#include "utils.h"
37#include "check_ldap.d/config.h" 36#include "check_ldap.d/config.h"
38 37
@@ -41,6 +40,10 @@ const char *email = "devel@monitoring-plugins.org";
41#define LDAP_DEPRECATED 1 40#define LDAP_DEPRECATED 1
42#include <ldap.h> 41#include <ldap.h>
43 42
43char *progname = "check_ldap";
44const char *copyright = "2000-2024";
45const char *email = "devel@monitoring-plugins.org";
46
44enum { 47enum {
45 DEFAULT_PORT = 389 48 DEFAULT_PORT = 389
46}; 49};
@@ -79,6 +82,10 @@ int main(int argc, char *argv[]) {
79 82
80 const check_ldap_config config = tmp_config.config; 83 const check_ldap_config config = tmp_config.config;
81 84
85 if (config.output_format_is_set) {
86 mp_set_format(config.output_format);
87 }
88
82 /* initialize alarm signal handling */ 89 /* initialize alarm signal handling */
83 signal(SIGALRM, socket_timeout_alarm_handler); 90 signal(SIGALRM, socket_timeout_alarm_handler);
84 91
@@ -89,101 +96,172 @@ int main(int argc, char *argv[]) {
89 struct timeval start_time; 96 struct timeval start_time;
90 gettimeofday(&start_time, NULL); 97 gettimeofday(&start_time, NULL);
91 98
99 mp_check overall = mp_check_init();
100
92 LDAP *ldap_connection; 101 LDAP *ldap_connection;
93 /* initialize ldap */ 102 /* initialize ldap */
103 {
94#ifdef HAVE_LDAP_INIT 104#ifdef HAVE_LDAP_INIT
95 if (!(ldap_connection = ldap_init(config.ld_host, config.ld_port))) { 105 mp_subcheck sc_ldap_init = mp_subcheck_init();
96 printf("Could not connect to the server at port %i\n", config.ld_port); 106 if (!(ldap_connection = ldap_init(config.ld_host, config.ld_port))) {
97 return STATE_CRITICAL; 107 xasprintf(&sc_ldap_init.output, "could not connect to the server at port %i",
98 } 108 config.ld_port);
109 sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_CRITICAL);
110 mp_add_subcheck_to_check(&overall, sc_ldap_init);
111 mp_exit(overall);
112 } else {
113 xasprintf(&sc_ldap_init.output, "connected to the server at port %i", config.ld_port);
114 sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_OK);
115 mp_add_subcheck_to_check(&overall, sc_ldap_init);
116 }
99#else 117#else
100 if (!(ld = ldap_open(config.ld_host, config.ld_port))) { 118 mp_subcheck sc_ldap_init = mp_subcheck_init();
101 if (verbose) { 119 if (!(ld = ldap_open(config.ld_host, config.ld_port))) {
102 ldap_perror(ldap_connection, "ldap_open"); 120 if (verbose) {
121 ldap_perror(ldap_connection, "ldap_open");
122 }
123 xasprintf(&sc_ldap_init.output, "Could not connect to the server at port %i"), config.ld_port);
124 sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_CRITICAL);
125 mp_add_subcheck_to_check(&overall, sc_ldap_init);
126 mp_exit(overall);
127 } else {
128 xasprintf(&sc_ldap_init.output, "connected to the server at port %i", config.ld_port);
129 sc_ldap_init = mp_set_subcheck_state(sc_ldap_init, STATE_OK);
130 mp_add_subcheck_to_check(&overall, sc_ldap_init);
103 } 131 }
104 printf(_("Could not connect to the server at port %i\n"), config.ld_port);
105 return STATE_CRITICAL;
106 }
107#endif /* HAVE_LDAP_INIT */ 132#endif /* HAVE_LDAP_INIT */
133 }
108 134
109#ifdef HAVE_LDAP_SET_OPTION 135#ifdef HAVE_LDAP_SET_OPTION
110 /* set ldap options */ 136 /* set ldap options */
137 mp_subcheck sc_ldap_set_opts = mp_subcheck_init();
111 if (ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &config.ld_protocol) != 138 if (ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &config.ld_protocol) !=
112 LDAP_OPT_SUCCESS) { 139 LDAP_OPT_SUCCESS) {
113 printf(_("Could not set protocol version %d\n"), config.ld_protocol); 140 xasprintf(&sc_ldap_set_opts.output, "Could not set protocol version %d",
114 return STATE_CRITICAL; 141 config.ld_protocol);
142 sc_ldap_set_opts = mp_set_subcheck_state(sc_ldap_set_opts, STATE_CRITICAL);
143 mp_add_subcheck_to_check(&overall, sc_ldap_set_opts);
144 mp_exit(overall);
145 } else {
146 xasprintf(&sc_ldap_set_opts.output, "set protocol version %d", config.ld_protocol);
147 sc_ldap_set_opts = mp_set_subcheck_state(sc_ldap_set_opts, STATE_OK);
148 mp_add_subcheck_to_check(&overall, sc_ldap_set_opts);
115 } 149 }
116#endif 150#endif
117 151
118 int version = 3; 152 int version = 3;
119 int tls; 153 int tls;
120 if (config.ld_port == LDAPS_PORT || config.ssl_on_connect) { 154 {
155 if (config.ld_port == LDAPS_PORT || config.ssl_on_connect) {
121#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS) 156#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS)
122 /* ldaps: set option tls */ 157 /* ldaps: set option tls */
123 tls = LDAP_OPT_X_TLS_HARD; 158 tls = LDAP_OPT_X_TLS_HARD;
124 159
125 if (ldap_set_option(ldap_connection, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) { 160 mp_subcheck sc_ldap_tls_init = mp_subcheck_init();
126 if (verbose) { 161 if (ldap_set_option(ldap_connection, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) {
127 ldap_perror(ldap_connection, "ldaps_option"); 162 if (verbose) {
163 ldap_perror(ldap_connection, "ldaps_option");
164 }
165 xasprintf(&sc_ldap_tls_init.output, "could not init TLS at port %i!",
166 config.ld_port);
167 sc_ldap_tls_init = mp_set_subcheck_state(sc_ldap_tls_init, STATE_CRITICAL);
168 mp_add_subcheck_to_check(&overall, sc_ldap_tls_init);
169 mp_exit(overall);
170 } else {
171 xasprintf(&sc_ldap_tls_init.output, "initiated TLS at port %i!", config.ld_port);
172 sc_ldap_tls_init = mp_set_subcheck_state(sc_ldap_tls_init, STATE_OK);
173 mp_add_subcheck_to_check(&overall, sc_ldap_tls_init);
128 } 174 }
129 printf(_("Could not init TLS at port %i!\n"), config.ld_port);
130 return STATE_CRITICAL;
131 }
132#else 175#else
133 printf(_("TLS not supported by the libraries!\n")); 176 printf(_("TLS not supported by the libraries!\n"));
134 return STATE_CRITICAL; 177 exit(STATE_CRITICAL);
135#endif /* LDAP_OPT_X_TLS */ 178#endif /* LDAP_OPT_X_TLS */
136 } else if (config.starttls) { 179 } else if (config.starttls) {
137#if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S) 180#if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S)
138 /* ldap with startTLS: set option version */ 181 /* ldap with startTLS: set option version */
139 if (ldap_get_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version) == 182 if (ldap_get_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version) ==
140 LDAP_OPT_SUCCESS) { 183 LDAP_OPT_SUCCESS) {
141 if (version < LDAP_VERSION3) { 184 if (version < LDAP_VERSION3) {
142 version = LDAP_VERSION3; 185 version = LDAP_VERSION3;
143 ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version); 186 ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version);
187 }
144 } 188 }
145 } 189 /* call start_tls */
146 /* call start_tls */ 190 mp_subcheck sc_ldap_starttls = mp_subcheck_init();
147 if (ldap_start_tls_s(ldap_connection, NULL, NULL) != LDAP_SUCCESS) { 191 if (ldap_start_tls_s(ldap_connection, NULL, NULL) != LDAP_SUCCESS) {
148 if (verbose) { 192 if (verbose) {
149 ldap_perror(ldap_connection, "ldap_start_tls"); 193 ldap_perror(ldap_connection, "ldap_start_tls");
194 }
195 xasprintf(&sc_ldap_starttls.output, "could not init STARTTLS at port %i!",
196 config.ld_port);
197 sc_ldap_starttls = mp_set_subcheck_state(sc_ldap_starttls, STATE_CRITICAL);
198 mp_add_subcheck_to_check(&overall, sc_ldap_starttls);
199 mp_exit(overall);
200 } else {
201 xasprintf(&sc_ldap_starttls.output, "initiated STARTTLS at port %i!",
202 config.ld_port);
203 sc_ldap_starttls = mp_set_subcheck_state(sc_ldap_starttls, STATE_OK);
204 mp_add_subcheck_to_check(&overall, sc_ldap_starttls);
150 } 205 }
151 printf(_("Could not init startTLS at port %i!\n"), config.ld_port);
152 return STATE_CRITICAL;
153 }
154#else 206#else
155 printf(_("startTLS not supported by the library, needs LDAPv3!\n")); 207 printf(_("startTLS not supported by the library, needs LDAPv3!\n"));
156 return STATE_CRITICAL; 208 exit(STATE_CRITICAL);
157#endif /* HAVE_LDAP_START_TLS_S */ 209#endif /* HAVE_LDAP_START_TLS_S */
210 }
158 } 211 }
159 212
160 /* bind to the ldap server */ 213 /* bind to the ldap server */
161 if (ldap_bind_s(ldap_connection, config.ld_binddn, config.ld_passwd, LDAP_AUTH_SIMPLE) != 214 {
162 LDAP_SUCCESS) { 215 mp_subcheck sc_ldap_bind = mp_subcheck_init();
163 if (verbose) { 216 int ldap_error =
164 ldap_perror(ldap_connection, "ldap_bind"); 217 ldap_bind_s(ldap_connection, config.ld_binddn, config.ld_passwd, LDAP_AUTH_SIMPLE);
218 if (ldap_error != LDAP_SUCCESS) {
219 if (verbose) {
220 ldap_perror(ldap_connection, "ldap_bind");
221 }
222
223 xasprintf(&sc_ldap_bind.output, "could not bind to the LDAP server: %s",
224 ldap_err2string(ldap_error));
225 sc_ldap_bind = mp_set_subcheck_state(sc_ldap_bind, STATE_CRITICAL);
226 mp_add_subcheck_to_check(&overall, sc_ldap_bind);
227 mp_exit(overall);
228 } else {
229 xasprintf(&sc_ldap_bind.output, "execute bind to the LDAP server");
230 sc_ldap_bind = mp_set_subcheck_state(sc_ldap_bind, STATE_OK);
231 mp_add_subcheck_to_check(&overall, sc_ldap_bind);
165 } 232 }
166 printf(_("Could not bind to the LDAP server\n"));
167 return STATE_CRITICAL;
168 } 233 }
169 234
170 LDAPMessage *result; 235 LDAPMessage *result;
171 int num_entries = 0;
172 /* do a search of all objectclasses in the base dn */ 236 /* do a search of all objectclasses in the base dn */
173 if (ldap_search_s(ldap_connection, config.ld_base, 237 {
174 (config.crit_entries != NULL || config.warn_entries != NULL) 238 mp_subcheck sc_ldap_search = mp_subcheck_init();
175 ? LDAP_SCOPE_SUBTREE 239 int ldap_error = ldap_search_s(
176 : LDAP_SCOPE_BASE, 240 ldap_connection, config.ld_base,
177 config.ld_attr, NULL, 0, &result) != LDAP_SUCCESS) { 241 (config.entries_thresholds.warning_is_set || config.entries_thresholds.critical_is_set)
178 if (verbose) { 242 ? LDAP_SCOPE_SUBTREE
179 ldap_perror(ldap_connection, "ldap_search"); 243 : LDAP_SCOPE_BASE,
244 config.ld_attr, NULL, 0, &result);
245
246 if (ldap_error != LDAP_SUCCESS) {
247 if (verbose) {
248 ldap_perror(ldap_connection, "ldap_search");
249 }
250 xasprintf(&sc_ldap_search.output, "could not search/find objectclasses in %s: %s",
251 config.ld_base, ldap_err2string(ldap_error));
252 sc_ldap_search = mp_set_subcheck_state(sc_ldap_search, STATE_CRITICAL);
253 mp_add_subcheck_to_check(&overall, sc_ldap_search);
254 mp_exit(overall);
255 } else {
256 xasprintf(&sc_ldap_search.output, "search/find objectclasses in %s", config.ld_base);
257 sc_ldap_search = mp_set_subcheck_state(sc_ldap_search, STATE_OK);
258 mp_add_subcheck_to_check(&overall, sc_ldap_search);
180 } 259 }
181 printf(_("Could not search/find objectclasses in %s\n"), config.ld_base);
182 return STATE_CRITICAL;
183 } 260 }
184 261
185 if (config.crit_entries != NULL || config.warn_entries != NULL) { 262 int num_entries = ldap_count_entries(ldap_connection, result);
186 num_entries = ldap_count_entries(ldap_connection, result); 263 if (verbose) {
264 printf("entries found: %d\n", num_entries);
187 } 265 }
188 266
189 /* unbind from the ldap server */ 267 /* unbind from the ldap server */
@@ -193,50 +271,50 @@ int main(int argc, char *argv[]) {
193 alarm(0); 271 alarm(0);
194 272
195 /* calculate the elapsed time and compare to thresholds */ 273 /* calculate the elapsed time and compare to thresholds */
196
197 long microsec = deltime(start_time); 274 long microsec = deltime(start_time);
198 double elapsed_time = (double)microsec / 1.0e6; 275 double elapsed_time = (double)microsec / 1.0e6;
199 mp_state_enum status = STATE_UNKNOWN; 276 mp_perfdata pd_connection_time = perfdata_init();
200 if (config.crit_time_set && elapsed_time > config.crit_time) { 277 pd_connection_time.label = "time";
201 status = STATE_CRITICAL; 278 pd_connection_time.value = mp_create_pd_value(elapsed_time);
202 } else if (config.warn_time_set && elapsed_time > config.warn_time) { 279 pd_connection_time = mp_pd_set_thresholds(pd_connection_time, config.connection_time_threshold);
203 status = STATE_WARNING;
204 } else {
205 status = STATE_OK;
206 }
207 280
208 if (config.entries_thresholds != NULL) { 281 mp_subcheck sc_connection_time = mp_subcheck_init();
209 if (verbose) { 282 mp_add_perfdata_to_subcheck(&sc_connection_time, pd_connection_time);
210 printf("entries found: %d\n", num_entries); 283
211 print_thresholds("entry thresholds", config.entries_thresholds); 284 mp_state_enum connection_time_state = mp_get_pd_status(pd_connection_time);
212 } 285 sc_connection_time = mp_set_subcheck_state(sc_connection_time, connection_time_state);
213 mp_state_enum status_entries = get_status(num_entries, config.entries_thresholds);
214 if (status_entries == STATE_CRITICAL) {
215 status = STATE_CRITICAL;
216 } else if (status != STATE_CRITICAL) {
217 status = status_entries;
218 }
219 }
220 286
221 /* print out the result */ 287 if (connection_time_state == STATE_OK) {
222 if (config.crit_entries != NULL || config.warn_entries != NULL) { 288 xasprintf(&sc_connection_time.output, "connection time %.3fs is within thresholds",
223 printf(_("LDAP %s - found %d entries in %.3f seconds|%s %s\n"), state_text(status), 289 elapsed_time);
224 num_entries, elapsed_time,
225 fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time,
226 config.crit_time_set, config.crit_time, true, 0, false, 0),
227 sperfdata("entries", (double)num_entries, "", config.warn_entries,
228 config.crit_entries, true, 0.0, false, 0.0));
229 } else { 290 } else {
230 printf(_("LDAP %s - %.3f seconds response time|%s\n"), state_text(status), elapsed_time, 291 xasprintf(&sc_connection_time.output, "connection time %.3fs is violating thresholds",
231 fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time, 292 elapsed_time);
232 config.crit_time_set, config.crit_time, true, 0, false, 0));
233 } 293 }
234 294
235 exit(status); 295 mp_add_subcheck_to_check(&overall, sc_connection_time);
296
297 mp_perfdata pd_num_entries = perfdata_init();
298 pd_num_entries.label = "entries";
299 pd_num_entries.value = mp_create_pd_value(num_entries);
300 pd_num_entries = mp_pd_set_thresholds(pd_num_entries, config.entries_thresholds);
301
302 mp_subcheck sc_num_entries = mp_subcheck_init();
303 mp_add_perfdata_to_subcheck(&sc_num_entries, pd_num_entries);
304 xasprintf(&sc_num_entries.output, "found %d entries", num_entries);
305 sc_num_entries = mp_set_subcheck_state(sc_num_entries, mp_get_pd_status(pd_num_entries));
306
307 mp_add_subcheck_to_check(&overall, sc_num_entries);
308
309 mp_exit(overall);
236} 310}
237 311
238/* process command-line arguments */ 312/* process command-line arguments */
239check_ldap_config_wrapper process_arguments(int argc, char **argv) { 313check_ldap_config_wrapper process_arguments(int argc, char **argv) {
314 enum {
315 output_format_index = CHAR_MAX + 1,
316 };
317
240 /* initialize the long option struct */ 318 /* initialize the long option struct */
241 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, 319 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
242 {"version", no_argument, 0, 'V'}, 320 {"version", no_argument, 0, 'V'},
@@ -260,6 +338,7 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) {
260 {"warn-entries", required_argument, 0, 'W'}, 338 {"warn-entries", required_argument, 0, 'W'},
261 {"crit-entries", required_argument, 0, 'C'}, 339 {"crit-entries", required_argument, 0, 'C'},
262 {"verbose", no_argument, 0, 'v'}, 340 {"verbose", no_argument, 0, 'v'},
341 {"output-format", required_argument, 0, output_format_index},
263 {0, 0, 0, 0}}; 342 {0, 0, 0, 0}};
264 343
265 check_ldap_config_wrapper result = { 344 check_ldap_config_wrapper result = {
@@ -319,20 +398,38 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) {
319 case 'P': 398 case 'P':
320 result.config.ld_passwd = optarg; 399 result.config.ld_passwd = optarg;
321 break; 400 break;
322 case 'w': 401 case 'w': {
323 result.config.warn_time_set = true; 402 mp_range_parsed tmp = mp_parse_range_string(optarg);
324 result.config.warn_time = strtod(optarg, NULL); 403 if (tmp.error != MP_PARSING_SUCCES) {
325 break; 404 die(STATE_UNKNOWN, "failed to parse warning connection time threshold");
326 case 'c': 405 }
327 result.config.crit_time_set = true; 406 result.config.connection_time_threshold =
328 result.config.crit_time = strtod(optarg, NULL); 407 mp_thresholds_set_warn(result.config.connection_time_threshold, tmp.range);
329 break; 408 } break;
330 case 'W': 409 case 'c': {
331 result.config.warn_entries = optarg; 410 mp_range_parsed tmp = mp_parse_range_string(optarg);
332 break; 411 if (tmp.error != MP_PARSING_SUCCES) {
333 case 'C': 412 die(STATE_UNKNOWN, "failed to parse critical connection time threshold");
334 result.config.crit_entries = optarg; 413 }
335 break; 414 result.config.connection_time_threshold =
415 mp_thresholds_set_crit(result.config.connection_time_threshold, tmp.range);
416 } break;
417 case 'W': {
418 mp_range_parsed tmp = mp_parse_range_string(optarg);
419 if (tmp.error != MP_PARSING_SUCCES) {
420 die(STATE_UNKNOWN, "failed to parse number of entries warning threshold");
421 }
422 result.config.entries_thresholds =
423 mp_thresholds_set_warn(result.config.entries_thresholds, tmp.range);
424 } break;
425 case 'C': {
426 mp_range_parsed tmp = mp_parse_range_string(optarg);
427 if (tmp.error != MP_PARSING_SUCCES) {
428 die(STATE_UNKNOWN, "failed to parse number of entries critical threshold");
429 }
430 result.config.entries_thresholds =
431 mp_thresholds_set_crit(result.config.entries_thresholds, tmp.range);
432 } break;
336#ifdef HAVE_LDAP_SET_OPTION 433#ifdef HAVE_LDAP_SET_OPTION
337 case '2': 434 case '2':
338 result.config.ld_protocol = 2; 435 result.config.ld_protocol = 2;
@@ -371,6 +468,18 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) {
371 usage(_("IPv6 support not available\n")); 468 usage(_("IPv6 support not available\n"));
372#endif 469#endif
373 break; 470 break;
471 case output_format_index: {
472 parsed_output_format parser = mp_parse_output_format(optarg);
473 if (!parser.parsing_success) {
474 // TODO List all available formats here, maybe add anothoer usage function
475 printf("Invalid output format: %s\n", optarg);
476 exit(STATE_UNKNOWN);
477 }
478
479 result.config.output_format_is_set = true;
480 result.config.output_format = parser.output_format;
481 break;
482 }
374 default: 483 default:
375 usage5(); 484 usage5();
376 } 485 }
@@ -406,11 +515,6 @@ check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper config_wr
406 usage4(_("Please specify the LDAP base\n")); 515 usage4(_("Please specify the LDAP base\n"));
407 } 516 }
408 517
409 if (config_wrapper.config.crit_entries != NULL || config_wrapper.config.warn_entries != NULL) {
410 set_thresholds(&config_wrapper.config.entries_thresholds,
411 config_wrapper.config.warn_entries, config_wrapper.config.crit_entries);
412 }
413
414 if (config_wrapper.config.ld_passwd == NULL) { 518 if (config_wrapper.config.ld_passwd == NULL) {
415 config_wrapper.config.ld_passwd = getenv("LDAP_PASSWORD"); 519 config_wrapper.config.ld_passwd = getenv("LDAP_PASSWORD");
416 } 520 }
@@ -471,6 +575,7 @@ void print_help(void) {
471 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 575 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
472 576
473 printf(UT_VERBOSE); 577 printf(UT_VERBOSE);
578 printf(UT_OUTPUT_FORMAT);
474 579
475 printf("\n"); 580 printf("\n");
476 printf("%s\n", _("Notes:")); 581 printf("%s\n", _("Notes:"));
diff --git a/plugins/check_ldap.d/config.h b/plugins/check_ldap.d/config.h
index c8a40610..50191725 100644
--- a/plugins/check_ldap.d/config.h
+++ b/plugins/check_ldap.d/config.h
@@ -1,6 +1,7 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
4#include "thresholds.h" 5#include "thresholds.h"
5#include <stddef.h> 6#include <stddef.h>
6 7
@@ -25,13 +26,11 @@ typedef struct {
25 int ld_protocol; 26 int ld_protocol;
26#endif 27#endif
27 28
28 char *warn_entries; 29 mp_thresholds entries_thresholds;
29 char *crit_entries; 30 mp_thresholds connection_time_threshold;
30 thresholds *entries_thresholds; 31
31 bool warn_time_set; 32 bool output_format_is_set;
32 double warn_time; 33 mp_output_format output_format;
33 bool crit_time_set;
34 double crit_time;
35} check_ldap_config; 34} check_ldap_config;
36 35
37check_ldap_config check_ldap_config_init() { 36check_ldap_config check_ldap_config_init() {
@@ -48,13 +47,10 @@ check_ldap_config check_ldap_config_init() {
48 .ld_protocol = DEFAULT_PROTOCOL, 47 .ld_protocol = DEFAULT_PROTOCOL,
49#endif 48#endif
50 49
51 .warn_entries = NULL, 50 .entries_thresholds = mp_thresholds_init(),
52 .crit_entries = NULL, 51 .connection_time_threshold = mp_thresholds_init(),
53 .entries_thresholds = NULL, 52
54 .warn_time_set = false, 53 .output_format_is_set = false,
55 .warn_time = 0,
56 .crit_time_set = false,
57 .crit_time = 0,
58 }; 54 };
59 return tmp; 55 return tmp;
60} 56}
diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c
index 6134d6c6..9d8094c0 100644
--- a/plugins/check_mysql.c
+++ b/plugins/check_mysql.c
@@ -30,13 +30,11 @@
30 * 30 *
31 *****************************************************************************/ 31 *****************************************************************************/
32 32
33const char *progname = "check_mysql";
34const char *copyright = "1999-2024";
35const char *email = "devel@monitoring-plugins.org";
36
37#define REPLICA_RESULTSIZE 96
38
39#include "common.h" 33#include "common.h"
34#include "output.h"
35#include "perfdata.h"
36#include "states.h"
37#include "thresholds.h"
40#include "utils.h" 38#include "utils.h"
41#include "utils_base.h" 39#include "utils_base.h"
42#include "netutils.h" 40#include "netutils.h"
@@ -46,8 +44,14 @@ const char *email = "devel@monitoring-plugins.org";
46#include <mysqld_error.h> 44#include <mysqld_error.h>
47#include <errmsg.h> 45#include <errmsg.h>
48 46
47const char *progname = "check_mysql";
48const char *copyright = "1999-2024";
49const char *email = "devel@monitoring-plugins.org";
50
49static int verbose = 0; 51static int verbose = 0;
50 52
53#define REPLICA_RESULTSIZE 96
54
51#define LENGTH_METRIC_UNIT 6 55#define LENGTH_METRIC_UNIT 6
52static const char *metric_unit[LENGTH_METRIC_UNIT] = { 56static const char *metric_unit[LENGTH_METRIC_UNIT] = {
53 "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache", 57 "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache",
@@ -110,7 +114,11 @@ int main(int argc, char **argv) {
110 mysql_ssl_set(&mysql, config.key, config.cert, config.ca_cert, config.ca_dir, 114 mysql_ssl_set(&mysql, config.key, config.cert, config.ca_cert, config.ca_dir,
111 config.ciphers); 115 config.ciphers);
112 } 116 }
113 /* establish a connection to the server and error checking */ 117
118 mp_check overall = mp_check_init();
119
120 mp_subcheck sc_connection = mp_subcheck_init();
121 /* establish a connection to the server and check for errors */
114 if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, 122 if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db,
115 config.db_port, config.db_socket, 0)) { 123 config.db_port, config.db_socket, 0)) {
116 /* Depending on internally-selected auth plugin MySQL might return */ 124 /* Depending on internally-selected auth plugin MySQL might return */
@@ -118,78 +126,115 @@ int main(int argc, char **argv) {
118 /* Semantically these errors are the same. */ 126 /* Semantically these errors are the same. */
119 if (config.ignore_auth && (mysql_errno(&mysql) == ER_ACCESS_DENIED_ERROR || 127 if (config.ignore_auth && (mysql_errno(&mysql) == ER_ACCESS_DENIED_ERROR ||
120 mysql_errno(&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) { 128 mysql_errno(&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) {
121 printf("MySQL OK - Version: %s (protocol %d)\n", mysql_get_server_info(&mysql), 129 xasprintf(&sc_connection.output, "Version: %s (protocol %d)",
122 mysql_get_proto_info(&mysql)); 130 mysql_get_server_info(&mysql), mysql_get_proto_info(&mysql));
123 mysql_close(&mysql); 131 sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK);
124 return STATE_OK;
125 }
126 132
127 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) { 133 mysql_close(&mysql);
128 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
129 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) {
130 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
131 } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) {
132 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
133 } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) {
134 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
135 } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) {
136 die(STATE_WARNING, "%s\n", mysql_error(&mysql));
137 } else { 134 } else {
138 die(STATE_CRITICAL, "%s\n", mysql_error(&mysql)); 135 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) {
136 sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING);
137 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
138 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) {
139 sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING);
140 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
141 } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) {
142 sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING);
143 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
144 } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) {
145 sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING);
146 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
147 } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) {
148 sc_connection = mp_set_subcheck_state(sc_connection, STATE_WARNING);
149 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
150 } else {
151 sc_connection = mp_set_subcheck_state(sc_connection, STATE_CRITICAL);
152 xasprintf(&sc_connection.output, "%s", mysql_error(&mysql));
153 }
139 } 154 }
155
156 mp_add_subcheck_to_check(&overall, sc_connection);
157 mp_exit(overall);
158 } else {
159 // successful connection
160 sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK);
161 xasprintf(&sc_connection.output, "Version: %s (protocol %d)", mysql_get_server_info(&mysql),
162 mysql_get_proto_info(&mysql));
163 mp_add_subcheck_to_check(&overall, sc_connection);
140 } 164 }
141 165
142 /* get the server stats */ 166 /* get the server stats */
143 char *result = strdup(mysql_stat(&mysql)); 167 char *mysql_stats = strdup(mysql_stat(&mysql));
168
169 mp_subcheck sc_stats = mp_subcheck_init();
170 sc_stats = mp_set_subcheck_default_state(sc_stats, STATE_OK);
144 171
145 /* error checking once more */ 172 /* error checking once more */
146 if (mysql_error(&mysql)) { 173 if (mysql_errno(&mysql) != 0) {
147 if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { 174 if ((mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) ||
148 die(STATE_CRITICAL, "%s\n", mysql_error(&mysql)); 175 (mysql_errno(&mysql) == CR_SERVER_LOST) || (mysql_errno(&mysql) == CR_UNKNOWN_ERROR)) {
149 } else if (mysql_errno(&mysql) == CR_SERVER_LOST) { 176 sc_stats = mp_set_subcheck_state(sc_stats, STATE_CRITICAL);
150 die(STATE_CRITICAL, "%s\n", mysql_error(&mysql)); 177 xasprintf(&sc_stats.output, "Retrieving stats failed: %s", mysql_error(&mysql));
151 } else if (mysql_errno(&mysql) == CR_UNKNOWN_ERROR) { 178 } else {
152 die(STATE_CRITICAL, "%s\n", mysql_error(&mysql)); 179 // not sure which error modes occur here, but mysql_error indicates an error
180 sc_stats = mp_set_subcheck_state(sc_stats, STATE_WARNING);
181 xasprintf(&sc_stats.output, "retrieving stats caused an error: %s",
182 mysql_error(&mysql));
153 } 183 }
184
185 mp_add_subcheck_to_check(&overall, sc_stats);
186 mp_exit(overall);
187 } else {
188 xasprintf(&sc_stats.output, "retrieved stats: %s", mysql_stats);
189 sc_stats = mp_set_subcheck_state(sc_stats, STATE_OK);
190 mp_add_subcheck_to_check(&overall, sc_stats);
154 } 191 }
155 192
156 char *perf = strdup("");
157 char *error = NULL;
158 MYSQL_RES *res; 193 MYSQL_RES *res;
159 MYSQL_ROW row; 194 MYSQL_ROW row;
195 mp_subcheck sc_query = mp_subcheck_init();
160 /* try to fetch some perf data */ 196 /* try to fetch some perf data */
161 if (mysql_query(&mysql, "show global status") == 0) { 197 if (mysql_query(&mysql, "show global status") == 0) {
162 if ((res = mysql_store_result(&mysql)) == NULL) { 198 if ((res = mysql_store_result(&mysql)) == NULL) {
163 error = strdup(mysql_error(&mysql)); 199 xasprintf(&sc_connection.output, "query failed - status store_result error: %s",
200 mysql_error(&mysql));
164 mysql_close(&mysql); 201 mysql_close(&mysql);
165 die(STATE_CRITICAL, _("status store_result error: %s\n"), error); 202
203 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
204 mp_add_subcheck_to_check(&overall, sc_query);
205 mp_exit(overall);
166 } 206 }
167 207
168 while ((row = mysql_fetch_row(res)) != NULL) { 208 while ((row = mysql_fetch_row(res)) != NULL) {
169 for (int i = 0; i < LENGTH_METRIC_UNIT; i++) { 209 for (int i = 0; i < LENGTH_METRIC_UNIT; i++) {
170 if (strcmp(row[0], metric_unit[i]) == 0) { 210 if (strcmp(row[0], metric_unit[i]) == 0) {
171 xasprintf(&perf, "%s%s ", perf, 211 mp_perfdata pd_mysql_stat = perfdata_init();
172 perfdata(metric_unit[i], atol(row[1]), "", false, 0, false, 0, false, 212 pd_mysql_stat.label = (char *)metric_unit[i];
173 0, false, 0)); 213 pd_mysql_stat.value = mp_create_pd_value(atol(row[1]));
214 mp_add_perfdata_to_subcheck(&sc_stats, pd_mysql_stat);
174 continue; 215 continue;
175 } 216 }
176 } 217 }
218
177 for (int i = 0; i < LENGTH_METRIC_COUNTER; i++) { 219 for (int i = 0; i < LENGTH_METRIC_COUNTER; i++) {
178 if (strcmp(row[0], metric_counter[i]) == 0) { 220 if (strcmp(row[0], metric_counter[i]) == 0) {
179 xasprintf(&perf, "%s%s ", perf, 221 mp_perfdata pd_mysql_stat = perfdata_init();
180 perfdata(metric_counter[i], atol(row[1]), "c", false, 0, false, 0, 222 pd_mysql_stat.label = (char *)metric_counter[i];
181 false, 0, false, 0)); 223 pd_mysql_stat.value = mp_create_pd_value(atol(row[1]));
224 pd_mysql_stat.uom = "c";
225 mp_add_perfdata_to_subcheck(&sc_stats, pd_mysql_stat);
182 continue; 226 continue;
183 } 227 }
184 } 228 }
185 } 229 }
186 /* remove trailing space */ 230 } else {
187 if (strlen(perf) > 0) { 231 // Query failed!
188 perf[strlen(perf) - 1] = '\0'; 232 xasprintf(&sc_connection.output, "query failed");
189 } 233 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
234 mp_add_subcheck_to_check(&overall, sc_query);
235 mp_exit(overall);
190 } 236 }
191 237
192 char replica_result[REPLICA_RESULTSIZE] = {0};
193 if (config.check_replica) { 238 if (config.check_replica) {
194 // Detect which version we are, on older version 239 // Detect which version we are, on older version
195 // "show slave status" should work, on newer ones 240 // "show slave status" should work, on newer ones
@@ -203,8 +248,10 @@ int main(int argc, char **argv) {
203 unsigned long major_version = server_verion_int / 10000; 248 unsigned long major_version = server_verion_int / 10000;
204 unsigned long minor_version = (server_verion_int % 10000) / 100; 249 unsigned long minor_version = (server_verion_int % 10000) / 100;
205 unsigned long patch_version = (server_verion_int % 100); 250 unsigned long patch_version = (server_verion_int % 100);
251
206 if (verbose) { 252 if (verbose) {
207 printf("Found MariaDB: %s, main version: %lu, minor version: %lu, patch version: %lu\n", 253 printf("Found MariaDB/MySQL: %s, main version: %lu, minor version: %lu, patch version: "
254 "%lu\n",
208 server_version, major_version, minor_version, patch_version); 255 server_version, major_version, minor_version, patch_version);
209 } 256 }
210 257
@@ -235,43 +282,60 @@ int main(int argc, char **argv) {
235 replica_query = "show replica status"; 282 replica_query = "show replica status";
236 } 283 }
237 284
285 mp_subcheck sc_replica = mp_subcheck_init();
286
238 /* check the replica status */ 287 /* check the replica status */
239 if (mysql_query(&mysql, replica_query) != 0) { 288 if (mysql_query(&mysql, replica_query) != 0) {
240 error = strdup(mysql_error(&mysql)); 289 xasprintf(&sc_replica.output, "replica query error: %s", mysql_error(&mysql));
241 mysql_close(&mysql); 290 mysql_close(&mysql);
242 die(STATE_CRITICAL, _("replica query error: %s\n"), error); 291
292 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
293 mp_add_subcheck_to_check(&overall, sc_replica);
294 mp_exit(overall);
243 } 295 }
244 296
245 /* store the result */ 297 /* store the result */
246 if ((res = mysql_store_result(&mysql)) == NULL) { 298 if ((res = mysql_store_result(&mysql)) == NULL) {
247 error = strdup(mysql_error(&mysql)); 299 xasprintf(&sc_replica.output, "replica store_result error: %s", mysql_error(&mysql));
248 mysql_close(&mysql); 300 mysql_close(&mysql);
249 die(STATE_CRITICAL, _("replica store_result error: %s\n"), error); 301
302 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
303 mp_add_subcheck_to_check(&overall, sc_replica);
304 mp_exit(overall);
250 } 305 }
251 306
252 /* Check there is some data */ 307 /* Check there is some data */
253 if (mysql_num_rows(res) == 0) { 308 if (mysql_num_rows(res) == 0) {
254 mysql_close(&mysql); 309 mysql_close(&mysql);
255 die(STATE_WARNING, "%s\n", _("No replicas defined")); 310
311 xasprintf(&sc_replica.output, "no replicas defined");
312 sc_replica = mp_set_subcheck_state(sc_replica, STATE_WARNING);
313 mp_add_subcheck_to_check(&overall, sc_replica);
314 mp_exit(overall);
256 } 315 }
257 316
258 /* fetch the first row */ 317 /* fetch the first row */
259 if ((row = mysql_fetch_row(res)) == NULL) { 318 if ((row = mysql_fetch_row(res)) == NULL) {
260 error = strdup(mysql_error(&mysql)); 319 xasprintf(&sc_replica.output, "replica fetch row error: %s", mysql_error(&mysql));
261 mysql_free_result(res); 320 mysql_free_result(res);
262 mysql_close(&mysql); 321 mysql_close(&mysql);
263 die(STATE_CRITICAL, _("replica fetch row error: %s\n"), error); 322
323 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
324 mp_add_subcheck_to_check(&overall, sc_replica);
325 mp_exit(overall);
264 } 326 }
265 327
266 if (mysql_field_count(&mysql) == 12) { 328 if (mysql_field_count(&mysql) == 12) {
267 /* mysql 3.23.x */ 329 /* mysql 3.23.x */
268 snprintf(replica_result, REPLICA_RESULTSIZE, _("Replica running: %s"), row[6]); 330 xasprintf(&sc_replica.output, "Replica running: %s", row[6]);
269 if (strcmp(row[6], "Yes") != 0) { 331 if (strcmp(row[6], "Yes") != 0) {
270 mysql_free_result(res); 332 mysql_free_result(res);
271 mysql_close(&mysql); 333 mysql_close(&mysql);
272 die(STATE_CRITICAL, "%s\n", replica_result);
273 }
274 334
335 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
336 mp_add_subcheck_to_check(&overall, sc_replica);
337 mp_exit(overall);
338 }
275 } else { 339 } else {
276 /* mysql 4.x.x and mysql 5.x.x */ 340 /* mysql 4.x.x and mysql 5.x.x */
277 int replica_io_field = -1; 341 int replica_io_field = -1;
@@ -315,14 +379,18 @@ int main(int argc, char **argv) {
315 if ((replica_io_field < 0) || (replica_sql_field < 0) || (num_fields == 0)) { 379 if ((replica_io_field < 0) || (replica_sql_field < 0) || (num_fields == 0)) {
316 mysql_free_result(res); 380 mysql_free_result(res);
317 mysql_close(&mysql); 381 mysql_close(&mysql);
318 die(STATE_CRITICAL, "Replica status unavailable\n"); 382
383 xasprintf(&sc_replica.output, "Replica status unavailable");
384 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
385 mp_add_subcheck_to_check(&overall, sc_replica);
386 mp_exit(overall);
319 } 387 }
320 388
321 /* Save replica status in replica_result */ 389 /* Save replica status in replica_result */
322 snprintf(replica_result, REPLICA_RESULTSIZE, 390 xasprintf(&sc_replica.output,
323 "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s", 391 "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s",
324 row[replica_io_field], row[replica_sql_field], 392 row[replica_io_field], row[replica_sql_field],
325 seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown"); 393 seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown");
326 394
327 /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no 395 /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no
328 * mysqldump threads running */ 396 * mysqldump threads running */
@@ -345,10 +413,14 @@ int main(int argc, char **argv) {
345 } 413 }
346 mysql_close(&mysql); 414 mysql_close(&mysql);
347 } 415 }
416
348 if (mysqldump_threads == 0) { 417 if (mysqldump_threads == 0) {
349 die(STATE_CRITICAL, "%s\n", replica_result); 418 sc_replica = mp_set_subcheck_state(sc_replica, STATE_CRITICAL);
419 mp_add_subcheck_to_check(&overall, sc_replica);
420 mp_exit(overall);
350 } else { 421 } else {
351 strncat(replica_result, " Mysqldump: in progress", REPLICA_RESULTSIZE - 1); 422 xasprintf(&sc_replica.output, "%s %s", sc_replica.output,
423 " Mysqldump: in progress");
352 } 424 }
353 } 425 }
354 426
@@ -364,22 +436,22 @@ int main(int argc, char **argv) {
364 /* Check Seconds Behind against threshold */ 436 /* Check Seconds Behind against threshold */
365 if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL && 437 if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL &&
366 strcmp(row[seconds_behind_field], "NULL") != 0)) { 438 strcmp(row[seconds_behind_field], "NULL") != 0)) {
367 double value = atof(row[seconds_behind_field]); 439 mp_perfdata pd_seconds_behind = perfdata_init();
368 int status; 440 pd_seconds_behind.label = "seconds behind master";
369 441 pd_seconds_behind.value = mp_create_pd_value(atof(row[seconds_behind_field]));
370 status = get_status(value, config.my_threshold); 442 pd_seconds_behind =
371 443 mp_pd_set_thresholds(pd_seconds_behind, config.replica_thresholds);
372 xasprintf(&perf, "%s %s", perf, 444 pd_seconds_behind.uom = "s";
373 fperfdata("seconds behind master", value, "s", true, 445 mp_add_perfdata_to_subcheck(&sc_replica, pd_seconds_behind);
374 (double)config.warning_time, true, (double)config.critical_time, 446
375 false, 0, false, 0)); 447 mp_state_enum status = mp_get_pd_status(pd_seconds_behind);
376 448
377 if (status == STATE_WARNING) { 449 sc_replica = mp_set_subcheck_state(sc_replica, status);
378 printf("SLOW_REPLICA %s: %s|%s\n", _("WARNING"), replica_result, perf); 450
379 exit(STATE_WARNING); 451 if (status != STATE_OK) {
380 } else if (status == STATE_CRITICAL) { 452 xasprintf(&sc_replica.output, "slow replica - %s", sc_replica.output);
381 printf("SLOW_REPLICA %s: %s|%s\n", _("CRITICAL"), replica_result, perf); 453 mp_add_subcheck_to_check(&overall, sc_replica);
382 exit(STATE_CRITICAL); 454 mp_exit(overall);
383 } 455 }
384 } 456 }
385 } 457 }
@@ -391,20 +463,16 @@ int main(int argc, char **argv) {
391 /* close the connection */ 463 /* close the connection */
392 mysql_close(&mysql); 464 mysql_close(&mysql);
393 465
394 /* print out the result of stats */ 466 mp_exit(overall);
395 if (config.check_replica) {
396 printf("%s %s|%s\n", result, replica_result, perf);
397 } else {
398 printf("%s|%s\n", result, perf);
399 }
400
401 return STATE_OK;
402} 467}
403 468
404#define CHECK_REPLICA_OPT CHAR_MAX + 1
405
406/* process command-line arguments */ 469/* process command-line arguments */
407check_mysql_config_wrapper process_arguments(int argc, char **argv) { 470check_mysql_config_wrapper process_arguments(int argc, char **argv) {
471
472 enum {
473 CHECK_REPLICA_OPT = CHAR_MAX + 1,
474 };
475
408 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 476 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'},
409 {"socket", required_argument, 0, 's'}, 477 {"socket", required_argument, 0, 's'},
410 {"database", required_argument, 0, 'd'}, 478 {"database", required_argument, 0, 'd'},
@@ -439,9 +507,6 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) {
439 return result; 507 return result;
440 } 508 }
441 509
442 char *warning = NULL;
443 char *critical = NULL;
444
445 int option = 0; 510 int option = 0;
446 while (true) { 511 while (true) {
447 int option_index = 512 int option_index =
@@ -513,14 +578,22 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) {
513 case 'n': 578 case 'n':
514 result.config.ignore_auth = true; /* ignore-auth */ 579 result.config.ignore_auth = true; /* ignore-auth */
515 break; 580 break;
516 case 'w': 581 case 'w': {
517 warning = optarg; 582 mp_range_parsed tmp = mp_parse_range_string(optarg);
518 result.config.warning_time = strtod(warning, NULL); 583 if (tmp.error != MP_PARSING_SUCCES) {
519 break; 584 die(STATE_UNKNOWN, "failed to parse warning time threshold");
520 case 'c': 585 }
521 critical = optarg; 586 result.config.replica_thresholds =
522 result.config.critical_time = strtod(critical, NULL); 587 mp_thresholds_set_warn(result.config.replica_thresholds, tmp.range);
523 break; 588 } break;
589 case 'c': {
590 mp_range_parsed tmp = mp_parse_range_string(optarg);
591 if (tmp.error != MP_PARSING_SUCCES) {
592 die(STATE_UNKNOWN, "failed to parse critical time threshold");
593 }
594 result.config.replica_thresholds =
595 mp_thresholds_set_crit(result.config.replica_thresholds, tmp.range);
596 } break;
524 case 'V': /* version */ 597 case 'V': /* version */
525 print_revision(progname, NP_VERSION); 598 print_revision(progname, NP_VERSION);
526 exit(STATE_UNKNOWN); 599 exit(STATE_UNKNOWN);
@@ -537,8 +610,6 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) {
537 610
538 int index = optind; 611 int index = optind;
539 612
540 set_thresholds(&result.config.my_threshold, warning, critical);
541
542 while (argc > index) { 613 while (argc > index) {
543 if (result.config.db_host == NULL) { 614 if (result.config.db_host == NULL) {
544 if (is_host(argv[index])) { 615 if (is_host(argv[index])) {
diff --git a/plugins/check_mysql.d/config.h b/plugins/check_mysql.d/config.h
index 71ddbe8d..ef086cfc 100644
--- a/plugins/check_mysql.d/config.h
+++ b/plugins/check_mysql.d/config.h
@@ -24,9 +24,7 @@ typedef struct {
24 bool check_replica; 24 bool check_replica;
25 bool ignore_auth; 25 bool ignore_auth;
26 26
27 double warning_time; 27 mp_thresholds replica_thresholds;
28 double critical_time;
29 thresholds *my_threshold;
30 28
31} check_mysql_config; 29} check_mysql_config;
32 30
@@ -50,9 +48,7 @@ check_mysql_config check_mysql_config_init() {
50 .check_replica = false, 48 .check_replica = false,
51 .ignore_auth = false, 49 .ignore_auth = false,
52 50
53 .warning_time = 0, 51 .replica_thresholds = mp_thresholds_init(),
54 .critical_time = 0,
55 .my_threshold = NULL,
56 }; 52 };
57 return tmp; 53 return tmp;
58} 54}
diff --git a/plugins/check_mysql_query.c b/plugins/check_mysql_query.c
index c7e84deb..8af378d5 100644
--- a/plugins/check_mysql_query.c
+++ b/plugins/check_mysql_query.c
@@ -29,11 +29,11 @@
29 * 29 *
30 *****************************************************************************/ 30 *****************************************************************************/
31 31
32const char *progname = "check_mysql_query";
33const char *copyright = "1999-2024";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "common.h" 32#include "common.h"
33#include "output.h"
34#include "perfdata.h"
35#include "states.h"
36#include "thresholds.h"
37#include "utils.h" 37#include "utils.h"
38#include "utils_base.h" 38#include "utils_base.h"
39#include "netutils.h" 39#include "netutils.h"
@@ -42,6 +42,10 @@ const char *email = "devel@monitoring-plugins.org";
42#include <mysql.h> 42#include <mysql.h>
43#include <errmsg.h> 43#include <errmsg.h>
44 44
45const char *progname = "check_mysql_query";
46const char *copyright = "1999-2024";
47const char *email = "devel@monitoring-plugins.org";
48
45typedef struct { 49typedef struct {
46 int errorcode; 50 int errorcode;
47 check_mysql_query_config config; 51 check_mysql_query_config config;
@@ -83,27 +87,38 @@ int main(int argc, char **argv) {
83 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client"); 87 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client");
84 } 88 }
85 89
90 mp_check overall = mp_check_init();
91 mp_subcheck sc_connect = mp_subcheck_init();
92
86 /* establish a connection to the server and error checking */ 93 /* establish a connection to the server and error checking */
87 if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, 94 if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db,
88 config.db_port, config.db_socket, 0)) { 95 config.db_port, config.db_socket, 0)) {
96 xasprintf(&sc_connect.output, "query failed: %s", mysql_error(&mysql));
97
89 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) { 98 if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) {
90 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 99 sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING);
91 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) { 100 } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) {
92 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 101 sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING);
93 } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) { 102 } else if (mysql_errno(&mysql) == CR_OUT_OF_MEMORY) {
94 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 103 sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING);
95 } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) { 104 } else if (mysql_errno(&mysql) == CR_IPSOCK_ERROR) {
96 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 105 sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING);
97 } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) { 106 } else if (mysql_errno(&mysql) == CR_SOCKET_CREATE_ERROR) {
98 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); 107 sc_connect = mp_set_subcheck_state(sc_connect, STATE_WARNING);
99 } else { 108 } else {
100 die(STATE_CRITICAL, "QUERY %s: %s\n", _("CRITICAL"), mysql_error(&mysql)); 109 sc_connect = mp_set_subcheck_state(sc_connect, STATE_CRITICAL);
101 } 110 }
111
112 mp_add_subcheck_to_check(&overall, sc_connect);
113 mp_exit(overall);
102 } 114 }
103 115
104 char *error = NULL; 116 sc_connect = mp_set_subcheck_state(sc_connect, STATE_OK);
117 xasprintf(&sc_connect.output, "query succeeded");
118 mp_add_subcheck_to_check(&overall, sc_connect);
119
105 if (mysql_query(&mysql, config.sql_query) != 0) { 120 if (mysql_query(&mysql, config.sql_query) != 0) {
106 error = strdup(mysql_error(&mysql)); 121 char *error = strdup(mysql_error(&mysql));
107 mysql_close(&mysql); 122 mysql_close(&mysql);
108 die(STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error); 123 die(STATE_CRITICAL, "QUERY %s: %s - %s\n", _("CRITICAL"), _("Error with query"), error);
109 } 124 }
@@ -111,7 +126,7 @@ int main(int argc, char **argv) {
111 MYSQL_RES *res; 126 MYSQL_RES *res;
112 /* store the result */ 127 /* store the result */
113 if ((res = mysql_store_result(&mysql)) == NULL) { 128 if ((res = mysql_store_result(&mysql)) == NULL) {
114 error = strdup(mysql_error(&mysql)); 129 char *error = strdup(mysql_error(&mysql));
115 mysql_close(&mysql); 130 mysql_close(&mysql);
116 die(STATE_CRITICAL, "QUERY %s: Error with store_result - %s\n", _("CRITICAL"), error); 131 die(STATE_CRITICAL, "QUERY %s: Error with store_result - %s\n", _("CRITICAL"), error);
117 } 132 }
@@ -122,17 +137,24 @@ int main(int argc, char **argv) {
122 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), _("No rows returned")); 137 die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), _("No rows returned"));
123 } 138 }
124 139
140 mp_subcheck sc_value = mp_subcheck_init();
125 MYSQL_ROW row; 141 MYSQL_ROW row;
126 /* fetch the first row */ 142 /* fetch the first row */
127 if ((row = mysql_fetch_row(res)) == NULL) { 143 if ((row = mysql_fetch_row(res)) == NULL) {
128 error = strdup(mysql_error(&mysql)); 144 xasprintf(&sc_value.output, "fetch row error - %s", mysql_error(&mysql));
129 mysql_free_result(res); 145 mysql_free_result(res);
130 mysql_close(&mysql); 146 mysql_close(&mysql);
131 die(STATE_CRITICAL, "QUERY %s: Fetch row error - %s\n", _("CRITICAL"), error); 147
148 sc_value = mp_set_subcheck_state(sc_value, STATE_CRITICAL);
149 mp_add_subcheck_to_check(&overall, sc_value);
150 mp_exit(overall);
132 } 151 }
133 152
134 if (!is_numeric(row[0])) { 153 if (!is_numeric(row[0])) {
135 die(STATE_CRITICAL, "QUERY %s: %s - '%s'\n", _("CRITICAL"), _("Is not a numeric"), row[0]); 154 xasprintf(&sc_value.output, "query result is not numeric");
155 sc_value = mp_set_subcheck_state(sc_value, STATE_CRITICAL);
156 mp_add_subcheck_to_check(&overall, sc_value);
157 mp_exit(overall);
136 } 158 }
137 159
138 double value = strtod(row[0], NULL); 160 double value = strtod(row[0], NULL);
@@ -147,24 +169,18 @@ int main(int argc, char **argv) {
147 printf("mysql result: %f\n", value); 169 printf("mysql result: %f\n", value);
148 } 170 }
149 171
150 int status = get_status(value, config.my_thresholds); 172 mp_perfdata pd_query_result = perfdata_init();
173 pd_query_result = mp_set_pd_value(pd_query_result, value);
174 pd_query_result = mp_pd_set_thresholds(pd_query_result, config.thresholds);
175 pd_query_result.label = "result";
176 mp_add_perfdata_to_subcheck(&sc_value, pd_query_result);
151 177
152 if (status == STATE_OK) { 178 sc_value = mp_set_subcheck_state(sc_value, mp_get_pd_status(pd_query_result));
153 printf("QUERY %s: ", _("OK")); 179 xasprintf(&sc_value.output, "'%s' returned '%f'", config.sql_query, value);
154 } else if (status == STATE_WARNING) { 180
155 printf("QUERY %s: ", _("WARNING")); 181 mp_add_subcheck_to_check(&overall, sc_value);
156 } else if (status == STATE_CRITICAL) {
157 printf("QUERY %s: ", _("CRITICAL"));
158 }
159 printf(_("'%s' returned %f | %s"), config.sql_query, value,
160 fperfdata("result", value, "", config.my_thresholds->warning,
161 config.my_thresholds->warning ? config.my_thresholds->warning->end : 0,
162 config.my_thresholds->critical,
163 config.my_thresholds->critical ? config.my_thresholds->critical->end : 0,
164 false, 0, false, 0));
165 printf("\n");
166 182
167 return status; 183 mp_exit(overall);
168} 184}
169 185
170/* process command-line arguments */ 186/* process command-line arguments */
@@ -195,9 +211,6 @@ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) {
195 return result; 211 return result;
196 } 212 }
197 213
198 char *warning = NULL;
199 char *critical = NULL;
200
201 while (true) { 214 while (true) {
202 int option = 0; 215 int option = 0;
203 int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option); 216 int option_char = getopt_long(argc, argv, "hvVP:p:u:d:H:s:q:w:c:f:g:", longopts, &option);
@@ -253,19 +266,25 @@ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) {
253 case 'q': 266 case 'q':
254 xasprintf(&result.config.sql_query, "%s", optarg); 267 xasprintf(&result.config.sql_query, "%s", optarg);
255 break; 268 break;
256 case 'w': 269 case 'w': {
257 warning = optarg; 270 mp_range_parsed tmp = mp_parse_range_string(optarg);
258 break; 271 if (tmp.error != MP_PARSING_SUCCES) {
259 case 'c': 272 die(STATE_UNKNOWN, "failed to parse warning threshold");
260 critical = optarg; 273 }
261 break; 274 result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range);
275 } break;
276 case 'c': {
277 mp_range_parsed tmp = mp_parse_range_string(optarg);
278 if (tmp.error != MP_PARSING_SUCCES) {
279 die(STATE_UNKNOWN, "failed to parse critical threshold");
280 }
281 result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range);
282 } break;
262 case '?': /* help */ 283 case '?': /* help */
263 usage5(); 284 usage5();
264 } 285 }
265 } 286 }
266 287
267 set_thresholds(&result.config.my_thresholds, warning, critical);
268
269 return validate_arguments(result); 288 return validate_arguments(result);
270} 289}
271 290
diff --git a/plugins/check_mysql_query.d/config.h b/plugins/check_mysql_query.d/config.h
index be019160..1c9952e5 100644
--- a/plugins/check_mysql_query.d/config.h
+++ b/plugins/check_mysql_query.d/config.h
@@ -15,7 +15,7 @@ typedef struct {
15 unsigned int db_port; 15 unsigned int db_port;
16 16
17 char *sql_query; 17 char *sql_query;
18 thresholds *my_thresholds; 18 mp_thresholds thresholds;
19} check_mysql_query_config; 19} check_mysql_query_config;
20 20
21check_mysql_query_config check_mysql_query_config_init() { 21check_mysql_query_config check_mysql_query_config_init() {
@@ -30,7 +30,7 @@ check_mysql_query_config check_mysql_query_config_init() {
30 .db_port = MYSQL_PORT, 30 .db_port = MYSQL_PORT,
31 31
32 .sql_query = NULL, 32 .sql_query = NULL,
33 .my_thresholds = NULL, 33 .thresholds = mp_thresholds_init(),
34 }; 34 };
35 return tmp; 35 return tmp;
36} 36}
diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c
index 24d1c9b5..f7cad630 100644
--- a/plugins/check_ntp_peer.c
+++ b/plugins/check_ntp_peer.c
@@ -35,11 +35,14 @@
35 * 35 *
36 *****************************************************************************/ 36 *****************************************************************************/
37 37
38#include "thresholds.h"
39const char *progname = "check_ntp_peer"; 38const char *progname = "check_ntp_peer";
40const char *copyright = "2006-2024"; 39const char *copyright = "2006-2024";
41const char *email = "devel@monitoring-plugins.org"; 40const char *email = "devel@monitoring-plugins.org";
42 41
42#include "output.h"
43#include "perfdata.h"
44#include <openssl/x509.h>
45#include "thresholds.h"
43#include "common.h" 46#include "common.h"
44#include "netutils.h" 47#include "netutils.h"
45#include "utils.h" 48#include "utils.h"
@@ -47,8 +50,6 @@ const char *email = "devel@monitoring-plugins.org";
47#include "check_ntp_peer.d/config.h" 50#include "check_ntp_peer.d/config.h"
48 51
49static int verbose = 0; 52static int verbose = 0;
50static bool syncsource_found = false;
51static bool li_alarm = false;
52 53
53typedef struct { 54typedef struct {
54 int errorcode; 55 int errorcode;
@@ -198,9 +199,7 @@ void setup_control_request(ntp_control_message *message, uint8_t opcode, uint16_
198 * positive value means a success retrieving the value. 199 * positive value means a success retrieving the value.
199 * - status is set to WARNING if there's no sync.peer (otherwise OK) and is 200 * - status is set to WARNING if there's no sync.peer (otherwise OK) and is
200 * the return value of the function. 201 * the return value of the function.
201 * status is pretty much useless as syncsource_found is a global variable 202 */
202 * used later in main to check is the server was synchronized. It works
203 * so I left it alone */
204typedef struct { 203typedef struct {
205 mp_state_enum state; 204 mp_state_enum state;
206 mp_state_enum offset_result; 205 mp_state_enum offset_result;
@@ -208,6 +207,8 @@ typedef struct {
208 double jitter; 207 double jitter;
209 long stratum; 208 long stratum;
210 int num_truechimers; 209 int num_truechimers;
210 bool syncsource_found;
211 bool li_alarm;
211} ntp_request_result; 212} ntp_request_result;
212ntp_request_result ntp_request(const check_ntp_peer_config config) { 213ntp_request_result ntp_request(const check_ntp_peer_config config) {
213 214
@@ -217,6 +218,8 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
217 .jitter = -1, 218 .jitter = -1,
218 .stratum = -1, 219 .stratum = -1,
219 .num_truechimers = 0, 220 .num_truechimers = 0,
221 .syncsource_found = false,
222 .li_alarm = false,
220 }; 223 };
221 224
222 /* Long-winded explanation: 225 /* Long-winded explanation:
@@ -235,19 +238,16 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
235 * 4) Extract the offset, jitter and stratum value from the data[] 238 * 4) Extract the offset, jitter and stratum value from the data[]
236 * (it's ASCII) 239 * (it's ASCII)
237 */ 240 */
238 int min_peer_sel = PEER_INCLUDED;
239 int num_candidates = 0;
240 void *tmp;
241 ntp_assoc_status_pair *peers = NULL;
242 int peer_offset = 0;
243 size_t peers_size = 0;
244 size_t npeers = 0;
245 int conn = -1; 241 int conn = -1;
246 my_udp_connect(config.server_address, config.port, &conn); 242 my_udp_connect(config.server_address, config.port, &conn);
247 243
248 /* keep sending requests until the server stops setting the 244 /* keep sending requests until the server stops setting the
249 * REM_MORE bit, though usually this is only 1 packet. */ 245 * REM_MORE bit, though usually this is only 1 packet. */
250 ntp_control_message req; 246 ntp_control_message req;
247 ntp_assoc_status_pair *peers = NULL;
248 int peer_offset = 0;
249 size_t peers_size = 0;
250 size_t npeers = 0;
251 do { 251 do {
252 setup_control_request(&req, OP_READSTAT, 1); 252 setup_control_request(&req, OP_READSTAT, 1);
253 DBG(printf("sending READSTAT request")); 253 DBG(printf("sending READSTAT request"));
@@ -269,12 +269,13 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
269 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1)); 269 } while (!(req.op & OP_READSTAT && ntohs(req.seq) == 1));
270 270
271 if (LI(req.flags) == LI_ALARM) { 271 if (LI(req.flags) == LI_ALARM) {
272 li_alarm = true; 272 result.li_alarm = true;
273 } 273 }
274 /* Each peer identifier is 4 bytes in the data section, which 274 /* Each peer identifier is 4 bytes in the data section, which
275 * we represent as a ntp_assoc_status_pair datatype. 275 * we represent as a ntp_assoc_status_pair datatype.
276 */ 276 */
277 peers_size += ntohs(req.count); 277 peers_size += ntohs(req.count);
278 void *tmp;
278 if ((tmp = realloc(peers, peers_size)) == NULL) { 279 if ((tmp = realloc(peers, peers_size)) == NULL) {
279 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); 280 free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
280 } 281 }
@@ -287,13 +288,15 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
287 /* first, let's find out if we have a sync source, or if there are 288 /* first, let's find out if we have a sync source, or if there are
288 * at least some candidates. In the latter case we'll issue 289 * at least some candidates. In the latter case we'll issue
289 * a warning but go ahead with the check on them. */ 290 * a warning but go ahead with the check on them. */
291 int min_peer_sel = PEER_INCLUDED;
292 int num_candidates = 0;
290 for (size_t i = 0; i < npeers; i++) { 293 for (size_t i = 0; i < npeers; i++) {
291 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) { 294 if (PEER_SEL(peers[i].status) >= PEER_TRUECHIMER) {
292 result.num_truechimers++; 295 result.num_truechimers++;
293 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { 296 if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) {
294 num_candidates++; 297 num_candidates++;
295 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { 298 if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) {
296 syncsource_found = true; 299 result.syncsource_found = true;
297 min_peer_sel = PEER_SYNCSOURCE; 300 min_peer_sel = PEER_SYNCSOURCE;
298 } 301 }
299 } 302 }
@@ -302,18 +305,18 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
302 305
303 if (verbose) { 306 if (verbose) {
304 printf("%d candidate peers available\n", num_candidates); 307 printf("%d candidate peers available\n", num_candidates);
305 } 308 if (result.syncsource_found) {
306 if (verbose && syncsource_found) { 309 printf("synchronization source found\n");
307 printf("synchronization source found\n"); 310 }
308 } 311 }
309 312
310 if (!syncsource_found) { 313 if (!result.syncsource_found) {
311 result.state = STATE_WARNING; 314 result.state = STATE_WARNING;
312 if (verbose) { 315 if (verbose) {
313 printf("warning: no synchronization source found\n"); 316 printf("warning: no synchronization source found\n");
314 } 317 }
315 } 318 }
316 if (li_alarm) { 319 if (result.li_alarm) {
317 result.state = STATE_WARNING; 320 result.state = STATE_WARNING;
318 if (verbose) { 321 if (verbose) {
319 printf("warning: LI_ALARM bit is set\n"); 322 printf("warning: LI_ALARM bit is set\n");
@@ -329,7 +332,7 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
329 if (verbose) { 332 if (verbose) {
330 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc)); 333 printf("Getting offset, jitter and stratum for peer %.2x\n", ntohs(peers[i].assoc));
331 } 334 }
332 xasprintf(&data, ""); 335 data = strdup("");
333 do { 336 do {
334 setup_control_request(&req, OP_READVAR, 2); 337 setup_control_request(&req, OP_READVAR, 2);
335 req.assoc = peers[i].assoc; 338 req.assoc = peers[i].assoc;
@@ -475,16 +478,30 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) {
475} 478}
476 479
477check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { 480check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
478 static struct option longopts[] = { 481
479 {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, 482 enum {
480 {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'}, 483 output_format_index = CHAR_MAX + 1,
481 {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, 484 };
482 {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, 485
483 {"swarn", required_argument, 0, 'W'}, {"scrit", required_argument, 0, 'C'}, 486 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
484 {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'}, 487 {"help", no_argument, 0, 'h'},
485 {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, 488 {"verbose", no_argument, 0, 'v'},
486 {"timeout", required_argument, 0, 't'}, {"hostname", required_argument, 0, 'H'}, 489 {"use-ipv4", no_argument, 0, '4'},
487 {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}}; 490 {"use-ipv6", no_argument, 0, '6'},
491 {"quiet", no_argument, 0, 'q'},
492 {"warning", required_argument, 0, 'w'},
493 {"critical", required_argument, 0, 'c'},
494 {"swarn", required_argument, 0, 'W'},
495 {"scrit", required_argument, 0, 'C'},
496 {"jwarn", required_argument, 0, 'j'},
497 {"jcrit", required_argument, 0, 'k'},
498 {"twarn", required_argument, 0, 'm'},
499 {"tcrit", required_argument, 0, 'n'},
500 {"timeout", required_argument, 0, 't'},
501 {"hostname", required_argument, 0, 'H'},
502 {"port", required_argument, 0, 'p'},
503 {"output-format", required_argument, 0, output_format_index},
504 {0, 0, 0, 0}};
488 505
489 if (argc < 2) { 506 if (argc < 2) {
490 usage("\n"); 507 usage("\n");
@@ -504,6 +521,17 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
504 } 521 }
505 522
506 switch (option_char) { 523 switch (option_char) {
524 case output_format_index: {
525 parsed_output_format parser = mp_parse_output_format(optarg);
526 if (!parser.parsing_success) {
527 printf("Invalid output format: %s\n", optarg);
528 exit(STATE_UNKNOWN);
529 }
530
531 result.config.output_format_is_set = true;
532 result.config.output_format = parser.output_format;
533 break;
534 }
507 case 'h': 535 case 'h':
508 print_help(); 536 print_help();
509 exit(STATE_UNKNOWN); 537 exit(STATE_UNKNOWN);
@@ -518,36 +546,84 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
518 case 'q': 546 case 'q':
519 result.config.quiet = true; 547 result.config.quiet = true;
520 break; 548 break;
521 case 'w': 549 case 'w': {
522 result.config.owarn = optarg; 550 mp_range_parsed tmp = mp_parse_range_string(optarg);
523 break; 551 if (tmp.error != MP_PARSING_SUCCES) {
524 case 'c': 552 die(STATE_UNKNOWN, "failed to parse warning offset threshold");
525 result.config.ocrit = optarg; 553 }
526 break; 554
527 case 'W': 555 result.config.offset_thresholds =
556 mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range);
557 } break;
558 case 'c': {
559 mp_range_parsed tmp = mp_parse_range_string(optarg);
560 if (tmp.error != MP_PARSING_SUCCES) {
561 die(STATE_UNKNOWN, "failed to parse critical offset threshold");
562 }
563
564 result.config.offset_thresholds =
565 mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range);
566 } break;
567 case 'W': {
528 result.config.do_stratum = true; 568 result.config.do_stratum = true;
529 result.config.swarn = optarg; 569 mp_range_parsed tmp = mp_parse_range_string(optarg);
530 break; 570 if (tmp.error != MP_PARSING_SUCCES) {
531 case 'C': 571 die(STATE_UNKNOWN, "failed to parse warning stratum threshold");
572 }
573
574 result.config.stratum_thresholds =
575 mp_thresholds_set_warn(result.config.stratum_thresholds, tmp.range);
576 } break;
577 case 'C': {
532 result.config.do_stratum = true; 578 result.config.do_stratum = true;
533 result.config.scrit = optarg; 579 mp_range_parsed tmp = mp_parse_range_string(optarg);
534 break; 580 if (tmp.error != MP_PARSING_SUCCES) {
535 case 'j': 581 die(STATE_UNKNOWN, "failed to parse critical stratum threshold");
582 }
583
584 result.config.stratum_thresholds =
585 mp_thresholds_set_crit(result.config.stratum_thresholds, tmp.range);
586 } break;
587 case 'j': {
536 result.config.do_jitter = true; 588 result.config.do_jitter = true;
537 result.config.jwarn = optarg; 589 mp_range_parsed tmp = mp_parse_range_string(optarg);
538 break; 590 if (tmp.error != MP_PARSING_SUCCES) {
539 case 'k': 591 die(STATE_UNKNOWN, "failed to parse warning jitter threshold");
592 }
593
594 result.config.jitter_thresholds =
595 mp_thresholds_set_warn(result.config.jitter_thresholds, tmp.range);
596 } break;
597 case 'k': {
540 result.config.do_jitter = true; 598 result.config.do_jitter = true;
541 result.config.jcrit = optarg; 599 mp_range_parsed tmp = mp_parse_range_string(optarg);
542 break; 600 if (tmp.error != MP_PARSING_SUCCES) {
543 case 'm': 601 die(STATE_UNKNOWN, "failed to parse critical jitter threshold");
602 }
603
604 result.config.jitter_thresholds =
605 mp_thresholds_set_crit(result.config.jitter_thresholds, tmp.range);
606 } break;
607 case 'm': {
544 result.config.do_truechimers = true; 608 result.config.do_truechimers = true;
545 result.config.twarn = optarg; 609 mp_range_parsed tmp = mp_parse_range_string(optarg);
546 break; 610 if (tmp.error != MP_PARSING_SUCCES) {
547 case 'n': 611 die(STATE_UNKNOWN, "failed to parse warning truechimer threshold");
612 }
613
614 result.config.truechimer_thresholds =
615 mp_thresholds_set_warn(result.config.truechimer_thresholds, tmp.range);
616 } break;
617 case 'n': {
548 result.config.do_truechimers = true; 618 result.config.do_truechimers = true;
549 result.config.tcrit = optarg; 619 mp_range_parsed tmp = mp_parse_range_string(optarg);
550 break; 620 if (tmp.error != MP_PARSING_SUCCES) {
621 die(STATE_UNKNOWN, "failed to parse critical truechimer threshold");
622 }
623
624 result.config.truechimer_thresholds =
625 mp_thresholds_set_crit(result.config.truechimer_thresholds, tmp.range);
626 } break;
551 case 'H': 627 case 'H':
552 if (!is_host(optarg)) { 628 if (!is_host(optarg)) {
553 usage2(_("Invalid hostname/address"), optarg); 629 usage2(_("Invalid hostname/address"), optarg);
@@ -581,11 +657,6 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) {
581 usage4(_("Hostname was not supplied")); 657 usage4(_("Hostname was not supplied"));
582 } 658 }
583 659
584 set_thresholds(&result.config.offset_thresholds, result.config.owarn, result.config.ocrit);
585 set_thresholds(&result.config.jitter_thresholds, result.config.jwarn, result.config.jcrit);
586 set_thresholds(&result.config.stratum_thresholds, result.config.swarn, result.config.scrit);
587 set_thresholds(&result.config.truechimer_thresholds, result.config.twarn, result.config.tcrit);
588
589 return result; 660 return result;
590} 661}
591 662
@@ -627,6 +698,10 @@ int main(int argc, char *argv[]) {
627 698
628 const check_ntp_peer_config config = tmp_config.config; 699 const check_ntp_peer_config config = tmp_config.config;
629 700
701 if (config.output_format_is_set) {
702 mp_set_format(config.output_format);
703 }
704
630 /* initialize alarm signal handling */ 705 /* initialize alarm signal handling */
631 signal(SIGALRM, socket_timeout_alarm_handler); 706 signal(SIGALRM, socket_timeout_alarm_handler);
632 707
@@ -634,125 +709,113 @@ int main(int argc, char *argv[]) {
634 alarm(socket_timeout); 709 alarm(socket_timeout);
635 710
636 /* This returns either OK or WARNING (See comment preceding ntp_request) */ 711 /* This returns either OK or WARNING (See comment preceding ntp_request) */
637 ntp_request_result ntp_res = ntp_request(config); 712 const ntp_request_result ntp_res = ntp_request(config);
638 mp_state_enum result = STATE_UNKNOWN; 713 mp_check overall = mp_check_init();
639 714
715 mp_subcheck sc_offset = mp_subcheck_init();
716 xasprintf(&sc_offset.output, "offset");
640 if (ntp_res.offset_result == STATE_UNKNOWN) { 717 if (ntp_res.offset_result == STATE_UNKNOWN) {
641 /* if there's no sync peer (this overrides ntp_request output): */ 718 /* if there's no sync peer (this overrides ntp_request output): */
642 result = (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL); 719 sc_offset =
720 mp_set_subcheck_state(sc_offset, (config.quiet ? STATE_UNKNOWN : STATE_CRITICAL));
721 xasprintf(&sc_offset.output, "%s unknown", sc_offset.output);
643 } else { 722 } else {
644 /* Be quiet if there's no candidates either */ 723 /* Be quiet if there's no candidates either */
645 if (config.quiet && result == STATE_WARNING) { 724 mp_state_enum tmp = STATE_OK;
646 result = STATE_UNKNOWN; 725 if (config.quiet && ntp_res.state == STATE_WARNING) {
726 tmp = STATE_UNKNOWN;
647 } 727 }
648 result = max_state_alt(result, get_status(fabs(ntp_res.offset), config.offset_thresholds)); 728
729 xasprintf(&sc_offset.output, "%s: %.6fs", sc_offset.output, ntp_res.offset);
730
731 mp_perfdata pd_offset = perfdata_init();
732 pd_offset.value = mp_create_pd_value(fabs(ntp_res.offset));
733 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
734 pd_offset.label = "offset";
735 pd_offset.uom = "s";
736 mp_add_perfdata_to_subcheck(&sc_offset, pd_offset);
737
738 tmp = max_state_alt(tmp, mp_get_pd_status(pd_offset));
739 sc_offset = mp_set_subcheck_state(sc_offset, tmp);
649 } 740 }
650 741
651 mp_state_enum oresult = result; 742 mp_add_subcheck_to_check(&overall, sc_offset);
652 mp_state_enum tresult = STATE_UNKNOWN;
653 743
744 // truechimers
654 if (config.do_truechimers) { 745 if (config.do_truechimers) {
655 tresult = get_status(ntp_res.num_truechimers, config.truechimer_thresholds); 746 mp_subcheck sc_truechimers = mp_subcheck_init();
656 result = max_state_alt(result, tresult); 747 xasprintf(&sc_truechimers.output, "truechimers: %i", ntp_res.num_truechimers);
657 }
658 748
659 mp_state_enum sresult = STATE_UNKNOWN; 749 mp_perfdata pd_truechimers = perfdata_init();
750 pd_truechimers.value = mp_create_pd_value(ntp_res.num_truechimers);
751 pd_truechimers.label = "truechimers";
752 pd_truechimers = mp_pd_set_thresholds(pd_truechimers, config.truechimer_thresholds);
660 753
661 if (config.do_stratum) { 754 mp_add_perfdata_to_subcheck(&sc_truechimers, pd_truechimers);
662 sresult = get_status((double)ntp_res.stratum, config.stratum_thresholds);
663 result = max_state_alt(result, sresult);
664 }
665 755
666 mp_state_enum jresult = STATE_UNKNOWN; 756 sc_truechimers = mp_set_subcheck_state(sc_truechimers, mp_get_pd_status(pd_truechimers));
667 757
668 if (config.do_jitter) { 758 mp_add_subcheck_to_check(&overall, sc_truechimers);
669 jresult = get_status(ntp_res.jitter, config.jitter_thresholds);
670 result = max_state_alt(result, jresult);
671 } 759 }
672 760
673 char *result_line; 761 if (config.do_stratum) {
674 switch (result) { 762 mp_subcheck sc_stratum = mp_subcheck_init();
675 case STATE_CRITICAL: 763 xasprintf(&sc_stratum.output, "stratum: %li", ntp_res.stratum);
676 xasprintf(&result_line, _("NTP CRITICAL:"));
677 break;
678 case STATE_WARNING:
679 xasprintf(&result_line, _("NTP WARNING:"));
680 break;
681 case STATE_OK:
682 xasprintf(&result_line, _("NTP OK:"));
683 break;
684 default:
685 xasprintf(&result_line, _("NTP UNKNOWN:"));
686 break;
687 }
688 764
689 if (!syncsource_found) { 765 mp_perfdata pd_stratum = perfdata_init();
690 xasprintf(&result_line, "%s %s,", result_line, _("Server not synchronized")); 766 pd_stratum.value = mp_create_pd_value(ntp_res.stratum);
691 } else if (li_alarm) { 767 pd_stratum = mp_pd_set_thresholds(pd_stratum, config.stratum_thresholds);
692 xasprintf(&result_line, "%s %s,", result_line, _("Server has the LI_ALARM bit set")); 768 pd_stratum.label = "stratum";
693 }
694 769
695 char *perfdata_line; 770 mp_add_perfdata_to_subcheck(&sc_stratum, pd_stratum);
696 if (ntp_res.offset_result == STATE_UNKNOWN) { 771
697 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 772 sc_stratum = mp_set_subcheck_state(sc_stratum, mp_get_pd_status(pd_stratum));
698 xasprintf(&perfdata_line, ""); 773
699 } else if (oresult == STATE_WARNING) { 774 mp_add_subcheck_to_check(&overall, sc_stratum);
700 xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"),
701 ntp_res.offset);
702 } else if (oresult == STATE_CRITICAL) {
703 xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"),
704 ntp_res.offset);
705 } else {
706 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), ntp_res.offset);
707 } 775 }
708 xasprintf(&perfdata_line, "%s", perfd_offset(ntp_res.offset, config.offset_thresholds));
709 776
710 if (config.do_jitter) { 777 if (config.do_jitter) {
711 if (jresult == STATE_WARNING) { 778 mp_subcheck sc_jitter = mp_subcheck_init();
712 xasprintf(&result_line, "%s, jitter=%f (WARNING)", result_line, ntp_res.jitter); 779 xasprintf(&sc_jitter.output, "jitter: %f", ntp_res.jitter);
713 } else if (jresult == STATE_CRITICAL) {
714 xasprintf(&result_line, "%s, jitter=%f (CRITICAL)", result_line, ntp_res.jitter);
715 } else {
716 xasprintf(&result_line, "%s, jitter=%f", result_line, ntp_res.jitter);
717 }
718 xasprintf(&perfdata_line, "%s %s", perfdata_line,
719 perfd_jitter(ntp_res.jitter, config.do_jitter, config.jitter_thresholds));
720 }
721 780
722 if (config.do_stratum) { 781 mp_perfdata pd_jitter = perfdata_init();
723 if (sresult == STATE_WARNING) { 782 pd_jitter.value = mp_create_pd_value(ntp_res.jitter);
724 xasprintf(&result_line, "%s, stratum=%li (WARNING)", result_line, ntp_res.stratum); 783 pd_jitter = mp_pd_set_thresholds(pd_jitter, config.jitter_thresholds);
725 } else if (sresult == STATE_CRITICAL) { 784 pd_jitter.label = "jitter";
726 xasprintf(&result_line, "%s, stratum=%li (CRITICAL)", result_line, ntp_res.stratum); 785
727 } else { 786 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter);
728 xasprintf(&result_line, "%s, stratum=%li", result_line, ntp_res.stratum); 787
729 } 788 sc_jitter = mp_set_subcheck_state(sc_jitter, mp_get_pd_status(pd_jitter));
730 xasprintf(&perfdata_line, "%s %s", perfdata_line, 789 mp_add_subcheck_to_check(&overall, sc_jitter);
731 perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds));
732 } 790 }
733 791
734 if (config.do_truechimers) { 792 mp_subcheck sc_other_info = mp_subcheck_init();
735 if (tresult == STATE_WARNING) { 793 sc_other_info = mp_set_subcheck_default_state(sc_other_info, STATE_OK);
736 xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, 794 if (!ntp_res.syncsource_found) {
737 ntp_res.num_truechimers); 795 xasprintf(&sc_other_info.output, "%s", _("Server not synchronized"));
738 } else if (tresult == STATE_CRITICAL) { 796 mp_add_subcheck_to_check(&overall, sc_other_info);
739 xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, 797 } else if (ntp_res.li_alarm) {
740 ntp_res.num_truechimers); 798 xasprintf(&sc_other_info.output, "%s", _("Server has the LI_ALARM bit set"));
741 } else { 799 mp_add_subcheck_to_check(&overall, sc_other_info);
742 xasprintf(&result_line, "%s, truechimers=%i", result_line, ntp_res.num_truechimers);
743 }
744 xasprintf(&perfdata_line, "%s %s", perfdata_line,
745 perfd_truechimers(ntp_res.num_truechimers, config.do_truechimers,
746 config.truechimer_thresholds));
747 } 800 }
748 801
749 printf("%s|%s\n", result_line, perfdata_line); 802 {
803 mp_subcheck sc_offset = mp_subcheck_init();
804 sc_offset = mp_set_subcheck_default_state(sc_offset, STATE_OK);
805 xasprintf(&sc_offset.output, "offset: %.10gs", ntp_res.offset);
806
807 mp_perfdata pd_offset = perfdata_init();
808 pd_offset.value = mp_create_pd_value(ntp_res.offset);
809 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
810
811 sc_offset = mp_set_subcheck_state(sc_offset, ntp_res.offset_result);
812 }
750 813
751 if (config.server_address != NULL) { 814 if (config.server_address != NULL) {
752 free(config.server_address); 815 free(config.server_address);
753 } 816 }
754 817
755 exit(result); 818 mp_exit(overall);
756} 819}
757 820
758void print_help(void) { 821void print_help(void) {
@@ -791,6 +854,7 @@ void print_help(void) {
791 printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")")); 854 printf(" %s\n", _("Critical threshold for number of usable time sources (\"truechimers\")"));
792 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 855 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
793 printf(UT_VERBOSE); 856 printf(UT_VERBOSE);
857 printf(UT_OUTPUT_FORMAT);
794 858
795 printf("\n"); 859 printf("\n");
796 printf("%s\n", _("This plugin checks an NTP server independent of any commandline")); 860 printf("%s\n", _("This plugin checks an NTP server independent of any commandline"));
diff --git a/plugins/check_ntp_peer.d/config.h b/plugins/check_ntp_peer.d/config.h
index 00e6b05d..488d936c 100644
--- a/plugins/check_ntp_peer.d/config.h
+++ b/plugins/check_ntp_peer.d/config.h
@@ -1,6 +1,8 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
5#include "perfdata.h"
4#include "thresholds.h" 6#include "thresholds.h"
5#include <stddef.h> 7#include <stddef.h>
6 8
@@ -16,26 +18,21 @@ typedef struct {
16 18
17 // truechimer stuff 19 // truechimer stuff
18 bool do_truechimers; 20 bool do_truechimers;
19 char *twarn; 21 mp_thresholds truechimer_thresholds;
20 char *tcrit;
21 thresholds *truechimer_thresholds;
22 22
23 char *owarn; 23 // offset thresholds
24 char *ocrit; 24 mp_thresholds offset_thresholds;
25 thresholds *offset_thresholds;
26 25
27 // stratum stuff 26 // stratum stuff
28 bool do_stratum; 27 bool do_stratum;
29 char *swarn; 28 mp_thresholds stratum_thresholds;
30 char *scrit;
31 thresholds *stratum_thresholds;
32 29
33 // jitter stuff 30 // jitter stuff
34 bool do_jitter; 31 bool do_jitter;
35 char *jwarn; 32 mp_thresholds jitter_thresholds;
36 char *jcrit;
37 thresholds *jitter_thresholds;
38 33
34 bool output_format_is_set;
35 mp_output_format output_format;
39} check_ntp_peer_config; 36} check_ntp_peer_config;
40 37
41check_ntp_peer_config check_ntp_peer_config_init() { 38check_ntp_peer_config check_ntp_peer_config_init() {
@@ -45,23 +42,41 @@ check_ntp_peer_config check_ntp_peer_config_init() {
45 42
46 .quiet = false, 43 .quiet = false,
47 .do_truechimers = false, 44 .do_truechimers = false,
48 .twarn = "0:", 45 .truechimer_thresholds = mp_thresholds_init(),
49 .tcrit = "0:",
50 .truechimer_thresholds = NULL,
51 46
52 .owarn = "60", 47 .offset_thresholds = mp_thresholds_init(),
53 .ocrit = "120",
54 .offset_thresholds = NULL,
55 48
56 .do_stratum = false, 49 .do_stratum = false,
57 .swarn = "-1:16", 50 .stratum_thresholds = mp_thresholds_init(),
58 .scrit = "-1:16",
59 .stratum_thresholds = NULL,
60 51
61 .do_jitter = false, 52 .do_jitter = false,
62 .jwarn = "-1:5000", 53 .jitter_thresholds = mp_thresholds_init(),
63 .jcrit = "-1:10000", 54
64 .jitter_thresholds = NULL, 55 .output_format_is_set = false,
65 }; 56 };
57
58 mp_range stratum_default = mp_range_init();
59 stratum_default = mp_range_set_start(stratum_default, mp_create_pd_value(-1));
60 stratum_default = mp_range_set_end(stratum_default, mp_create_pd_value(16));
61 tmp.stratum_thresholds = mp_thresholds_set_warn(tmp.stratum_thresholds, stratum_default);
62 tmp.stratum_thresholds = mp_thresholds_set_crit(tmp.stratum_thresholds, stratum_default);
63
64 mp_range jitter_w_default = mp_range_init();
65 jitter_w_default = mp_range_set_start(jitter_w_default, mp_create_pd_value(-1));
66 jitter_w_default = mp_range_set_end(jitter_w_default, mp_create_pd_value(5000));
67 tmp.jitter_thresholds = mp_thresholds_set_warn(tmp.jitter_thresholds, jitter_w_default);
68
69 mp_range jitter_c_default = mp_range_init();
70 jitter_c_default = mp_range_set_start(jitter_c_default, mp_create_pd_value(-1));
71 jitter_c_default = mp_range_set_end(jitter_c_default, mp_create_pd_value(10000));
72 tmp.jitter_thresholds = mp_thresholds_set_crit(tmp.jitter_thresholds, jitter_c_default);
73
74 mp_range offset_w_default = mp_range_init();
75 offset_w_default = mp_range_set_end(offset_w_default, mp_create_pd_value(60));
76 tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, offset_w_default);
77
78 mp_range offset_c_default = mp_range_init();
79 offset_c_default = mp_range_set_end(offset_c_default, mp_create_pd_value(120));
80 tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, offset_c_default);
66 return tmp; 81 return tmp;
67} 82}
diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c
index ad69b804..602b6010 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -34,12 +34,10 @@
34 * 34 *
35 *****************************************************************************/ 35 *****************************************************************************/
36 36
37const char *progname = "check_ntp_time"; 37#include "output.h"
38const char *copyright = "2006-2024";
39const char *email = "devel@monitoring-plugins.org";
40
41#include "common.h" 38#include "common.h"
42#include "netutils.h" 39#include "netutils.h"
40#include "perfdata.h"
43#include "utils.h" 41#include "utils.h"
44#include "states.h" 42#include "states.h"
45#include "thresholds.h" 43#include "thresholds.h"
@@ -47,6 +45,10 @@ const char *email = "devel@monitoring-plugins.org";
47 45
48static int verbose = 0; 46static int verbose = 0;
49 47
48const char *progname = "check_ntp_time";
49const char *copyright = "2006-2024";
50const char *email = "devel@monitoring-plugins.org";
51
50typedef struct { 52typedef struct {
51 int errorcode; 53 int errorcode;
52 check_ntp_time_config config; 54 check_ntp_time_config config;
@@ -61,9 +63,6 @@ void print_usage(void);
61# define AVG_NUM 4 63# define AVG_NUM 4
62#endif 64#endif
63 65
64/* max size of control message data */
65#define MAX_CM_SIZE 468
66
67/* this structure holds everything in an ntp request/response as per rfc1305 */ 66/* this structure holds everything in an ntp request/response as per rfc1305 */
68typedef struct { 67typedef struct {
69 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ 68 uint8_t flags; /* byte with leapindicator,vers,mode. see macros */
@@ -169,7 +168,9 @@ typedef struct {
169 : 0) 168 : 0)
170 169
171/* convert a struct timeval to a double */ 170/* convert a struct timeval to a double */
172#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec)) 171static double TVasDOUBLE(struct timeval time) {
172 return ((double)time.tv_sec + (0.000001 * (double)time.tv_usec));
173}
173 174
174/* convert an ntp 64-bit fp number to a struct timeval */ 175/* convert an ntp 64-bit fp number to a struct timeval */
175#define NTP64toTV(n, t) \ 176#define NTP64toTV(n, t) \
@@ -262,8 +263,8 @@ void setup_request(ntp_message *message) {
262/* select the "best" server from a list of servers, and return its index. 263/* select the "best" server from a list of servers, and return its index.
263 * this is done by filtering servers based on stratum, dispersion, and 264 * this is done by filtering servers based on stratum, dispersion, and
264 * finally round-trip delay. */ 265 * finally round-trip delay. */
265int best_offset_server(const ntp_server_results *slist, int nservers) { 266static int best_offset_server(const ntp_server_results *slist, int nservers) {
266 int best_server = -1; 267 int best_server_index = -1;
267 268
268 /* for each server */ 269 /* for each server */
269 for (int cserver = 0; cserver < nservers; cserver++) { 270 for (int cserver = 0; cserver < nservers; cserver++) {
@@ -286,33 +287,33 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
286 } 287 }
287 288
288 /* If we don't have a server yet, use the first one */ 289 /* If we don't have a server yet, use the first one */
289 if (best_server == -1) { 290 if (best_server_index == -1) {
290 best_server = cserver; 291 best_server_index = cserver;
291 DBG(printf("using peer %d as our first candidate\n", best_server)); 292 DBG(printf("using peer %d as our first candidate\n", best_server_index));
292 continue; 293 continue;
293 } 294 }
294 295
295 /* compare the server to the best one we've seen so far */ 296 /* compare the server to the best one we've seen so far */
296 /* does it have an equal or better stratum? */ 297 /* does it have an equal or better stratum? */
297 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server)); 298 DBG(printf("comparing peer %d with peer %d\n", cserver, best_server_index));
298 if (slist[cserver].stratum <= slist[best_server].stratum) { 299 if (slist[cserver].stratum <= slist[best_server_index].stratum) {
299 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server)); 300 DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server_index));
300 /* does it have an equal or better dispersion? */ 301 /* does it have an equal or better dispersion? */
301 if (slist[cserver].rtdisp <= slist[best_server].rtdisp) { 302 if (slist[cserver].rtdisp <= slist[best_server_index].rtdisp) {
302 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server)); 303 DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server_index));
303 /* does it have a better rtdelay? */ 304 /* does it have a better rtdelay? */
304 if (slist[cserver].rtdelay < slist[best_server].rtdelay) { 305 if (slist[cserver].rtdelay < slist[best_server_index].rtdelay) {
305 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server)); 306 DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server_index));
306 best_server = cserver; 307 best_server_index = cserver;
307 DBG(printf("peer %d is now our best candidate\n", best_server)); 308 DBG(printf("peer %d is now our best candidate\n", best_server_index));
308 } 309 }
309 } 310 }
310 } 311 }
311 } 312 }
312 313
313 if (best_server >= 0) { 314 if (best_server_index >= 0) {
314 DBG(printf("best server selected: peer %d\n", best_server)); 315 DBG(printf("best server selected: peer %d\n", best_server_index));
315 return best_server; 316 return best_server_index;
316 } 317 }
317 DBG(printf("no peers meeting synchronization criteria :(\n")); 318 DBG(printf("no peers meeting synchronization criteria :(\n"));
318 return -1; 319 return -1;
@@ -323,7 +324,11 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
323 * we don't waste time sitting around waiting for single packets. 324 * we don't waste time sitting around waiting for single packets.
324 * - we also "manually" handle resolving host names and connecting, because 325 * - we also "manually" handle resolving host names and connecting, because
325 * we have to do it in a way that our lazy macros don't handle currently :( */ 326 * we have to do it in a way that our lazy macros don't handle currently :( */
326double offset_request(const char *host, const char *port, mp_state_enum *status, int time_offset) { 327typedef struct {
328 mp_state_enum offset_result;
329 double offset;
330} offset_request_wrapper;
331static offset_request_wrapper offset_request(const char *host, const char *port, int time_offset) {
327 /* setup hints to only return results from getaddrinfo that we'd like */ 332 /* setup hints to only return results from getaddrinfo that we'd like */
328 struct addrinfo hints; 333 struct addrinfo hints;
329 memset(&hints, 0, sizeof(struct addrinfo)); 334 memset(&hints, 0, sizeof(struct addrinfo));
@@ -462,12 +467,18 @@ double offset_request(const char *host, const char *port, mp_state_enum *status,
462 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n"); 467 die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
463 } 468 }
464 469
470 offset_request_wrapper result = {
471 .offset = 0,
472 .offset_result = STATE_UNKNOWN,
473 };
474
465 /* now, pick the best server from the list */ 475 /* now, pick the best server from the list */
466 double avg_offset = 0.; 476 double avg_offset = 0.;
467 int best_index = best_offset_server(servers, num_hosts); 477 int best_index = best_offset_server(servers, num_hosts);
468 if (best_index < 0) { 478 if (best_index < 0) {
469 *status = STATE_UNKNOWN; 479 result.offset_result = STATE_UNKNOWN;
470 } else { 480 } else {
481 result.offset_result = STATE_OK;
471 /* finally, calculate the average offset */ 482 /* finally, calculate the average offset */
472 for (int i = 0; i < servers[best_index].num_responses; i++) { 483 for (int i = 0; i < servers[best_index].num_responses; i++) {
473 avg_offset += servers[best_index].offset[i]; 484 avg_offset += servers[best_index].offset[i];
@@ -488,22 +499,30 @@ double offset_request(const char *host, const char *port, mp_state_enum *status,
488 if (verbose) { 499 if (verbose) {
489 printf("overall average offset: %.10g\n", avg_offset); 500 printf("overall average offset: %.10g\n", avg_offset);
490 } 501 }
491 return avg_offset; 502
503 result.offset = avg_offset;
504 return result;
492} 505}
493 506
494check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { 507static check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
508
509 enum {
510 output_format_index = CHAR_MAX + 1,
511 };
512
495 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 513 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
496 {"help", no_argument, 0, 'h'}, 514 {"help", no_argument, 0, 'h'},
497 {"verbose", no_argument, 0, 'v'}, 515 {"verbose", no_argument, 0, 'v'},
498 {"use-ipv4", no_argument, 0, '4'}, 516 {"use-ipv4", no_argument, 0, '4'},
499 {"use-ipv6", no_argument, 0, '6'}, 517 {"use-ipv6", no_argument, 0, '6'},
500 {"quiet", no_argument, 0, 'q'}, 518 {"quiet", no_argument, 0, 'q'},
501 {"time-offset", optional_argument, 0, 'o'}, 519 {"time-offset", required_argument, 0, 'o'},
502 {"warning", required_argument, 0, 'w'}, 520 {"warning", required_argument, 0, 'w'},
503 {"critical", required_argument, 0, 'c'}, 521 {"critical", required_argument, 0, 'c'},
504 {"timeout", required_argument, 0, 't'}, 522 {"timeout", required_argument, 0, 't'},
505 {"hostname", required_argument, 0, 'H'}, 523 {"hostname", required_argument, 0, 'H'},
506 {"port", required_argument, 0, 'p'}, 524 {"port", required_argument, 0, 'p'},
525 {"output-format", required_argument, 0, output_format_index},
507 {0, 0, 0, 0}}; 526 {0, 0, 0, 0}};
508 527
509 if (argc < 2) { 528 if (argc < 2) {
@@ -515,9 +534,6 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
515 .config = check_ntp_time_config_init(), 534 .config = check_ntp_time_config_init(),
516 }; 535 };
517 536
518 char *owarn = "60";
519 char *ocrit = "120";
520
521 while (true) { 537 while (true) {
522 int option = 0; 538 int option = 0;
523 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option); 539 int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option);
@@ -526,6 +542,17 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
526 } 542 }
527 543
528 switch (option_char) { 544 switch (option_char) {
545 case output_format_index: {
546 parsed_output_format parser = mp_parse_output_format(optarg);
547 if (!parser.parsing_success) {
548 printf("Invalid output format: %s\n", optarg);
549 exit(STATE_UNKNOWN);
550 }
551
552 result.config.output_format_is_set = true;
553 result.config.output_format = parser.output_format;
554 break;
555 }
529 case 'h': 556 case 'h':
530 print_help(); 557 print_help();
531 exit(STATE_UNKNOWN); 558 exit(STATE_UNKNOWN);
@@ -540,12 +567,24 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
540 case 'q': 567 case 'q':
541 result.config.quiet = true; 568 result.config.quiet = true;
542 break; 569 break;
543 case 'w': 570 case 'w': {
544 owarn = optarg; 571 mp_range_parsed tmp = mp_parse_range_string(optarg);
545 break; 572 if (tmp.error != MP_PARSING_SUCCES) {
546 case 'c': 573 die(STATE_UNKNOWN, "failed to parse warning threshold");
547 ocrit = optarg; 574 }
548 break; 575
576 result.config.offset_thresholds =
577 mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range);
578 } break;
579 case 'c': {
580 mp_range_parsed tmp = mp_parse_range_string(optarg);
581 if (tmp.error != MP_PARSING_SUCCES) {
582 die(STATE_UNKNOWN, "failed to parse crit threshold");
583 }
584
585 result.config.offset_thresholds =
586 mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range);
587 } break;
549 case 'H': 588 case 'H':
550 if (!is_host(optarg)) { 589 if (!is_host(optarg)) {
551 usage2(_("Invalid hostname/address"), optarg); 590 usage2(_("Invalid hostname/address"), optarg);
@@ -582,16 +621,9 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
582 usage4(_("Hostname was not supplied")); 621 usage4(_("Hostname was not supplied"));
583 } 622 }
584 623
585 set_thresholds(&result.config.offset_thresholds, owarn, ocrit);
586
587 return result; 624 return result;
588} 625}
589 626
590char *perfd_offset(double offset, thresholds *offset_thresholds) {
591 return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true,
592 offset_thresholds->critical->end, false, 0, false, 0);
593}
594
595int main(int argc, char *argv[]) { 627int main(int argc, char *argv[]) {
596 setlocale(LC_ALL, ""); 628 setlocale(LC_ALL, "");
597 bindtextdomain(PACKAGE, LOCALEDIR); 629 bindtextdomain(PACKAGE, LOCALEDIR);
@@ -608,52 +640,47 @@ int main(int argc, char *argv[]) {
608 640
609 const check_ntp_time_config config = tmp_config.config; 641 const check_ntp_time_config config = tmp_config.config;
610 642
643 if (config.output_format_is_set) {
644 mp_set_format(config.output_format);
645 }
646
611 /* initialize alarm signal handling */ 647 /* initialize alarm signal handling */
612 signal(SIGALRM, socket_timeout_alarm_handler); 648 signal(SIGALRM, socket_timeout_alarm_handler);
613 649
614 /* set socket timeout */ 650 /* set socket timeout */
615 alarm(socket_timeout); 651 alarm(socket_timeout);
616 652
617 mp_state_enum offset_result = STATE_OK; 653 mp_check overall = mp_check_init();
618 mp_state_enum result = STATE_OK;
619 double offset =
620 offset_request(config.server_address, config.port, &offset_result, config.time_offset);
621 if (offset_result == STATE_UNKNOWN) {
622 result = ((!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
623 } else {
624 result = get_status(fabs(offset), config.offset_thresholds);
625 }
626 654
627 char *result_line; 655 mp_subcheck sc_offset = mp_subcheck_init();
628 switch (result) { 656 offset_request_wrapper offset_result =
629 case STATE_CRITICAL: 657 offset_request(config.server_address, config.port, config.time_offset);
630 xasprintf(&result_line, _("NTP CRITICAL:"));
631 break;
632 case STATE_WARNING:
633 xasprintf(&result_line, _("NTP WARNING:"));
634 break;
635 case STATE_OK:
636 xasprintf(&result_line, _("NTP OK:"));
637 break;
638 default:
639 xasprintf(&result_line, _("NTP UNKNOWN:"));
640 break;
641 }
642 658
643 char *perfdata_line; 659 if (offset_result.offset_result == STATE_UNKNOWN) {
644 if (offset_result == STATE_UNKNOWN) { 660 sc_offset =
645 xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); 661 mp_set_subcheck_state(sc_offset, (!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
646 xasprintf(&perfdata_line, ""); 662 xasprintf(&sc_offset.output, "Offset unknown");
647 } else { 663 mp_add_subcheck_to_check(&overall, sc_offset);
648 xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset); 664 mp_exit(overall);
649 xasprintf(&perfdata_line, "%s", perfd_offset(offset, config.offset_thresholds));
650 } 665 }
651 printf("%s|%s\n", result_line, perfdata_line); 666
667 xasprintf(&sc_offset.output, "Offset: %.6fs", offset_result.offset);
668
669 mp_perfdata pd_offset = perfdata_init();
670 pd_offset = mp_set_pd_value(pd_offset, fabs(offset_result.offset));
671 pd_offset.label = "offset";
672 pd_offset.uom = "s";
673 pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
674
675 sc_offset = mp_set_subcheck_state(sc_offset, mp_get_pd_status(pd_offset));
676
677 mp_add_perfdata_to_subcheck(&sc_offset, pd_offset);
678 mp_add_subcheck_to_check(&overall, sc_offset);
652 679
653 if (config.server_address != NULL) { 680 if (config.server_address != NULL) {
654 free(config.server_address); 681 free(config.server_address);
655 } 682 }
656 exit(result); 683 mp_exit(overall);
657} 684}
658 685
659void print_help(void) { 686void print_help(void) {
@@ -677,10 +704,11 @@ void print_help(void) {
677 printf(" %s\n", _("Offset to result in warning status (seconds)")); 704 printf(" %s\n", _("Offset to result in warning status (seconds)"));
678 printf(" %s\n", "-c, --critical=THRESHOLD"); 705 printf(" %s\n", "-c, --critical=THRESHOLD");
679 printf(" %s\n", _("Offset to result in critical status (seconds)")); 706 printf(" %s\n", _("Offset to result in critical status (seconds)"));
680 printf(" %s\n", "-o, --time_offset=INTEGER"); 707 printf(" %s\n", "-o, --time-offset=INTEGER");
681 printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)")); 708 printf(" %s\n", _("Expected offset of the ntp server relative to local server (seconds)"));
682 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 709 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
683 printf(UT_VERBOSE); 710 printf(UT_VERBOSE);
711 printf(UT_OUTPUT_FORMAT);
684 712
685 printf("\n"); 713 printf("\n");
686 printf("%s\n", _("This plugin checks the clock offset between the local host and a")); 714 printf("%s\n", _("This plugin checks the clock offset between the local host and a"));
diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h
index 99dabbbd..9bbd82aa 100644
--- a/plugins/check_ntp_time.d/config.h
+++ b/plugins/check_ntp_time.d/config.h
@@ -1,6 +1,7 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
4#include "thresholds.h" 5#include "thresholds.h"
5#include <stddef.h> 6#include <stddef.h>
6 7
@@ -11,7 +12,10 @@ typedef struct {
11 bool quiet; 12 bool quiet;
12 int time_offset; 13 int time_offset;
13 14
14 thresholds *offset_thresholds; 15 mp_thresholds offset_thresholds;
16
17 bool output_format_is_set;
18 mp_output_format output_format;
15} check_ntp_time_config; 19} check_ntp_time_config;
16 20
17check_ntp_time_config check_ntp_time_config_init() { 21check_ntp_time_config check_ntp_time_config_init() {
@@ -22,7 +26,18 @@ check_ntp_time_config check_ntp_time_config_init() {
22 .quiet = false, 26 .quiet = false,
23 .time_offset = 0, 27 .time_offset = 0,
24 28
25 .offset_thresholds = NULL, 29 .offset_thresholds = mp_thresholds_init(),
30
31 .output_format_is_set = false,
26 }; 32 };
33
34 mp_range warning = mp_range_init();
35 warning = mp_range_set_end(warning, mp_create_pd_value(60));
36 tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, warning);
37
38 mp_range critical = mp_range_init();
39 critical = mp_range_set_end(warning, mp_create_pd_value(120));
40 tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, critical);
41
27 return tmp; 42 return tmp;
28} 43}
diff --git a/plugins/check_pgsql.c b/plugins/check_pgsql.c
index 793a686f..0ce75e0a 100644
--- a/plugins/check_pgsql.c
+++ b/plugins/check_pgsql.c
@@ -28,27 +28,29 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "output.h"
32#include "perfdata.h"
31#include "states.h" 33#include "states.h"
32const char *progname = "check_pgsql";
33const char *copyright = "1999-2024";
34const char *email = "devel@monitoring-plugins.org";
35
36#include "common.h" 34#include "common.h"
37#include "utils.h" 35#include "utils.h"
38#include "utils_cmd.h" 36#include "utils_cmd.h"
39#include "check_pgsql.d/config.h" 37#include "check_pgsql.d/config.h"
40#include "thresholds.h" 38#include "thresholds.h"
41
42#include "netutils.h" 39#include "netutils.h"
43#include <libpq-fe.h> 40#include <libpq-fe.h>
44#include <pg_config_manual.h> 41#include <pg_config_manual.h>
45 42
43const char *progname = "check_pgsql";
44const char *copyright = "1999-2024";
45const char *email = "devel@monitoring-plugins.org";
46
46#define DEFAULT_HOST "127.0.0.1" 47#define DEFAULT_HOST "127.0.0.1"
47 48
48/* return the PSQL server version as a 3-tuple */ 49/* return the PSQL server version as a 3-tuple */
49#define PSQL_SERVER_VERSION3(server_version) \ 50#define PSQL_SERVER_VERSION3(server_version) \
50 (server_version) / 10000, (server_version) / 100 - (int)((server_version) / 10000) * 100, \ 51 ((server_version) / 10000), \
51 (server_version) - (int)((server_version) / 100) * 100 52 (((server_version) / 100) - (int)(((server_version) / 10000) * 100)), \
53 (server_version) - (int)(((server_version) / 100) * 100)
52/* return true if the given host is a UNIX domain socket */ 54/* return true if the given host is a UNIX domain socket */
53#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host))) 55#define PSQL_IS_UNIX_DOMAIN_SOCKET(host) ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host)))
54/* return a 3-tuple identifying a host/port independent of the socket type */ 56/* return a 3-tuple identifying a host/port independent of the socket type */
@@ -64,15 +66,25 @@ static check_pgsql_config_wrapper process_arguments(int /*argc*/, char ** /*argv
64 66
65static void print_help(void); 67static void print_help(void);
66static bool is_pg_logname(char * /*username*/); 68static bool is_pg_logname(char * /*username*/);
67static mp_state_enum do_query(PGconn * /*conn*/, char * /*query*/, const char /*pgqueryname*/[], 69
68 thresholds * /*qthresholds*/, char * /*query_warning*/, 70typedef enum {
69 char * /*query_critical*/); 71 QUERY_OK,
72 ERROR_WITH_QUERY, // critical
73 NO_ROWS_RETURNED, // warning
74 NO_COLUMNS_RETURNED, // warning
75 NO_DATA_RETURNED, // critica/
76 RESULT_IS_NOT_NUMERIC // critical
77} do_query_errorcode;
78
79typedef struct {
80 do_query_errorcode error_code;
81 double numerical_result;
82} do_query_wrapper;
83static do_query_wrapper do_query(PGconn * /*conn*/, char * /*query*/);
70void print_usage(void); 84void print_usage(void);
71 85
72static int verbose = 0; 86static int verbose = 0;
73 87
74#define OPTID_QUERYNAME -1000
75
76/****************************************************************************** 88/******************************************************************************
77 89
78The (pseudo?)literate programming XML is contained within \@\@\- <XML> \-\@\@ 90The (pseudo?)literate programming XML is contained within \@\@\- <XML> \-\@\@
@@ -138,8 +150,8 @@ int main(int argc, char **argv) {
138 150
139 const check_pgsql_config config = tmp_config.config; 151 const check_pgsql_config config = tmp_config.config;
140 152
141 if (verbose > 2) { 153 if (config.output_format_is_set) {
142 printf("Arguments initialized\n"); 154 mp_set_format(config.output_format);
143 } 155 }
144 156
145 /* Set signal handling and alarm */ 157 /* Set signal handling and alarm */
@@ -199,21 +211,41 @@ int main(int argc, char **argv) {
199 if (verbose) { 211 if (verbose) {
200 printf("Verifying connection\n"); 212 printf("Verifying connection\n");
201 } 213 }
214
215 mp_check overall = mp_check_init();
216
217 mp_subcheck sc_connection = mp_subcheck_init();
218
202 if (PQstatus(conn) == CONNECTION_BAD) { 219 if (PQstatus(conn) == CONNECTION_BAD) {
203 printf(_("CRITICAL - no connection to '%s' (%s).\n"), config.dbName, PQerrorMessage(conn)); 220 sc_connection = mp_set_subcheck_state(sc_connection, STATE_CRITICAL);
221 xasprintf(&sc_connection.output, "no connection to '%s' (%s)", config.dbName,
222 PQerrorMessage(conn));
204 PQfinish(conn); 223 PQfinish(conn);
205 return STATE_CRITICAL; 224 mp_add_subcheck_to_check(&overall, sc_connection);
206 } 225 mp_exit(overall);
207
208 mp_state_enum status = STATE_UNKNOWN;
209 if (elapsed_time > config.tcrit) {
210 status = STATE_CRITICAL;
211 } else if (elapsed_time > config.twarn) {
212 status = STATE_WARNING;
213 } else { 226 } else {
214 status = STATE_OK; 227 sc_connection = mp_set_subcheck_state(sc_connection, STATE_OK);
228 xasprintf(&sc_connection.output, "connected to '%s'", config.dbName);
229 mp_add_subcheck_to_check(&overall, sc_connection);
215 } 230 }
216 231
232 mp_subcheck sc_connection_time = mp_subcheck_init();
233 sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_UNKNOWN);
234
235 xasprintf(&sc_connection_time.output, "connection time: %.10g", elapsed_time);
236
237 mp_perfdata pd_connection_time = perfdata_init();
238 pd_connection_time.label = "time";
239 pd_connection_time.uom = "s";
240 pd_connection_time = mp_set_pd_value(pd_connection_time, elapsed_time);
241 pd_connection_time = mp_pd_set_thresholds(pd_connection_time, config.time_thresholds);
242
243 mp_add_perfdata_to_subcheck(&sc_connection_time, pd_connection_time);
244 sc_connection_time =
245 mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_connection_time));
246
247 mp_add_subcheck_to_check(&overall, sc_connection_time);
248
217 if (verbose) { 249 if (verbose) {
218 char *server_host = PQhost(conn); 250 char *server_host = PQhost(conn);
219 int server_version = PQserverVersion(conn); 251 int server_version = PQserverVersion(conn);
@@ -225,25 +257,87 @@ int main(int argc, char **argv) {
225 PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn)); 257 PSQL_SERVER_VERSION3(server_version), PQprotocolVersion(conn), PQbackendPID(conn));
226 } 258 }
227 259
228 printf(_(" %s - database %s (%f sec.)|%s\n"), state_text(status), config.dbName, elapsed_time,
229 fperfdata("time", elapsed_time, "s", (config.twarn > 0.0), config.twarn,
230 (config.tcrit > 0.0), config.tcrit, true, 0, false, 0));
231
232 mp_state_enum query_status = STATE_UNKNOWN;
233 if (config.pgquery) { 260 if (config.pgquery) {
234 query_status = do_query(conn, config.pgquery, config.pgqueryname, config.qthresholds, 261 mp_subcheck sc_query = mp_subcheck_init();
235 config.query_warning, config.query_critical); 262 sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN);
263 if (config.pgqueryname) {
264 xasprintf(&sc_query.output, "query '%s'", config.pgqueryname);
265 } else {
266 xasprintf(&sc_query.output, "query '%s'", config.pgquery);
267 }
268
269 do_query_wrapper query_result = do_query(conn, config.pgquery);
270
271 switch (query_result.error_code) {
272 case QUERY_OK: {
273 // Query was successful and there is a numerical result
274 sc_query = mp_set_subcheck_state(sc_query, STATE_OK);
275 xasprintf(&sc_query.output, "%s succeeded", sc_query.output);
276
277 mp_perfdata pd_query = perfdata_init();
278 pd_query = mp_set_pd_value(pd_query, query_result.numerical_result);
279 pd_query = mp_pd_set_thresholds(pd_query, config.qthresholds);
280 pd_query.label = "query";
281
282 mp_subcheck sc_query_compare = mp_subcheck_init();
283 mp_state_enum query_compare_state = mp_get_pd_status(pd_query);
284
285 sc_query_compare = mp_set_subcheck_state(sc_query_compare, query_compare_state);
286 mp_add_perfdata_to_subcheck(&sc_query_compare, pd_query);
287
288 if (query_compare_state == STATE_OK) {
289 xasprintf(&sc_query_compare.output, "query result '%f' is within thresholds",
290 query_result.numerical_result);
291 } else {
292 xasprintf(&sc_query_compare.output, "query result '%f' is violating thresholds",
293 query_result.numerical_result);
294 }
295 mp_add_subcheck_to_check(&overall, sc_query_compare);
296
297 } break;
298 case ERROR_WITH_QUERY:
299 xasprintf(&sc_query.output, "%s - Error with query: %s", sc_query.output,
300 PQerrorMessage(conn));
301 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
302 break;
303 case NO_ROWS_RETURNED:
304 xasprintf(&sc_query.output, "%s - no rows were returned by the query", sc_query.output);
305 sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING);
306 break;
307 case NO_COLUMNS_RETURNED:
308 xasprintf(&sc_query.output, "%s - no columns were returned by the query",
309 sc_query.output);
310 sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING);
311 break;
312 case NO_DATA_RETURNED:
313 xasprintf(&sc_query.output, "%s - no data was returned by the query", sc_query.output);
314 sc_query = mp_set_subcheck_state(sc_query, STATE_WARNING);
315 break;
316 case RESULT_IS_NOT_NUMERIC:
317 xasprintf(&sc_query.output, "%s - result of the query is not numeric", sc_query.output);
318 sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL);
319 break;
320 };
321
322 mp_add_subcheck_to_check(&overall, sc_query);
236 } 323 }
237 324
238 if (verbose) { 325 if (verbose) {
239 printf("Closing connection\n"); 326 printf("Closing connection\n");
240 } 327 }
241 PQfinish(conn); 328 PQfinish(conn);
242 return (config.pgquery && query_status > status) ? query_status : status; 329
330 mp_exit(overall);
243} 331}
244 332
245/* process command-line arguments */ 333/* process command-line arguments */
246check_pgsql_config_wrapper process_arguments(int argc, char **argv) { 334static check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
335
336 enum {
337 OPTID_QUERYNAME = CHAR_MAX + 1,
338 output_format_index,
339 };
340
247 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, 341 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
248 {"version", no_argument, 0, 'V'}, 342 {"version", no_argument, 0, 'V'},
249 {"timeout", required_argument, 0, 't'}, 343 {"timeout", required_argument, 0, 't'},
@@ -261,6 +355,7 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
261 {"query_critical", required_argument, 0, 'C'}, 355 {"query_critical", required_argument, 0, 'C'},
262 {"query_warning", required_argument, 0, 'W'}, 356 {"query_warning", required_argument, 0, 'W'},
263 {"verbose", no_argument, 0, 'v'}, 357 {"verbose", no_argument, 0, 'v'},
358 {"output-format", required_argument, 0, output_format_index},
264 {0, 0, 0, 0}}; 359 {0, 0, 0, 0}};
265 360
266 check_pgsql_config_wrapper result = { 361 check_pgsql_config_wrapper result = {
@@ -278,6 +373,17 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
278 } 373 }
279 374
280 switch (option_char) { 375 switch (option_char) {
376 case output_format_index: {
377 parsed_output_format parser = mp_parse_output_format(optarg);
378 if (!parser.parsing_success) {
379 printf("Invalid output format: %s\n", optarg);
380 exit(STATE_UNKNOWN);
381 }
382
383 result.config.output_format_is_set = true;
384 result.config.output_format = parser.output_format;
385 break;
386 }
281 case '?': /* usage */ 387 case '?': /* usage */
282 usage5(); 388 usage5();
283 case 'h': /* help */ 389 case 'h': /* help */
@@ -293,26 +399,40 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
293 timeout_interval = atoi(optarg); 399 timeout_interval = atoi(optarg);
294 } 400 }
295 break; 401 break;
296 case 'c': /* critical time threshold */ 402 case 'c': /* critical time threshold */ {
297 if (!is_nonnegative(optarg)) { 403 mp_range_parsed tmp = mp_parse_range_string(optarg);
298 usage2(_("Critical threshold must be a positive integer"), optarg); 404 if (tmp.error != MP_PARSING_SUCCES) {
299 } else { 405 die(STATE_UNKNOWN, "failed to parse critical time threshold");
300 result.config.tcrit = strtod(optarg, NULL);
301 } 406 }
302 break; 407 result.config.time_thresholds =
303 case 'w': /* warning time threshold */ 408 mp_thresholds_set_crit(result.config.time_thresholds, tmp.range);
304 if (!is_nonnegative(optarg)) { 409 } break;
305 usage2(_("Warning threshold must be a positive integer"), optarg); 410 case 'w': /* warning time threshold */ {
306 } else { 411 mp_range_parsed tmp = mp_parse_range_string(optarg);
307 result.config.twarn = strtod(optarg, NULL); 412 if (tmp.error != MP_PARSING_SUCCES) {
413 die(STATE_UNKNOWN, "failed to parse warning time threshold");
308 } 414 }
309 break; 415 result.config.time_thresholds =
310 case 'C': /* critical query threshold */ 416 mp_thresholds_set_warn(result.config.time_thresholds, tmp.range);
311 result.config.query_critical = optarg; 417 } break;
312 break; 418 case 'C': /* critical query threshold */ {
313 case 'W': /* warning query threshold */ 419 mp_range_parsed tmp = mp_parse_range_string(optarg);
314 result.config.query_warning = optarg; 420 if (tmp.error != MP_PARSING_SUCCES) {
315 break; 421 die(STATE_UNKNOWN, "failed to parse critical query threshold");
422 }
423
424 result.config.qthresholds =
425 mp_thresholds_set_crit(result.config.qthresholds, tmp.range);
426
427 } break;
428 case 'W': /* warning query threshold */ {
429 mp_range_parsed tmp = mp_parse_range_string(optarg);
430 if (tmp.error != MP_PARSING_SUCCES) {
431 die(STATE_UNKNOWN, "failed to parse warning query threshold");
432 }
433 result.config.qthresholds =
434 mp_thresholds_set_warn(result.config.qthresholds, tmp.range);
435 } break;
316 case 'H': /* host */ 436 case 'H': /* host */
317 if ((*optarg != '/') && (!is_host(optarg))) { 437 if ((*optarg != '/') && (!is_host(optarg))) {
318 usage2(_("Invalid hostname/address"), optarg); 438 usage2(_("Invalid hostname/address"), optarg);
@@ -363,9 +483,6 @@ check_pgsql_config_wrapper process_arguments(int argc, char **argv) {
363 } 483 }
364 } 484 }
365 485
366 set_thresholds(&result.config.qthresholds, result.config.query_warning,
367 result.config.query_critical);
368
369 return result; 486 return result;
370} 487}
371 488
@@ -393,7 +510,7 @@ should be added.</para>
393-@@ 510-@@
394******************************************************************************/ 511******************************************************************************/
395 512
396bool is_pg_logname(char *username) { 513static bool is_pg_logname(char *username) {
397 if (strlen(username) > NAMEDATALEN - 1) { 514 if (strlen(username) > NAMEDATALEN - 1) {
398 return (false); 515 return (false);
399 } 516 }
@@ -447,12 +564,13 @@ void print_help(void) {
447 printf(" %s\n", "--queryname=STRING"); 564 printf(" %s\n", "--queryname=STRING");
448 printf(" %s\n", _("A name for the query, this string is used instead of the query")); 565 printf(" %s\n", _("A name for the query, this string is used instead of the query"));
449 printf(" %s\n", _("in the long output of the plugin")); 566 printf(" %s\n", _("in the long output of the plugin"));
450 printf(" %s\n", "-W, --query-warning=RANGE"); 567 printf(" %s\n", "-W, --query_warning=RANGE");
451 printf(" %s\n", _("SQL query value to result in warning status (double)")); 568 printf(" %s\n", _("SQL query value to result in warning status (double)"));
452 printf(" %s\n", "-C, --query-critical=RANGE"); 569 printf(" %s\n", "-C, --query_critical=RANGE");
453 printf(" %s\n", _("SQL query value to result in critical status (double)")); 570 printf(" %s\n", _("SQL query value to result in critical status (double)"));
454 571
455 printf(UT_VERBOSE); 572 printf(UT_VERBOSE);
573 printf(UT_OUTPUT_FORMAT);
456 574
457 printf("\n"); 575 printf("\n");
458 printf(" %s\n", _("All parameters are optional.")); 576 printf(" %s\n", _("All parameters are optional."));
@@ -509,33 +627,44 @@ void print_usage(void) {
509 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n"); 627 "[-q <query>] [-C <critical query range>] [-W <warning query range>]\n");
510} 628}
511 629
512mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thresholds *qthresholds, 630static do_query_wrapper do_query(PGconn *conn, char *query) {
513 char *query_warning, char *query_critical) {
514 if (verbose) { 631 if (verbose) {
515 printf("Executing SQL query \"%s\".\n", query); 632 printf("Executing SQL query \"%s\".\n", query);
516 } 633 }
517 PGresult *res = PQexec(conn, query); 634 PGresult *res = PQexec(conn, query);
518 635
636 do_query_wrapper result = {
637 .error_code = QUERY_OK,
638 };
639
519 if (PGRES_TUPLES_OK != PQresultStatus(res)) { 640 if (PGRES_TUPLES_OK != PQresultStatus(res)) {
520 printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"), 641 // TODO
521 PQerrorMessage(conn)); 642 // printf(_("QUERY %s - %s: %s.\n"), _("CRITICAL"), _("Error with query"),
522 return STATE_CRITICAL; 643 // PQerrorMessage(conn));
644 result.error_code = ERROR_WITH_QUERY;
645 return result;
523 } 646 }
524 647
525 if (PQntuples(res) < 1) { 648 if (PQntuples(res) < 1) {
526 printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned")); 649 // TODO
527 return STATE_WARNING; 650 // printf("QUERY %s - %s.\n", _("WARNING"), _("No rows returned"));
651 result.error_code = NO_ROWS_RETURNED;
652 return result;
528 } 653 }
529 654
530 if (PQnfields(res) < 1) { 655 if (PQnfields(res) < 1) {
531 printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned")); 656 // TODO
532 return STATE_WARNING; 657 // printf("QUERY %s - %s.\n", _("WARNING"), _("No columns returned"));
658 result.error_code = NO_COLUMNS_RETURNED;
659 return result;
533 } 660 }
534 661
535 char *val_str = PQgetvalue(res, 0, 0); 662 char *val_str = PQgetvalue(res, 0, 0);
536 if (!val_str) { 663 if (!val_str) {
537 printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned")); 664 // TODO
538 return STATE_CRITICAL; 665 // printf("QUERY %s - %s.\n", _("CRITICAL"), _("No data returned"));
666 result.error_code = NO_DATA_RETURNED;
667 return result;
539 } 668 }
540 669
541 char *endptr = NULL; 670 char *endptr = NULL;
@@ -545,8 +674,10 @@ mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thre
545 } 674 }
546 675
547 if (endptr == val_str) { 676 if (endptr == val_str) {
548 printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str); 677 // TODO
549 return STATE_CRITICAL; 678 // printf("QUERY %s - %s: %s\n", _("CRITICAL"), _("Is not a numeric"), val_str);
679 result.error_code = RESULT_IS_NOT_NUMERIC;
680 return result;
550 } 681 }
551 682
552 if ((endptr != NULL) && (*endptr != '\0')) { 683 if ((endptr != NULL) && (*endptr != '\0')) {
@@ -555,24 +686,7 @@ mp_state_enum do_query(PGconn *conn, char *query, const char pgqueryname[], thre
555 } 686 }
556 } 687 }
557 688
558 mp_state_enum my_status = get_status(value, qthresholds); 689 result.numerical_result = value;
559 printf("QUERY %s - ", (my_status == STATE_OK) ? _("OK")
560 : (my_status == STATE_WARNING) ? _("WARNING")
561 : (my_status == STATE_CRITICAL) ? _("CRITICAL")
562 : _("UNKNOWN"));
563 if (pgqueryname) {
564 printf(_("%s returned %f"), pgqueryname, value);
565 } else {
566 printf(_("'%s' returned %f"), query, value);
567 }
568 690
569 printf("|query=%f;%s;%s;;\n", value, query_warning ? query_warning : "", 691 return result;
570 query_critical ? query_critical : "");
571 if (PQnfields(res) > 1) {
572 char *extra_info = PQgetvalue(res, 0, 1);
573 if (extra_info != NULL) {
574 printf("Extra Info: %s\n", extra_info);
575 }
576 }
577 return my_status;
578} 692}
diff --git a/plugins/check_pgsql.d/config.h b/plugins/check_pgsql.d/config.h
index 2d4b8b89..7cf0637b 100644
--- a/plugins/check_pgsql.d/config.h
+++ b/plugins/check_pgsql.d/config.h
@@ -1,6 +1,8 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
5#include "perfdata.h"
4#include "thresholds.h" 6#include "thresholds.h"
5#include <stddef.h> 7#include <stddef.h>
6#include <pg_config_manual.h> 8#include <pg_config_manual.h>
@@ -24,11 +26,11 @@ typedef struct {
24 char *pgquery; 26 char *pgquery;
25 char *pgqueryname; 27 char *pgqueryname;
26 28
27 double twarn; 29 mp_thresholds time_thresholds;
28 double tcrit; 30 mp_thresholds qthresholds;
29 thresholds *qthresholds; 31
30 char *query_warning; 32 bool output_format_is_set;
31 char *query_critical; 33 mp_output_format output_format;
32} check_pgsql_config; 34} check_pgsql_config;
33 35
34/* begin, by setting the parameters for a backend connection if the 36/* begin, by setting the parameters for a backend connection if the
@@ -51,11 +53,18 @@ check_pgsql_config check_pgsql_config_init() {
51 .pgquery = NULL, 53 .pgquery = NULL,
52 .pgqueryname = NULL, 54 .pgqueryname = NULL,
53 55
54 .twarn = (double)DEFAULT_WARN, 56 .time_thresholds = mp_thresholds_init(),
55 .tcrit = (double)DEFAULT_CRIT, 57 .qthresholds = mp_thresholds_init(),
56 .qthresholds = NULL, 58
57 .query_warning = NULL, 59 .output_format_is_set = false,
58 .query_critical = NULL,
59 }; 60 };
61
62 mp_range tmp_range = mp_range_init();
63 tmp_range = mp_range_set_end(tmp_range, mp_create_pd_value(DEFAULT_WARN));
64 tmp.time_thresholds = mp_thresholds_set_warn(tmp.time_thresholds, tmp_range);
65
66 tmp_range = mp_range_set_end(tmp_range, mp_create_pd_value(DEFAULT_CRIT));
67 tmp.time_thresholds = mp_thresholds_set_crit(tmp.time_thresholds, tmp_range);
68
60 return tmp; 69 return tmp;
61} 70}
diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c
index 83ad575c..e806ad29 100644
--- a/plugins/check_smtp.c
+++ b/plugins/check_smtp.c
@@ -28,20 +28,25 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_smtp";
32const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org";
34
35#include "common.h" 31#include "common.h"
36#include "netutils.h" 32#include "netutils.h"
33#include "output.h"
34#include "perfdata.h"
35#include "thresholds.h"
37#include "utils.h" 36#include "utils.h"
38#include "base64.h" 37#include "base64.h"
39#include "regex.h" 38#include "regex.h"
40 39
40#include <bits/getopt_ext.h>
41#include <ctype.h> 41#include <ctype.h>
42#include <string.h>
42#include "check_smtp.d/config.h" 43#include "check_smtp.d/config.h"
43#include "../lib/states.h" 44#include "../lib/states.h"
44 45
46const char *progname = "check_smtp";
47const char *copyright = "2000-2024";
48const char *email = "devel@monitoring-plugins.org";
49
45#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n" 50#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n"
46#define SMTP_HELO "HELO " 51#define SMTP_HELO "HELO "
47#define SMTP_EHLO "EHLO " 52#define SMTP_EHLO "EHLO "
@@ -111,6 +116,10 @@ int main(int argc, char **argv) {
111 116
112 const check_smtp_config config = tmp_config.config; 117 const check_smtp_config config = tmp_config.config;
113 118
119 if (config.output_format_is_set) {
120 mp_set_format(config.output_format);
121 }
122
114 /* If localhostname not set on command line, use gethostname to set */ 123 /* If localhostname not set on command line, use gethostname to set */
115 char *localhostname = config.localhostname; 124 char *localhostname = config.localhostname;
116 if (!localhostname) { 125 if (!localhostname) {
@@ -161,359 +170,459 @@ int main(int argc, char **argv) {
161 gettimeofday(&start_time, NULL); 170 gettimeofday(&start_time, NULL);
162 171
163 int socket_descriptor = 0; 172 int socket_descriptor = 0;
173
164 /* try to connect to the host at the given port number */ 174 /* try to connect to the host at the given port number */
165 mp_state_enum result = 175 mp_state_enum tcp_result =
166 my_tcp_connect(config.server_address, config.server_port, &socket_descriptor); 176 my_tcp_connect(config.server_address, config.server_port, &socket_descriptor);
167 177
168 char *error_msg = ""; 178 mp_check overall = mp_check_init();
179 mp_subcheck sc_tcp_connect = mp_subcheck_init();
169 char buffer[MAX_INPUT_BUFFER]; 180 char buffer[MAX_INPUT_BUFFER];
170 bool ssl_established = false; 181 bool ssl_established = false;
171 if (result == STATE_OK) { /* we connected */ 182
172 /* If requested, send PROXY header */ 183 if (tcp_result != STATE_OK) {
173 if (config.use_proxy_prefix) { 184 // Connect failed
174 if (verbose) { 185 sc_tcp_connect = mp_set_subcheck_state(sc_tcp_connect, STATE_CRITICAL);
175 printf("Sending header %s\n", PROXY_PREFIX); 186 xasprintf(&sc_tcp_connect.output, "TCP connect to '%s' failed", config.server_address);
176 } 187 mp_add_subcheck_to_check(&overall, sc_tcp_connect);
177 my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established); 188 mp_exit(overall);
189 }
190
191 /* we connected */
192 /* If requested, send PROXY header */
193 if (config.use_proxy_prefix) {
194 if (verbose) {
195 printf("Sending header %s\n", PROXY_PREFIX);
178 } 196 }
197 my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established);
198 }
179 199
180#ifdef HAVE_SSL 200#ifdef HAVE_SSL
181 if (config.use_ssl) { 201 if (config.use_ssl) {
182 result = np_net_ssl_init_with_hostname(socket_descriptor, 202 int tls_result = np_net_ssl_init_with_hostname(
183 (config.use_sni ? config.server_address : NULL)); 203 socket_descriptor, (config.use_sni ? config.server_address : NULL));
184 if (result != STATE_OK) { 204
185 printf(_("CRITICAL - Cannot create SSL context.\n")); 205 mp_subcheck sc_tls_connection = mp_subcheck_init();
186 close(socket_descriptor); 206
187 np_net_ssl_cleanup(); 207 if (tls_result != STATE_OK) {
188 exit(STATE_CRITICAL); 208 close(socket_descriptor);
189 } 209 np_net_ssl_cleanup();
190 ssl_established = true; 210
211 sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_CRITICAL);
212 xasprintf(&sc_tls_connection.output, "cannot create TLS context");
213 mp_add_subcheck_to_check(&overall, sc_tls_connection);
214 mp_exit(overall);
191 } 215 }
216
217 sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_OK);
218 xasprintf(&sc_tls_connection.output, "TLS context established");
219 mp_add_subcheck_to_check(&overall, sc_tls_connection);
220 ssl_established = true;
221 }
192#endif 222#endif
193 223
194 /* watch for the SMTP connection string and */ 224 /* watch for the SMTP connection string and */
195 /* return a WARNING status if we couldn't read any data */ 225 /* return a WARNING status if we couldn't read any data */
196 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { 226 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
197 printf(_("recv() failed\n")); 227 mp_subcheck sc_read_data = mp_subcheck_init();
198 exit(STATE_WARNING); 228 sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
199 } 229 xasprintf(&sc_read_data.output, "recv() failed");
230 mp_add_subcheck_to_check(&overall, sc_read_data);
231 mp_exit(overall);
232 }
200 233
201 char *server_response = NULL; 234 char *server_response = NULL;
202 /* save connect return (220 hostname ..) for later use */ 235 /* save connect return (220 hostname ..) for later use */
203 xasprintf(&server_response, "%s", buffer); 236 xasprintf(&server_response, "%s", buffer);
204 237
205 /* send the HELO/EHLO command */ 238 /* send the HELO/EHLO command */
206 my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established); 239 my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established);
207 240
208 /* allow for response to helo command to reach us */ 241 /* allow for response to helo command to reach us */
209 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { 242 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
210 printf(_("recv() failed\n")); 243 mp_subcheck sc_read_data = mp_subcheck_init();
211 exit(STATE_WARNING); 244 sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
212 } 245 xasprintf(&sc_read_data.output, "recv() failed");
246 mp_add_subcheck_to_check(&overall, sc_read_data);
247 mp_exit(overall);
248 }
213 249
214 bool supports_tls = false; 250 bool supports_tls = false;
215 if (config.use_ehlo || config.use_lhlo) { 251 if (config.use_ehlo || config.use_lhlo) {
216 if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) { 252 if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) {
217 supports_tls = true; 253 supports_tls = true;
218 }
219 } 254 }
255 }
220 256
221 if (config.use_starttls && !supports_tls) { 257 if (config.use_starttls && !supports_tls) {
222 printf(_("WARNING - TLS not supported by server\n")); 258 smtp_quit(config, buffer, socket_descriptor, ssl_established);
259
260 mp_subcheck sc_read_data = mp_subcheck_init();
261 sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
262 xasprintf(&sc_read_data.output, "StartTLS not supported by server");
263 mp_add_subcheck_to_check(&overall, sc_read_data);
264 mp_exit(overall);
265 }
266
267#ifdef HAVE_SSL
268 if (config.use_starttls) {
269 /* send the STARTTLS command */
270 send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
271
272 mp_subcheck sc_starttls_init = mp_subcheck_init();
273 recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
274 ssl_established); /* wait for it */
275 if (!strstr(buffer, SMTP_EXPECT)) {
223 smtp_quit(config, buffer, socket_descriptor, ssl_established); 276 smtp_quit(config, buffer, socket_descriptor, ssl_established);
224 exit(STATE_WARNING); 277
278 xasprintf(&sc_starttls_init.output, "StartTLS not supported by server");
279 sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_UNKNOWN);
280 mp_add_subcheck_to_check(&overall, sc_starttls_init);
281 mp_exit(overall);
225 } 282 }
226 283
227#ifdef HAVE_SSL 284 mp_state_enum starttls_result = np_net_ssl_init_with_hostname(
228 if (config.use_starttls) { 285 socket_descriptor, (config.use_sni ? config.server_address : NULL));
229 /* send the STARTTLS command */ 286 if (starttls_result != STATE_OK) {
230 send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); 287 close(socket_descriptor);
231 288 np_net_ssl_cleanup();
232 recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
233 ssl_established); /* wait for it */
234 if (!strstr(buffer, SMTP_EXPECT)) {
235 printf(_("Server does not support STARTTLS\n"));
236 smtp_quit(config, buffer, socket_descriptor, ssl_established);
237 exit(STATE_UNKNOWN);
238 }
239 289
240 result = np_net_ssl_init_with_hostname(socket_descriptor, 290 sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_CRITICAL);
241 (config.use_sni ? config.server_address : NULL)); 291 xasprintf(&sc_starttls_init.output, "failed to create StartTLS context");
242 if (result != STATE_OK) { 292 mp_add_subcheck_to_check(&overall, sc_starttls_init);
243 printf(_("CRITICAL - Cannot create SSL context.\n")); 293 mp_exit(overall);
244 close(socket_descriptor); 294 }
245 np_net_ssl_cleanup(); 295 sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_OK);
246 exit(STATE_CRITICAL); 296 xasprintf(&sc_starttls_init.output, "created StartTLS context");
247 } 297 mp_add_subcheck_to_check(&overall, sc_starttls_init);
298
299 ssl_established = true;
300
301 /*
302 * Resend the EHLO command.
303 *
304 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
305 * obtained from the server, such as the list of SMTP service
306 * extensions, which was not obtained from the TLS negotiation
307 * itself. The client SHOULD send an EHLO command as the first
308 * command after a successful TLS negotiation.'' For this
309 * reason, some MTAs will not allow an AUTH LOGIN command before
310 * we resent EHLO via TLS.
311 */
312 if (my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established) <=
313 0) {
314 my_close(socket_descriptor);
315
316 mp_subcheck sc_ehlo = mp_subcheck_init();
317 sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN);
318 xasprintf(&sc_ehlo.output, "cannot send EHLO command via StartTLS");
319 mp_add_subcheck_to_check(&overall, sc_ehlo);
320 mp_exit(overall);
321 }
248 322
249 ssl_established = true; 323 if (verbose) {
250 324 printf(_("sent %s"), helocmd);
251 /* 325 }
252 * Resend the EHLO command.
253 *
254 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
255 * obtained from the server, such as the list of SMTP service
256 * extensions, which was not obtained from the TLS negotiation
257 * itself. The client SHOULD send an EHLO command as the first
258 * command after a successful TLS negotiation.'' For this
259 * reason, some MTAs will not allow an AUTH LOGIN command before
260 * we resent EHLO via TLS.
261 */
262 if (my_send(config, helocmd, strlen(helocmd), socket_descriptor, ssl_established) <=
263 0) {
264 printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS."));
265 my_close(socket_descriptor);
266 exit(STATE_UNKNOWN);
267 }
268 326
269 if (verbose) { 327 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
270 printf(_("sent %s"), helocmd); 328 my_close(socket_descriptor);
271 }
272 329
273 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 330 mp_subcheck sc_ehlo = mp_subcheck_init();
274 0) { 331 sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN);
275 printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS.")); 332 xasprintf(&sc_ehlo.output, "cannot read EHLO response via StartTLS");
276 my_close(socket_descriptor); 333 mp_add_subcheck_to_check(&overall, sc_ehlo);
277 exit(STATE_UNKNOWN); 334 mp_exit(overall);
278 } 335 }
279 336
280 if (verbose) { 337 if (verbose) {
281 printf("%s", buffer); 338 printf("%s", buffer);
282 } 339 }
340 }
283 341
284# ifdef USE_OPENSSL 342# ifdef USE_OPENSSL
285 if (config.check_cert) { 343 if (ssl_established) {
286 result = 344 net_ssl_check_cert_result cert_check_result =
287 np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit); 345 np_net_ssl_check_cert2(config.days_till_exp_warn, config.days_till_exp_crit);
288 smtp_quit(config, buffer, socket_descriptor, ssl_established); 346
289 my_close(socket_descriptor); 347 mp_subcheck sc_cert_check = mp_subcheck_init();
290 exit(result); 348
349 switch (cert_check_result.errors) {
350 case ALL_OK: {
351
352 if (cert_check_result.result_state != STATE_OK &&
353 config.ignore_certificate_expiration) {
354 xasprintf(&sc_cert_check.output,
355 "Remaining certificate lifetime: %d days. Expiration will be ignored",
356 (int)(cert_check_result.remaining_seconds / 86400));
357 sc_cert_check = mp_set_subcheck_state(sc_cert_check, STATE_OK);
358 } else {
359 xasprintf(&sc_cert_check.output, "Remaining certificate lifetime: %d days",
360 (int)(cert_check_result.remaining_seconds / 86400));
361 sc_cert_check =
362 mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
291 } 363 }
364 } break;
365 case NO_SERVER_CERTIFICATE_PRESENT: {
366 xasprintf(&sc_cert_check.output, "no server certificate present");
367 sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
368 } break;
369 case UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT: {
370 xasprintf(&sc_cert_check.output, "can not retrieve certificate subject");
371 sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
372 } break;
373 case WRONG_TIME_FORMAT_IN_CERTIFICATE: {
374 xasprintf(&sc_cert_check.output, "wrong time format in certificate");
375 sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
376 } break;
377 };
378
379 mp_add_subcheck_to_check(&overall, sc_cert_check);
380 }
292# endif /* USE_OPENSSL */ 381# endif /* USE_OPENSSL */
293 } 382
294#endif 383#endif
295 384
296 if (verbose) { 385 if (verbose) {
386 printf("%s", buffer);
387 }
388
389 /* save buffer for later use */
390 xasprintf(&server_response, "%s%s", server_response, buffer);
391 /* strip the buffer of carriage returns */
392 strip(server_response);
393
394 /* make sure we find the droids we are looking for */
395 mp_subcheck sc_expect_response = mp_subcheck_init();
396
397 if (!strstr(server_response, config.server_expect)) {
398 sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_WARNING);
399 if (config.server_port == SMTP_PORT) {
400 xasprintf(&sc_expect_response.output, _("invalid SMTP response received from host: %s"),
401 server_response);
402 } else {
403 xasprintf(&sc_expect_response.output,
404 _("invalid SMTP response received from host on port %d: %s"),
405 config.server_port, server_response);
406 }
407 exit(STATE_WARNING);
408 } else {
409 xasprintf(&sc_expect_response.output, "received valid SMTP response '%s' from host: '%s'",
410 config.server_expect, server_response);
411 sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_OK);
412 }
413
414 mp_add_subcheck_to_check(&overall, sc_expect_response);
415
416 if (config.send_mail_from) {
417 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
418 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 &&
419 verbose) {
297 printf("%s", buffer); 420 printf("%s", buffer);
298 } 421 }
422 }
299 423
300 /* save buffer for later use */ 424 size_t counter = 0;
301 xasprintf(&server_response, "%s%s", server_response, buffer); 425 while (counter < config.ncommands) {
302 /* strip the buffer of carriage returns */ 426 xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n");
303 strip(server_response); 427 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
428 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 &&
429 verbose) {
430 printf("%s", buffer);
431 }
304 432
305 /* make sure we find the droids we are looking for */ 433 strip(buffer);
306 if (!strstr(server_response, config.server_expect)) { 434
307 if (config.server_port == SMTP_PORT) { 435 if (counter < config.nresponses) {
308 printf(_("Invalid SMTP response received from host: %s\n"), server_response); 436 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
309 } else { 437 regex_t preg;
310 printf(_("Invalid SMTP response received from host on port %d: %s\n"), 438 int errcode = regcomp(&preg, config.responses[counter], cflags);
311 config.server_port, server_response); 439 char errbuf[MAX_INPUT_BUFFER];
440 if (errcode != 0) {
441 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
442 printf(_("Could Not Compile Regular Expression"));
443 exit(STATE_UNKNOWN);
312 } 444 }
313 exit(STATE_WARNING);
314 }
315 445
316 if (config.send_mail_from) { 446 regmatch_t pmatch[10];
317 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established); 447 int eflags = 0;
318 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 448 int excode = regexec(&preg, buffer, 10, pmatch, eflags);
319 1 && 449 mp_subcheck sc_expected_responses = mp_subcheck_init();
320 verbose) { 450 if (excode == 0) {
321 printf("%s", buffer); 451 xasprintf(&sc_expected_responses.output, "valid response '%s' to command '%s'",
452 buffer, config.commands[counter]);
453 sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_OK);
454 } else if (excode == REG_NOMATCH) {
455 sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_WARNING);
456 xasprintf(&sc_expected_responses.output, "invalid response '%s' to command '%s'",
457 buffer, config.commands[counter]);
458 } else {
459 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
460 xasprintf(&sc_expected_responses.output, "regexec execute error: %s", errbuf);
461 sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_UNKNOWN);
322 } 462 }
323 } 463 }
464 counter++;
465 }
324 466
325 int counter = 0; 467 if (config.authtype != NULL) {
326 while (counter < config.ncommands) { 468 mp_subcheck sc_auth = mp_subcheck_init();
327 xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n"); 469
328 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established); 470 if (strcmp(config.authtype, "LOGIN") == 0) {
329 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 471 char *abuf;
330 1 && 472 int ret;
331 verbose) { 473 do {
332 printf("%s", buffer); 474 /* send AUTH LOGIN */
333 } 475 my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
334 strip(buffer); 476 ssl_established);
335 if (counter < config.nresponses) { 477
336 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 478 if (verbose) {
337 regex_t preg; 479 printf(_("sent %s\n"), "AUTH LOGIN");
338 int errcode = regcomp(&preg, config.responses[counter], cflags);
339 char errbuf[MAX_INPUT_BUFFER];
340 if (errcode != 0) {
341 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
342 printf(_("Could Not Compile Regular Expression"));
343 exit(STATE_UNKNOWN);
344 } 480 }
345 481
346 regmatch_t pmatch[10]; 482 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
347 int eflags = 0; 483 ssl_established)) <= 0) {
348 int excode = regexec(&preg, buffer, 10, pmatch, eflags); 484 xasprintf(&sc_auth.output, _("recv() failed after AUTH LOGIN"));
349 if (excode == 0) { 485 sc_auth = mp_set_subcheck_state(sc_auth, STATE_WARNING);
350 result = STATE_OK; 486 break;
351 } else if (excode == REG_NOMATCH) {
352 result = STATE_WARNING;
353 printf(_("SMTP %s - Invalid response '%s' to command '%s'\n"),
354 state_text(result), buffer, config.commands[counter]);
355 } else {
356 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
357 printf(_("Execute Error: %s\n"), errbuf);
358 result = STATE_UNKNOWN;
359 } 487 }
360 }
361 counter++;
362 }
363 488
364 if (config.authtype != NULL) { 489 if (verbose) {
365 if (strcmp(config.authtype, "LOGIN") == 0) { 490 printf(_("received %s\n"), buffer);
366 char *abuf; 491 }
367 int ret; 492
368 do { 493 if (strncmp(buffer, "334", 3) != 0) {
369 if (config.authuser == NULL) { 494 xasprintf(&sc_auth.output, "invalid response received after AUTH LOGIN");
370 result = STATE_CRITICAL; 495 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
371 xasprintf(&error_msg, _("no authuser specified, "));
372 break;
373 }
374 if (config.authpass == NULL) {
375 result = STATE_CRITICAL;
376 xasprintf(&error_msg, _("no authpass specified, "));
377 break;
378 }
379
380 /* send AUTH LOGIN */
381 my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
382 ssl_established);
383 if (verbose) {
384 printf(_("sent %s\n"), "AUTH LOGIN");
385 }
386
387 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
388 ssl_established)) <= 0) {
389 xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
390 result = STATE_WARNING;
391 break;
392 }
393 if (verbose) {
394 printf(_("received %s\n"), buffer);
395 }
396
397 if (strncmp(buffer, "334", 3) != 0) {
398 result = STATE_CRITICAL;
399 xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, "));
400 break;
401 }
402
403 /* encode authuser with base64 */
404 base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
405 xasprintf(&abuf, "%s\r\n", abuf);
406 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
407 if (verbose) {
408 printf(_("sent %s\n"), abuf);
409 }
410
411 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
412 ssl_established)) <= 0) {
413 result = STATE_CRITICAL;
414 xasprintf(&error_msg, _("recv() failed after sending authuser, "));
415 break;
416 }
417 if (verbose) {
418 printf(_("received %s\n"), buffer);
419 }
420 if (strncmp(buffer, "334", 3) != 0) {
421 result = STATE_CRITICAL;
422 xasprintf(&error_msg, _("invalid response received after authuser, "));
423 break;
424 }
425 /* encode authpass with base64 */
426 base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
427 xasprintf(&abuf, "%s\r\n", abuf);
428 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
429 if (verbose) {
430 printf(_("sent %s\n"), abuf);
431 }
432 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
433 ssl_established)) <= 0) {
434 result = STATE_CRITICAL;
435 xasprintf(&error_msg, _("recv() failed after sending authpass, "));
436 break;
437 }
438 if (verbose) {
439 printf(_("received %s\n"), buffer);
440 }
441 if (strncmp(buffer, "235", 3) != 0) {
442 result = STATE_CRITICAL;
443 xasprintf(&error_msg, _("invalid response received after authpass, "));
444 break;
445 }
446 break; 496 break;
447 } while (false); 497 }
448 } else {
449 result = STATE_CRITICAL;
450 xasprintf(&error_msg, _("only authtype LOGIN is supported, "));
451 }
452 }
453 498
454 /* tell the server we're done */ 499 /* encode authuser with base64 */
455 smtp_quit(config, buffer, socket_descriptor, ssl_established); 500 base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
501 xasprintf(&abuf, "%s\r\n", abuf);
502 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
503 if (verbose) {
504 printf(_("sent %s\n"), abuf);
505 }
506
507 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
508 ssl_established)) <= 0) {
509 xasprintf(&sc_auth.output, "recv() failed after sending authuser");
510 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
511 break;
512 }
513
514 if (verbose) {
515 printf(_("received %s\n"), buffer);
516 }
517
518 if (strncmp(buffer, "334", 3) != 0) {
519 xasprintf(&sc_auth.output, "invalid response received after authuser");
520 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
521 break;
522 }
456 523
457 /* finally close the connection */ 524 /* encode authpass with base64 */
458 close(socket_descriptor); 525 base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
526 xasprintf(&abuf, "%s\r\n", abuf);
527 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
528
529 if (verbose) {
530 printf(_("sent %s\n"), abuf);
531 }
532
533 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
534 ssl_established)) <= 0) {
535 xasprintf(&sc_auth.output, "recv() failed after sending authpass");
536 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
537 break;
538 }
539
540 if (verbose) {
541 printf(_("received %s\n"), buffer);
542 }
543
544 if (strncmp(buffer, "235", 3) != 0) {
545 xasprintf(&sc_auth.output, "invalid response received after authpass");
546 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
547 break;
548 }
549 break;
550 } while (false);
551 } else {
552 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
553 xasprintf(&sc_auth.output, "only authtype LOGIN is supported");
554 }
555
556 mp_add_subcheck_to_check(&overall, sc_auth);
459 } 557 }
460 558
559 /* tell the server we're done */
560 smtp_quit(config, buffer, socket_descriptor, ssl_established);
561
562 /* finally close the connection */
563 close(socket_descriptor);
564
461 /* reset the alarm */ 565 /* reset the alarm */
462 alarm(0); 566 alarm(0);
463 567
464 long microsec = deltime(start_time); 568 long microsec = deltime(start_time);
465 double elapsed_time = (double)microsec / 1.0e6; 569 double elapsed_time = (double)microsec / 1.0e6;
466 570
467 if (result == STATE_OK) { 571 mp_perfdata pd_elapsed_time = perfdata_init();
468 if (config.check_critical_time && elapsed_time > config.critical_time) { 572 pd_elapsed_time = mp_set_pd_value(pd_elapsed_time, elapsed_time);
469 result = STATE_CRITICAL; 573 pd_elapsed_time.label = "time";
470 } else if (config.check_warning_time && elapsed_time > config.warning_time) { 574 pd_elapsed_time.uom = "s";
471 result = STATE_WARNING; 575
472 } 576 pd_elapsed_time = mp_pd_set_thresholds(pd_elapsed_time, config.connection_time);
473 }
474 577
475 printf(_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), state_text(result), error_msg, 578 mp_subcheck sc_connection_time = mp_subcheck_init();
476 elapsed_time, verbose ? ", " : "", verbose ? buffer : "", 579 xasprintf(&sc_connection_time.output, "connection time: %.3gs", elapsed_time);
477 fperfdata("time", elapsed_time, "s", config.check_warning_time, config.warning_time, 580 sc_connection_time =
478 config.check_critical_time, config.critical_time, true, 0, false, 0)); 581 mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_elapsed_time));
582 mp_add_subcheck_to_check(&overall, sc_connection_time);
479 583
480 exit(result); 584 mp_exit(overall);
481} 585}
482 586
483/* process command-line arguments */ 587/* process command-line arguments */
484check_smtp_config_wrapper process_arguments(int argc, char **argv) { 588check_smtp_config_wrapper process_arguments(int argc, char **argv) {
485 enum { 589 enum {
486 SNI_OPTION = CHAR_MAX + 1 590 SNI_OPTION = CHAR_MAX + 1,
591 output_format_index,
592 ignore_certificate_expiration_index,
487 }; 593 };
488 594
489 int option = 0; 595 int option = 0;
490 static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, 596 static struct option longopts[] = {
491 {"expect", required_argument, 0, 'e'}, 597 {"hostname", required_argument, 0, 'H'},
492 {"critical", required_argument, 0, 'c'}, 598 {"expect", required_argument, 0, 'e'},
493 {"warning", required_argument, 0, 'w'}, 599 {"critical", required_argument, 0, 'c'},
494 {"timeout", required_argument, 0, 't'}, 600 {"warning", required_argument, 0, 'w'},
495 {"port", required_argument, 0, 'p'}, 601 {"timeout", required_argument, 0, 't'},
496 {"from", required_argument, 0, 'f'}, 602 {"port", required_argument, 0, 'p'},
497 {"fqdn", required_argument, 0, 'F'}, 603 {"from", required_argument, 0, 'f'},
498 {"authtype", required_argument, 0, 'A'}, 604 {"fqdn", required_argument, 0, 'F'},
499 {"authuser", required_argument, 0, 'U'}, 605 {"authtype", required_argument, 0, 'A'},
500 {"authpass", required_argument, 0, 'P'}, 606 {"authuser", required_argument, 0, 'U'},
501 {"command", required_argument, 0, 'C'}, 607 {"authpass", required_argument, 0, 'P'},
502 {"response", required_argument, 0, 'R'}, 608 {"command", required_argument, 0, 'C'},
503 {"verbose", no_argument, 0, 'v'}, 609 {"response", required_argument, 0, 'R'},
504 {"version", no_argument, 0, 'V'}, 610 {"verbose", no_argument, 0, 'v'},
505 {"use-ipv4", no_argument, 0, '4'}, 611 {"version", no_argument, 0, 'V'},
506 {"use-ipv6", no_argument, 0, '6'}, 612 {"use-ipv4", no_argument, 0, '4'},
507 {"help", no_argument, 0, 'h'}, 613 {"use-ipv6", no_argument, 0, '6'},
508 {"lmtp", no_argument, 0, 'L'}, 614 {"help", no_argument, 0, 'h'},
509 {"ssl", no_argument, 0, 's'}, 615 {"lmtp", no_argument, 0, 'L'},
510 {"tls", no_argument, 0, 's'}, 616 {"ssl", no_argument, 0, 's'},
511 {"starttls", no_argument, 0, 'S'}, 617 {"tls", no_argument, 0, 's'},
512 {"sni", no_argument, 0, SNI_OPTION}, 618 {"starttls", no_argument, 0, 'S'},
513 {"certificate", required_argument, 0, 'D'}, 619 {"sni", no_argument, 0, SNI_OPTION},
514 {"ignore-quit-failure", no_argument, 0, 'q'}, 620 {"certificate", required_argument, 0, 'D'},
515 {"proxy", no_argument, 0, 'r'}, 621 {"ignore-quit-failure", no_argument, 0, 'q'},
516 {0, 0, 0, 0}}; 622 {"proxy", no_argument, 0, 'r'},
623 {"ignore-certificate-expiration", no_argument, 0, ignore_certificate_expiration_index},
624 {"output-format", required_argument, 0, output_format_index},
625 {0, 0, 0, 0}};
517 626
518 check_smtp_config_wrapper result = { 627 check_smtp_config_wrapper result = {
519 .config = check_smtp_config_init(), 628 .config = check_smtp_config_init(),
@@ -535,8 +644,8 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
535 } 644 }
536 } 645 }
537 646
538 int command_size = 0; 647 unsigned long command_size = 0;
539 int response_size = 0; 648 unsigned long response_size = 0;
540 bool implicit_tls = false; 649 bool implicit_tls = false;
541 int server_port_option = 0; 650 int server_port_option = 0;
542 while (true) { 651 while (true) {
@@ -591,7 +700,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
591 result.config.commands = 700 result.config.commands =
592 realloc(result.config.commands, sizeof(char *) * command_size); 701 realloc(result.config.commands, sizeof(char *) * command_size);
593 if (result.config.commands == NULL) { 702 if (result.config.commands == NULL) {
594 die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"), 703 die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"),
595 result.config.ncommands); 704 result.config.ncommands);
596 } 705 }
597 } 706 }
@@ -605,7 +714,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
605 result.config.responses = 714 result.config.responses =
606 realloc(result.config.responses, sizeof(char *) * response_size); 715 realloc(result.config.responses, sizeof(char *) * response_size);
607 if (result.config.responses == NULL) { 716 if (result.config.responses == NULL) {
608 die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"), 717 die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"),
609 result.config.nresponses); 718 result.config.nresponses);
610 } 719 }
611 } 720 }
@@ -613,22 +722,22 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
613 strncpy(result.config.responses[result.config.nresponses], optarg, 255); 722 strncpy(result.config.responses[result.config.nresponses], optarg, 255);
614 result.config.nresponses++; 723 result.config.nresponses++;
615 break; 724 break;
616 case 'c': /* critical time threshold */ 725 case 'c': /* critical time threshold */ {
617 if (!is_nonnegative(optarg)) { 726 mp_range_parsed tmp = mp_parse_range_string(optarg);
618 usage4(_("Critical time must be a positive")); 727 if (tmp.error != MP_PARSING_SUCCES) {
619 } else { 728 die(STATE_UNKNOWN, "failed to parse critical time threshold");
620 result.config.critical_time = strtod(optarg, NULL);
621 result.config.check_critical_time = true;
622 } 729 }
623 break; 730 result.config.connection_time =
624 case 'w': /* warning time threshold */ 731 mp_thresholds_set_warn(result.config.connection_time, tmp.range);
625 if (!is_nonnegative(optarg)) { 732 } break;
626 usage4(_("Warning time must be a positive")); 733 case 'w': /* warning time threshold */ {
627 } else { 734 mp_range_parsed tmp = mp_parse_range_string(optarg);
628 result.config.warning_time = strtod(optarg, NULL); 735 if (tmp.error != MP_PARSING_SUCCES) {
629 result.config.check_warning_time = true; 736 die(STATE_UNKNOWN, "failed to parse warning time threshold");
630 } 737 }
631 break; 738 result.config.connection_time =
739 mp_thresholds_set_crit(result.config.connection_time, tmp.range);
740 } break;
632 case 'v': /* verbose */ 741 case 'v': /* verbose */
633 verbose++; 742 verbose++;
634 break; 743 break;
@@ -665,7 +774,6 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
665 } 774 }
666 result.config.days_till_exp_warn = atoi(optarg); 775 result.config.days_till_exp_warn = atoi(optarg);
667 } 776 }
668 result.config.check_cert = true;
669 result.config.ignore_send_quit_failure = true; 777 result.config.ignore_send_quit_failure = true;
670#else 778#else
671 usage(_("SSL support not available - install OpenSSL and recompile")); 779 usage(_("SSL support not available - install OpenSSL and recompile"));
@@ -714,6 +822,21 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
714 exit(STATE_UNKNOWN); 822 exit(STATE_UNKNOWN);
715 case '?': /* help */ 823 case '?': /* help */
716 usage5(); 824 usage5();
825 case output_format_index: {
826 parsed_output_format parser = mp_parse_output_format(optarg);
827 if (!parser.parsing_success) {
828 // TODO List all available formats here, maybe add anothoer usage function
829 printf("Invalid output format: %s\n", optarg);
830 exit(STATE_UNKNOWN);
831 }
832
833 result.config.output_format_is_set = true;
834 result.config.output_format = parser.output_format;
835 break;
836 }
837 case ignore_certificate_expiration_index: {
838 result.config.ignore_certificate_expiration = true;
839 }
717 } 840 }
718 } 841 }
719 842
@@ -742,6 +865,19 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
742 result.config.server_port = server_port_option; 865 result.config.server_port = server_port_option;
743 } 866 }
744 867
868 if (result.config.authtype) {
869 if (strcmp(result.config.authtype, "LOGIN") == 0) {
870 if (result.config.authuser == NULL) {
871 usage4("no authuser specified");
872 }
873 if (result.config.authpass == NULL) {
874 usage4("no authpass specified");
875 }
876 } else {
877 usage4("only authtype LOGIN is supported");
878 }
879 }
880
745 return result; 881 return result;
746} 882}
747 883
@@ -791,7 +927,7 @@ char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int soc
791int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor, 927int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor,
792 bool ssl_established) { 928 bool ssl_established) {
793 int result; 929 int result;
794 int counter; 930 size_t counter;
795 931
796 for (counter = result = 0; counter < bufsize - 1; counter++) { 932 for (counter = result = 0; counter < bufsize - 1; counter++) {
797 if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) { 933 if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) {
@@ -799,7 +935,7 @@ int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_des
799 } 935 }
800 if (buf[counter] == '\n') { 936 if (buf[counter] == '\n') {
801 buf[++counter] = '\0'; 937 buf[++counter] = '\0';
802 return counter; 938 return (int)counter;
803 } 939 }
804 } 940 }
805 return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */ 941 return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */
@@ -902,11 +1038,15 @@ void print_help(void) {
902 printf(" %s\n", _("Send LHLO instead of HELO/EHLO")); 1038 printf(" %s\n", _("Send LHLO instead of HELO/EHLO"));
903 printf(" %s\n", "-q, --ignore-quit-failure"); 1039 printf(" %s\n", "-q, --ignore-quit-failure");
904 printf(" %s\n", _("Ignore failure when sending QUIT command to server")); 1040 printf(" %s\n", _("Ignore failure when sending QUIT command to server"));
1041 printf(" %s\n", "--ignore-certificate-expiration");
1042 printf(" %s\n", _("Ignore certificate expiration"));
905 1043
906 printf(UT_WARN_CRIT); 1044 printf(UT_WARN_CRIT);
907 1045
908 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1046 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
909 1047
1048 printf(UT_OUTPUT_FORMAT);
1049
910 printf(UT_VERBOSE); 1050 printf(UT_VERBOSE);
911 1051
912 printf("\n"); 1052 printf("\n");
diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h
index 0a6511ef..b0d42ed1 100644
--- a/plugins/check_smtp.d/config.h
+++ b/plugins/check_smtp.d/config.h
@@ -1,6 +1,8 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "output.h"
5#include "thresholds.h"
4#include <stddef.h> 6#include <stddef.h>
5#include <string.h> 7#include <string.h>
6 8
@@ -18,20 +20,18 @@ typedef struct {
18 char *server_expect; 20 char *server_expect;
19 bool ignore_send_quit_failure; 21 bool ignore_send_quit_failure;
20 22
21 double warning_time; 23 mp_thresholds connection_time;
22 bool check_warning_time; 24
23 double critical_time;
24 bool check_critical_time;
25 bool use_ehlo; 25 bool use_ehlo;
26 bool use_lhlo; 26 bool use_lhlo;
27 27
28 char *from_arg; 28 char *from_arg;
29 bool send_mail_from; 29 bool send_mail_from;
30 30
31 int ncommands; 31 unsigned long ncommands;
32 char **commands; 32 char **commands;
33 33
34 int nresponses; 34 unsigned long nresponses;
35 char **responses; 35 char **responses;
36 36
37 char *authtype; 37 char *authtype;
@@ -40,13 +40,17 @@ typedef struct {
40 40
41 bool use_proxy_prefix; 41 bool use_proxy_prefix;
42#ifdef HAVE_SSL 42#ifdef HAVE_SSL
43 bool check_cert;
44 int days_till_exp_warn; 43 int days_till_exp_warn;
45 int days_till_exp_crit; 44 int days_till_exp_crit;
46 bool use_ssl; 45 bool use_ssl;
47 bool use_starttls; 46 bool use_starttls;
48 bool use_sni; 47 bool use_sni;
48
49 bool ignore_certificate_expiration;
49#endif 50#endif
51
52 bool output_format_is_set;
53 mp_output_format output_format;
50} check_smtp_config; 54} check_smtp_config;
51 55
52check_smtp_config check_smtp_config_init() { 56check_smtp_config check_smtp_config_init() {
@@ -58,10 +62,7 @@ check_smtp_config check_smtp_config_init() {
58 .server_expect = SMTP_EXPECT, 62 .server_expect = SMTP_EXPECT,
59 .ignore_send_quit_failure = false, 63 .ignore_send_quit_failure = false,
60 64
61 .warning_time = 0, 65 .connection_time = mp_thresholds_init(),
62 .check_warning_time = false,
63 .critical_time = 0,
64 .check_critical_time = false,
65 .use_ehlo = false, 66 .use_ehlo = false,
66 .use_lhlo = false, 67 .use_lhlo = false,
67 68
@@ -80,13 +81,16 @@ check_smtp_config check_smtp_config_init() {
80 81
81 .use_proxy_prefix = false, 82 .use_proxy_prefix = false,
82#ifdef HAVE_SSL 83#ifdef HAVE_SSL
83 .check_cert = false,
84 .days_till_exp_warn = 0, 84 .days_till_exp_warn = 0,
85 .days_till_exp_crit = 0, 85 .days_till_exp_crit = 0,
86 .use_ssl = false, 86 .use_ssl = false,
87 .use_starttls = false, 87 .use_starttls = false,
88 .use_sni = false, 88 .use_sni = false,
89
90 .ignore_certificate_expiration = false,
89#endif 91#endif
92
93 .output_format_is_set = false,
90 }; 94 };
91 return tmp; 95 return tmp;
92} 96}
diff --git a/plugins/netutils.h b/plugins/netutils.h
index c4461113..dbd22398 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -114,6 +114,26 @@ int np_net_ssl_init_with_hostname_version_and_cert(int socket, char *host_name,
114void np_net_ssl_cleanup(void); 114void np_net_ssl_cleanup(void);
115int np_net_ssl_write(const void *buf, int num); 115int np_net_ssl_write(const void *buf, int num);
116int np_net_ssl_read(void *buf, int num); 116int np_net_ssl_read(void *buf, int num);
117
118typedef enum {
119 ALL_OK,
120 NO_SERVER_CERTIFICATE_PRESENT,
121 UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT,
122 WRONG_TIME_FORMAT_IN_CERTIFICATE,
123} retrieve_expiration_date_errors;
124
125typedef struct {
126 double remaining_seconds;
127 retrieve_expiration_date_errors errors;
128} retrieve_expiration_time_result;
129
130typedef struct {
131 mp_state_enum result_state;
132 double remaining_seconds;
133 retrieve_expiration_date_errors errors;
134} net_ssl_check_cert_result;
135net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit);
136
117mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); 137mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
118mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); 138mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
119#endif /* HAVE_SSL */ 139#endif /* HAVE_SSL */
diff --git a/plugins/runcmd.c b/plugins/runcmd.c
index 7c583b85..be6691d2 100644
--- a/plugins/runcmd.c
+++ b/plugins/runcmd.c
@@ -300,7 +300,6 @@ static int np_fetch_output(int fd, output *op, int flags) {
300 } 300 }
301 301
302 op->line = NULL; 302 op->line = NULL;
303 op->lens = NULL;
304 i = 0; 303 i = 0;
305 while (i < op->buflen) { 304 while (i < op->buflen) {
306 /* make sure we have enough memory */ 305 /* make sure we have enough memory */
@@ -311,7 +310,6 @@ static int np_fetch_output(int fd, output *op, int flags) {
311 } while (!ary_size); 310 } while (!ary_size);
312 311
313 op->line = realloc(op->line, ary_size * sizeof(char *)); 312 op->line = realloc(op->line, ary_size * sizeof(char *));
314 op->lens = realloc(op->lens, ary_size * sizeof(size_t));
315 } 313 }
316 314
317 /* set the pointer to the string */ 315 /* set the pointer to the string */
@@ -323,9 +321,6 @@ static int np_fetch_output(int fd, output *op, int flags) {
323 } 321 }
324 buf[i] = '\0'; 322 buf[i] = '\0';
325 323
326 /* calculate the string length using pointer difference */
327 op->lens[lineno] = (size_t)&buf[i] - (size_t)op->line[lineno];
328
329 lineno++; 324 lineno++;
330 i++; 325 i++;
331 } 326 }
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index 0e6d7525..c58a35ab 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -312,6 +312,138 @@ mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_
312# endif /* USE_OPENSSL */ 312# endif /* USE_OPENSSL */
313} 313}
314 314
315retrieve_expiration_time_result np_net_ssl_get_cert_expiration(X509 *certificate) {
316# ifdef USE_OPENSSL
317 retrieve_expiration_time_result result = {
318 .errors = ALL_OK,
319 .remaining_seconds = 0,
320 };
321
322 if (!certificate) {
323 // printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
324 result.errors = NO_SERVER_CERTIFICATE_PRESENT;
325 return result;
326 }
327
328 /* Extract CN from certificate subject */
329 X509_NAME *subj = X509_get_subject_name(certificate);
330
331 if (!subj) {
332 // printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
333 result.errors = UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT;
334 return result;
335 }
336
337 char cn[MAX_CN_LENGTH] = "";
338 int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn));
339 if (cnlen == -1) {
340 strcpy(cn, _("Unknown CN"));
341 }
342
343 /* Retrieve timestamp of certificate */
344 ASN1_STRING *expiration_timestamp = X509_get_notAfter(certificate);
345
346 int offset = 0;
347 struct tm stamp = {};
348 /* Generate tm structure to process timestamp */
349 if (expiration_timestamp->type == V_ASN1_UTCTIME) {
350 if (expiration_timestamp->length < 10) {
351 result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE;
352 return result;
353 }
354
355 stamp.tm_year =
356 (expiration_timestamp->data[0] - '0') * 10 + (expiration_timestamp->data[1] - '0');
357 if (stamp.tm_year < 50) {
358 stamp.tm_year += 100;
359 }
360 offset = 0;
361 } else {
362 if (expiration_timestamp->length < 12) {
363 result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE;
364 return result;
365 }
366
367 stamp.tm_year = (expiration_timestamp->data[0] - '0') * 1000 +
368 (expiration_timestamp->data[1] - '0') * 100 +
369 (expiration_timestamp->data[2] - '0') * 10 +
370 (expiration_timestamp->data[3] - '0');
371 stamp.tm_year -= 1900;
372 offset = 2;
373 }
374 stamp.tm_mon = (expiration_timestamp->data[2 + offset] - '0') * 10 +
375 (expiration_timestamp->data[3 + offset] - '0') - 1;
376 stamp.tm_mday = (expiration_timestamp->data[4 + offset] - '0') * 10 +
377 (expiration_timestamp->data[5 + offset] - '0');
378 stamp.tm_hour = (expiration_timestamp->data[6 + offset] - '0') * 10 +
379 (expiration_timestamp->data[7 + offset] - '0');
380 stamp.tm_min = (expiration_timestamp->data[8 + offset] - '0') * 10 +
381 (expiration_timestamp->data[9 + offset] - '0');
382 stamp.tm_sec = (expiration_timestamp->data[10 + offset] - '0') * 10 +
383 (expiration_timestamp->data[11 + offset] - '0');
384 stamp.tm_isdst = -1;
385
386 time_t tm_t = timegm(&stamp);
387 double time_left = difftime(tm_t, time(NULL));
388 result.remaining_seconds = time_left;
389
390 char *timezone = getenv("TZ");
391 setenv("TZ", "GMT", 1);
392 tzset();
393
394 char timestamp[50] = "";
395 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
396 if (timezone) {
397 setenv("TZ", timezone, 1);
398 } else {
399 unsetenv("TZ");
400 }
401
402 tzset();
403
404 X509_free(certificate);
405
406 return result;
407# else /* ifndef USE_OPENSSL */
408 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
409 return STATE_WARNING;
410# endif /* USE_OPENSSL */
411}
412
413net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit) {
414# ifdef USE_OPENSSL
415 X509 *certificate = NULL;
416 certificate = SSL_get_peer_certificate(s);
417
418 retrieve_expiration_time_result expiration_date = np_net_ssl_get_cert_expiration(certificate);
419
420 net_ssl_check_cert_result result = {
421 .result_state = STATE_UNKNOWN,
422 .remaining_seconds = expiration_date.remaining_seconds,
423 .errors = expiration_date.errors,
424 };
425
426 if (expiration_date.errors == ALL_OK) {
427 // got a valid expiration date
428 unsigned int remaining_days = result.remaining_seconds / 86400;
429
430 if (remaining_days < days_till_exp_crit) {
431 result.result_state = STATE_CRITICAL;
432 } else if (remaining_days < days_till_exp_warn) {
433 result.result_state = STATE_WARNING;
434 } else {
435 result.result_state = STATE_OK;
436 }
437 }
438
439 return result;
440
441# else /* ifndef USE_OPENSSL */
442 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
443 return STATE_WARNING;
444# endif /* USE_OPENSSL */
445}
446
315mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { 447mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
316# ifdef USE_OPENSSL 448# ifdef USE_OPENSSL
317 X509 *certificate = NULL; 449 X509 *certificate = NULL;
diff --git a/plugins/t/check_by_ssh.t b/plugins/t/check_by_ssh.t
index b6479f1f..0ee310cd 100644
--- a/plugins/t/check_by_ssh.t
+++ b/plugins/t/check_by_ssh.t
@@ -16,7 +16,7 @@ my $ssh_conf = getTestParameter( "NP_SSH_CONFIGFILE", "A config file with ssh
16 16
17plan skip_all => "SSH_HOST and SSH_IDENTITY must be defined" unless ($ssh_service && $ssh_key); 17plan skip_all => "SSH_HOST and SSH_IDENTITY must be defined" unless ($ssh_service && $ssh_key);
18 18
19plan tests => 42; 19plan tests => 33;
20 20
21# Some random check strings/response 21# Some random check strings/response
22my @response = ('OK: Everything is fine', 22my @response = ('OK: Everything is fine',
@@ -47,70 +47,70 @@ for (my $i=0; $i<4; $i++) {
47 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[$i]; exit $i'" 47 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[$i]; exit $i'"
48 ); 48 );
49 cmp_ok($result->return_code, '==', $i, "Exit with return code $i"); 49 cmp_ok($result->return_code, '==', $i, "Exit with return code $i");
50 is($result->output, $response[$i], "Status text is correct for check $i"); 50 like($result->output, "/$response[$i]/", "Status text is correct for check $i");
51} 51}
52 52
53$result = NPTest->testCmd( 53$result = NPTest->testCmd(
54 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 0'" 54 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 0'"
55 ); 55 );
56cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); 56cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
57is($result->output, 'OK - check_by_ssh: Remote command \'exit 0\' returned status 0', "Status text if command returned none (OK)"); 57like($result->output, '/command \'exit 0\' returned status 0/', "Status text if command returned none (OK)");
58 58
59$result = NPTest->testCmd( 59$result = NPTest->testCmd(
60 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 1'" 60 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 1'"
61 ); 61 );
62cmp_ok($result->return_code, '==', 1, "Exit with return code 1 (WARNING)"); 62cmp_ok($result->return_code, '==', 1, "Exit with return code 1 (WARNING)");
63is($result->output, 'WARNING - check_by_ssh: Remote command \'exit 1\' returned status 1', "Status text if command returned none (WARNING)"); 63like($result->output, '/command \'exit 1\' returned status 1/', "Status text if command returned none (WARNING)");
64 64
65$result = NPTest->testCmd( 65$result = NPTest->testCmd(
66 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 2'" 66 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 2'"
67 ); 67 );
68cmp_ok($result->return_code, '==', 2, "Exit with return code 2 (CRITICAL)"); 68cmp_ok($result->return_code, '==', 2, "Exit with return code 2 (CRITICAL)");
69is($result->output, 'CRITICAL - check_by_ssh: Remote command \'exit 2\' returned status 2', "Status text if command returned none (CRITICAL)"); 69like($result->output, '/command \'exit 2\' returned status 2/', "Status text if command returned none (CRITICAL)");
70 70
71$result = NPTest->testCmd( 71$result = NPTest->testCmd(
72 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 3'" 72 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 3'"
73 ); 73 );
74cmp_ok($result->return_code, '==', 3, "Exit with return code 3 (UNKNOWN)"); 74cmp_ok($result->return_code, '==', 3, "Exit with return code 3 (UNKNOWN)");
75is($result->output, 'UNKNOWN - check_by_ssh: Remote command \'exit 3\' returned status 3', "Status text if command returned none (UNKNOWN)"); 75like($result->output, '/command \'exit 3\' returned status 3/', "Status text if command returned none (UNKNOWN)");
76 76
77$result = NPTest->testCmd( 77$result = NPTest->testCmd(
78 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 7'" 78 "./check_by_ssh -i $ssh_key -H $ssh_service -C 'exit 7'"
79 ); 79 );
80cmp_ok($result->return_code, '==', 7, "Exit with return code 7 (out of bounds)"); 80cmp_ok($result->return_code, '==', 3, "Exit with return code 3");
81is($result->output, 'UNKNOWN - check_by_ssh: Remote command \'exit 7\' returned status 7', "Status text if command returned none (out of bounds)"); 81like($result->output, '/command \'exit 7\' returned status 7/', "Status text if command returned none (out of bounds)");
82 82
83$result = NPTest->testCmd( 83$result = NPTest->testCmd(
84 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[4]; exit 8'" 84 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[4]; exit 8'"
85 ); 85 );
86cmp_ok($result->return_code, '==', 8, "Exit with return code 8 (out of bounds)"); 86cmp_ok($result->return_code, '==', 3, "Exit with return code 3");
87is($result->output, $response[4], "Return proper status text even with unknown status codes"); 87like($result->output, "/$response[4]/", "Return proper status text even with unknown status codes");
88 88
89$result = NPTest->testCmd( 89$result = NPTest->testCmd(
90 "./check_by_ssh -i $ssh_key -H $ssh_service -F $ssh_conf -C 'exit 0'" 90 "./check_by_ssh -i $ssh_key -H $ssh_service -F $ssh_conf -C 'exit 0'"
91 ); 91 );
92cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); 92cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
93is($result->output, 'OK - check_by_ssh: Remote command \'exit 0\' returned status 0', "Status text if command returned none (OK)"); 93like($result->output, '/command \'exit 0\' returned status 0/', "Status text if command returned none (OK)");
94 94
95# Multiple active checks 95# Multiple active checks
96$result = NPTest->testCmd( 96$result = NPTest->testCmd(
97 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[1]; sh -c exit\\ 1' -C '$check[0]; sh -c exit\\ 0' -C '$check[3]; sh -c exit\\ 3' -C '$check[2]; sh -c exit\\ 2'" 97 "./check_by_ssh -i $ssh_key -H $ssh_service -C '$check[1]; sh -c exit\\ 1' -C '$check[0]; sh -c exit\\ 0' -C '$check[3]; sh -c exit\\ 3' -C '$check[2]; sh -c exit\\ 2'"
98 ); 98 );
99cmp_ok($result->return_code, '==', 0, "Multiple checks always return OK"); 99cmp_ok($result->return_code, '==', 0, "Multiple checks always return OK");
100my @lines = split(/\n/, $result->output); 100# my @lines = split(/\n/, $result->output);
101cmp_ok(scalar(@lines), '==', 8, "Correct number of output lines for multiple checks"); 101# cmp_ok(scalar(@lines), '==', 8, "Correct number of output lines for multiple checks");
102my %linemap = ( 102# my %linemap = (
103 '0' => '1', 103# '0' => '1',
104 '2' => '0', 104# '2' => '0',
105 '4' => '3', 105# '4' => '3',
106 '6' => '2', 106# '6' => '2',
107); 107# );
108foreach my $line (0, 2, 4, 6) { 108# foreach my $line (0, 2, 4, 6) {
109 my $code = $linemap{$line}; 109 # my $code = $linemap{$line};
110 my $statline = $line+1; 110 # my $statline = $line+1;
111 is($lines[$line], "$response[$code]", "multiple checks status text is correct for line $line"); 111 # is($lines[$line], "$response[$code]", "multiple checks status text is correct for line $line");
112 is($lines[$statline], "STATUS CODE: $code", "multiple check status code is correct for line $line"); 112 # is($lines[$statline], "STATUS CODE: $code", "multiple check status code is correct for line $line");
113} 113# }
114 114
115# Passive checks 115# Passive checks
116unlink("/tmp/check_by_ssh.$$"); 116unlink("/tmp/check_by_ssh.$$");
diff --git a/plugins/t/check_dbi.t b/plugins/t/check_dbi.t
index c24b5a8c..75444de4 100644
--- a/plugins/t/check_dbi.t
+++ b/plugins/t/check_dbi.t
@@ -21,12 +21,12 @@ plan tests => $tests;
21my $missing_driver_output = "failed to open DBI driver 'sqlite3'"; 21my $missing_driver_output = "failed to open DBI driver 'sqlite3'";
22 22
23my $bad_driver_output = "/failed to open DBI driver 'nodriver'/"; 23my $bad_driver_output = "/failed to open DBI driver 'nodriver'/";
24my $conn_time_output = "/OK - connection time: [0-9\.]+s \|/"; 24my $conn_time_output = "/connection time: [0-9\.]+s \|/";
25my $missing_query_output = "/Must specify a query to execute/"; 25my $missing_query_output = "/Must specify a query to execute/";
26my $no_rows_output = "/WARNING - no rows returned/"; 26my $no_rows_output = "/no rows returned/";
27my $not_numeric_output = "/CRITICAL - result value is not a numeric:/"; 27my $not_numeric_output = "/result value is not a numeric:/";
28my $query_time_output = "/OK - connection time: [0-9\.]+s, 'SELECT 1' returned 1.000000 in [0-9\.]+s \|/"; 28my $query_time_output = "/connection time: [0-9\.]+s, 'SELECT 1' returned 1.000000 in [0-9\.]+s \|/";
29my $syntax_error_output = "/CRITICAL - failed to execute query 'GET ALL FROM test': 1: near \"GET\": syntax error/"; 29my $syntax_error_output = "/1: near \"GET\": syntax error/";
30 30
31my $result; 31my $result;
32 32
diff --git a/plugins/t/check_ldap.t b/plugins/t/check_ldap.t
index fcba0393..f3162ebb 100644
--- a/plugins/t/check_ldap.t
+++ b/plugins/t/check_ldap.t
@@ -32,7 +32,7 @@ SKIP: {
32 32
33 $result = NPTest->testCmd("$command -H $hostname_invalid -b ou=blah -t 5"); 33 $result = NPTest->testCmd("$command -H $hostname_invalid -b ou=blah -t 5");
34 is( $result->return_code, 2, "$command -H $hostname_invalid -b ou=blah -t 5" ); 34 is( $result->return_code, 2, "$command -H $hostname_invalid -b ou=blah -t 5" );
35 is( $result->output, 'Could not bind to the LDAP server', "output ok" ); 35 like( $result->output, '/could not bind to the LDAP server/', "output ok" );
36}; 36};
37 37
38SKIP: { 38SKIP: {
@@ -42,30 +42,30 @@ SKIP: {
42 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3"; 42 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3";
43 $result = NPTest->testCmd($cmd); 43 $result = NPTest->testCmd($cmd);
44 is( $result->return_code, 0, $cmd ); 44 is( $result->return_code, 0, $cmd );
45 like( $result->output, '/^LDAP OK - \d+.\d+ seconds response time\|time=\d+\.\d+s;2\.0+;3\.0+;0\.0+$/', "output ok" ); 45 like( $result->output, '/connection time \d+.\d+s/', "output ok" );
46 46
47 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000 -C 10000001"; 47 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000 -C 10000001";
48 $result = NPTest->testCmd($cmd); 48 $result = NPTest->testCmd($cmd);
49 is( $result->return_code, 0, $cmd ); 49 is( $result->return_code, 0, $cmd );
50 like( $result->output, '/^LDAP OK - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000;10000001;0\.0+$/', "output ok" ); 50 like( $result->output, '/found \d+ entries/', "output ok" );
51 51
52 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001:"; 52 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001:";
53 $result = NPTest->testCmd($cmd); 53 $result = NPTest->testCmd($cmd);
54 is( $result->return_code, 2, $cmd ); 54 is( $result->return_code, 2, $cmd );
55 like( $result->output, '/^LDAP CRITICAL - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000:;10000001:;0\.0+$/', "output ok" ); 55 like( $result->output, '/found \d+ entries/', "output ok" );
56 56
57 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 0 -C 0"; 57 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 0 -C 0";
58 $result = NPTest->testCmd($cmd); 58 $result = NPTest->testCmd($cmd);
59 is( $result->return_code, 2, $cmd ); 59 is( $result->return_code, 2, $cmd );
60 like( $result->output, '/^LDAP CRITICAL - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;0;0;0\.0+$/', "output ok" ); 60 like( $result->output, '/found \d+ entries/', "output ok" );
61 61
62 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001"; 62 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -W 10000000: -C 10000001";
63 $result = NPTest->testCmd($cmd); 63 $result = NPTest->testCmd($cmd);
64 is( $result->return_code, 1, $cmd ); 64 is( $result->return_code, 1, $cmd );
65 like( $result->output, '/^LDAP WARNING - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;10000000:;10000001;0\.0+$/', "output ok" ); 65 like( $result->output, '/found \d+ entries/', "output ok" );
66 66
67 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -C 10000001"; 67 $cmd = "$command -H $host_tcp_ldap -b $ldap_base_dn -t 5 -w 2 -c 3 -3 -C 10000001";
68 $result = NPTest->testCmd($cmd); 68 $result = NPTest->testCmd($cmd);
69 is( $result->return_code, 0, $cmd ); 69 is( $result->return_code, 0, $cmd );
70 like( $result->output, '/^LDAP OK - found \d+ entries in \d+\.\d+ seconds\|time=\d\.\d+s;2\.0+;3\.0+;0\.0+ entries=\d+\.0+;;10000001;0\.0+$/', "output ok" ); 70 like( $result->output, '/found \d+ entries/', "output ok" );
71}; 71};
diff --git a/plugins/t/check_mysql.t b/plugins/t/check_mysql.t
index a383bc99..9114cccc 100644
--- a/plugins/t/check_mysql.t
+++ b/plugins/t/check_mysql.t
@@ -11,6 +11,7 @@
11# mysql -u$user -p$password -h$host $db 11# mysql -u$user -p$password -h$host $db
12 12
13use strict; 13use strict;
14use warnings;
14use Test::More; 15use Test::More;
15use NPTest; 16use NPTest;
16 17
@@ -40,7 +41,7 @@ SKIP: {
40 41
41 $result = NPTest->testCmd("./check_mysql -S -H $mysqlserver $mysql_login_details"); 42 $result = NPTest->testCmd("./check_mysql -S -H $mysqlserver $mysql_login_details");
42 cmp_ok( $result->return_code, "==", 1, "No replicas defined" ); 43 cmp_ok( $result->return_code, "==", 1, "No replicas defined" );
43 like( $result->output, "/No replicas defined/", "Correct error message"); 44 like( $result->output, "/no replicas defined/", "Correct error message");
44} 45}
45 46
46SKIP: { 47SKIP: {
@@ -54,7 +55,7 @@ SKIP: {
54 55
55 $result = NPTest->testCmd("./check_mysql -S -s $mysqlsocket $mysql_login_details"); 56 $result = NPTest->testCmd("./check_mysql -S -s $mysqlsocket $mysql_login_details");
56 cmp_ok( $result->return_code, "==", 1, "No replicas defined" ); 57 cmp_ok( $result->return_code, "==", 1, "No replicas defined" );
57 like( $result->output, "/No replicas defined/", "Correct error message"); 58 like( $result->output, "/no replicas defined/", "Correct error message");
58} 59}
59 60
60SKIP: { 61SKIP: {
@@ -70,5 +71,5 @@ SKIP: {
70 71
71 $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login -w 60:"); 72 $result = NPTest->testCmd("./check_mysql -S -H $with_replica $with_replica_login -w 60:");
72 cmp_ok( $result->return_code, '==', 1, 'Alert warning if < 60 seconds behind'); 73 cmp_ok( $result->return_code, '==', 1, 'Alert warning if < 60 seconds behind');
73 like( $result->output, "/^SLOW_REPLICA WARNING:/", "Output okay"); 74 like( $result->output, "/^slow_replica/", "Output okay");
74} 75}
diff --git a/plugins/t/check_mysql_query.t b/plugins/t/check_mysql_query.t
index c30245b2..6de48bf6 100644
--- a/plugins/t/check_mysql_query.t
+++ b/plugins/t/check_mysql_query.t
@@ -54,5 +54,5 @@ like( $result->output, "/No rows returned/", "No rows error message");
54 54
55$result = NPTest->testCmd("./check_mysql_query -q 'SHOW VARIABLES' -H $mysqlserver $mysql_login_details"); 55$result = NPTest->testCmd("./check_mysql_query -q 'SHOW VARIABLES' -H $mysqlserver $mysql_login_details");
56cmp_ok( $result->return_code, '==', 2, "Data not numeric"); 56cmp_ok( $result->return_code, '==', 2, "Data not numeric");
57like( $result->output, "/Is not a numeric/", "Data not numeric error message"); 57like( $result->output, "/is not numeric/", "Data not numeric error message");
58 58
diff --git a/plugins/t/check_smtp.t b/plugins/t/check_smtp.t
index 73b4a1fd..c2f53c3d 100644
--- a/plugins/t/check_smtp.t
+++ b/plugins/t/check_smtp.t
@@ -5,6 +5,7 @@
5# 5#
6 6
7use strict; 7use strict;
8use warnings;
8use Test::More; 9use Test::More;
9use NPTest; 10use NPTest;
10 11
@@ -24,7 +25,7 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
24 "An invalid (not known to DNS) hostname", "nosuchhost" ); 25 "An invalid (not known to DNS) hostname", "nosuchhost" );
25my $res; 26my $res;
26 27
27plan tests => 15; 28plan tests => 13;
28 29
29SKIP: { 30SKIP: {
30 skip "No SMTP server defined", 4 unless $host_tcp_smtp; 31 skip "No SMTP server defined", 4 unless $host_tcp_smtp;
@@ -42,12 +43,11 @@ SKIP: {
42 43
43 TODO: { 44 TODO: {
44 local $TODO = "Output is over two lines"; 45 local $TODO = "Output is over two lines";
45 like ( $res->output, qr/^SMTP WARNING/, "Correct error message" );
46 } 46 }
47 47
48 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp --ssl -p 25" ); 48 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp --ssl -p 25" );
49 is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp with TLS on standard SMTP port" ); 49 is ($res->return_code, 2, "Check rc of connecting to $host_tcp_smtp with TLS on standard SMTP port" );
50 like ($res->output, qr/^CRITICAL - Cannot make SSL connection\./, "Check output of connecting to $host_tcp_smtp with TLS on standard SMTP port"); 50 like ($res->output, qr/cannot create TLS context/, "Check output of connecting to $host_tcp_smtp with TLS on standard SMTP port");
51} 51}
52 52
53SKIP: { 53SKIP: {
@@ -68,7 +68,6 @@ SKIP: {
68 skip "No SMTP server with TLS defined", 1 unless $host_tcp_smtp_tls; 68 skip "No SMTP server with TLS defined", 1 unless $host_tcp_smtp_tls;
69 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls --ssl" ); 69 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls --ssl" );
70 is ($res->return_code, 0, "Check rc of connecting to $host_tcp_smtp_tls with TLS" ); 70 is ($res->return_code, 0, "Check rc of connecting to $host_tcp_smtp_tls with TLS" );
71 like ($res->output, qr/^SMTP OK - /, "Check output of connecting to $host_tcp_smtp_tls with TLS" );
72 71
73 my $unused_port = 4465; 72 my $unused_port = 4465;
74 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls -p $unused_port --ssl" ); 73 $res = NPTest->testCmd( "./check_smtp -H $host_tcp_smtp_tls -p $unused_port --ssl" );